1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/mem/mem_controller.h"
17 #include "ecmascript/mem/concurrent_marker.h"
18 #include "ecmascript/mem/parallel_evacuator.h"
19 #include "ecmascript/mem/mem_controller_utils.h"
20
21 namespace panda::ecmascript {
MemController(Heap * heap)22 MemController::MemController(Heap *heap) : heap_(heap), allocTimeMs_(GetSystemTimeInMs())
23 {
24 ASSERT(heap != nullptr);
25 minAllocLimitGrowingStep_ = heap->GetEcmaVM()->GetEcmaParamConfiguration().GetMinAllocLimitGrowingStep();
26 }
27
CalculateAllocLimit(size_t currentSize,size_t minSize,size_t maxSize,size_t newSpaceCapacity,double factor) const28 size_t MemController::CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity,
29 double factor) const
30 {
31 const uint64_t limit = std::max(static_cast<uint64_t>(currentSize * factor),
32 static_cast<uint64_t>(currentSize) + minAllocLimitGrowingStep_) +
33 newSpaceCapacity;
34
35 const uint64_t limitAboveMinSize = std::max<uint64_t>(limit, minSize);
36 const uint64_t halfToMaxSize = (static_cast<uint64_t>(currentSize) + maxSize) / 2;
37 size_t result = static_cast<size_t>(std::min(limitAboveMinSize, halfToMaxSize));
38 result = static_cast<size_t>(std::min(result, maxSize));
39 return result;
40 }
41
CalculateGrowingFactor(double gcSpeed,double mutatorSpeed)42 double MemController::CalculateGrowingFactor(double gcSpeed, double mutatorSpeed)
43 {
44 double maxGrowingFactor = 4.0;
45 double halfMaxGrowingFactor = 2.0;
46 double minGrowingFactor = 1.3;
47 double minimumFactor = 1.1;
48 switch (heap_->GetMemGrowingType()) {
49 case MemGrowingType::HIGH_THROUGHPUT:
50 break;
51 case MemGrowingType::CONSERVATIVE:
52 minGrowingFactor = minimumFactor;
53 maxGrowingFactor = halfMaxGrowingFactor;
54 break;
55 case MemGrowingType::PRESSURE:
56 return minimumFactor;
57 default: // LCOV_EXCL_BR_LINE
58 break;
59 }
60
61 static constexpr double targetMutatorUtilization = 0.97;
62 if (gcSpeed == 0 || mutatorSpeed == 0) {
63 return maxGrowingFactor;
64 }
65
66 const double speedRatio = gcSpeed / mutatorSpeed;
67
68 const double a = speedRatio * (1 - targetMutatorUtilization);
69 const double b = speedRatio * (1 - targetMutatorUtilization) - targetMutatorUtilization;
70
71 double factor = (a < b * maxGrowingFactor) ? a / b : maxGrowingFactor;
72 factor = std::min(maxGrowingFactor, factor);
73 factor = std::max(factor, minGrowingFactor);
74 OPTIONAL_LOG(heap_->GetEcmaVM(), INFO) << "CalculateGrowingFactor gcSpeed"
75 << gcSpeed << " mutatorSpeed" << mutatorSpeed << " factor" << factor;
76 return factor;
77 }
78
StartCalculationBeforeGC()79 void MemController::StartCalculationBeforeGC()
80 {
81 startCounter_++;
82 if (startCounter_ != 1) {
83 return;
84 }
85
86 // It's unnecessary to calculate newSpaceAllocAccumulatedSize. newSpaceAllocBytesSinceGC can be calculated directly.
87 auto newSpace = heap_->GetNewSpace();
88 size_t newSpaceAllocBytesSinceGC = newSpace->GetAllocatedSizeSinceGC(newSpace->GetTop());
89 size_t hugeObjectAllocSizeSinceGC = heap_->GetHugeObjectSpace()->GetHeapObjectSize() - hugeObjectAllocSizeSinceGC_;
90 size_t oldSpaceAllocAccumulatedSize = heap_->GetOldSpace()->GetTotalAllocatedSize();
91 size_t nonMovableSpaceAllocAccumulatedSize = heap_->GetNonMovableSpace()->GetTotalAllocatedSize();
92 size_t codeSpaceAllocAccumulatedSize = heap_->GetMachineCodeSpace()->GetTotalAllocatedSize();
93 double currentTimeInMs = GetSystemTimeInMs();
94 gcStartTime_ = currentTimeInMs;
95 size_t oldSpaceAllocSize = oldSpaceAllocAccumulatedSize - oldSpaceAllocAccumulatedSize_;
96 size_t nonMovableSpaceAllocSize = nonMovableSpaceAllocAccumulatedSize - nonMovableSpaceAllocAccumulatedSize_;
97 size_t codeSpaceAllocSize = codeSpaceAllocAccumulatedSize - codeSpaceAllocAccumulatedSize_;
98
99 double duration = currentTimeInMs - allocTimeMs_;
100 allocTimeMs_ = currentTimeInMs;
101 oldSpaceAllocAccumulatedSize_ = oldSpaceAllocAccumulatedSize;
102 nonMovableSpaceAllocAccumulatedSize_ = nonMovableSpaceAllocAccumulatedSize;
103 codeSpaceAllocAccumulatedSize_ = codeSpaceAllocAccumulatedSize;
104
105 allocDurationSinceGc_ += duration;
106
107 newSpaceAllocSizeSinceGC_ += newSpaceAllocBytesSinceGC;
108 oldSpaceAllocSizeSinceGC_ += oldSpaceAllocSize;
109 oldSpaceAllocSizeSinceGC_ += hugeObjectAllocSizeSinceGC;
110 nonMovableSpaceAllocSizeSinceGC_ += nonMovableSpaceAllocSize;
111 codeSpaceAllocSizeSinceGC_ += codeSpaceAllocSize;
112
113 if (heap_->GetEcmaGCStats()->GetGCReason() != GCReason::IDLE) {
114 recordedIdleNewSpaceAllocations_.Push(MakeBytesAndDuration(
115 heap_->GetNewSpace()->GetHeapObjectSize() - newSpaceRecordLastTimeSizeIdle_,
116 currentTimeInMs - allocTimeMsIdle_));
117 recordedIdleOldSpaceAllocations_.Push(MakeBytesAndDuration(
118 heap_->GetOldSpace()->GetHeapObjectSize() - oldSpaceRecordLastTimeSizeIdle_,
119 currentTimeInMs - allocTimeMsIdle_));
120 }
121 }
122
RecordAllocationForIdle()123 void MemController::RecordAllocationForIdle()
124 {
125 double currentTimeInMs = GetSystemTimeInMs();
126 size_t currentNewSpaceObjectSize = heap_->GetNewSpace()->GetHeapObjectSize();
127 size_t currentOldSpaceObjectSize = heap_->GetOldSpace()->GetHeapObjectSize();
128 double duration = currentTimeInMs - allocTimeMsIdle_;
129 allocTimeMsIdle_ = currentTimeInMs;
130 if (currentNewSpaceObjectSize < newSpaceRecordLastTimeSizeIdle_ ||
131 currentOldSpaceObjectSize < oldSpaceRecordLastTimeSizeIdle_) {
132 newSpaceRecordLastTimeSizeIdle_ = currentNewSpaceObjectSize;
133 oldSpaceRecordLastTimeSizeIdle_ = currentOldSpaceObjectSize;
134 return;
135 }
136
137 size_t newSpaceAllocSizeSinceIdle = currentNewSpaceObjectSize - newSpaceRecordLastTimeSizeIdle_;
138 newSpaceRecordLastTimeSizeIdle_ = currentNewSpaceObjectSize;
139 size_t oldSpaceAllocSizeSinceIdle = currentOldSpaceObjectSize - oldSpaceRecordLastTimeSizeIdle_;
140 oldSpaceRecordLastTimeSizeIdle_ = currentOldSpaceObjectSize;
141 recordedIdleNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceIdle, duration));
142 recordedIdleOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceIdle, duration));
143 }
144
CheckLowAllocationUsageState() const145 bool MemController::CheckLowAllocationUsageState() const
146 {
147 LOG_GC(DEBUG) << "local CheckLowAllocationUsageState NewSpaceAllocBytesPerMS:" <<
148 GetIdleNewSpaceAllocationThroughputPerMS() << ",OldSpaceAllocBytesPerMS:" <<
149 GetIdleOldSpaceAllocationThroughputPerMS();
150 return GetIdleNewSpaceAllocationThroughputPerMS() < LOW_ALLOCATION_RATE_PER_MS &&
151 GetIdleOldSpaceAllocationThroughputPerMS() < LOW_ALLOCATION_RATE_PER_MS;
152 }
153
ResetCalculationWithoutGC()154 void MemController::ResetCalculationWithoutGC()
155 {
156 startCounter_--;
157 if (startCounter_ != 0) {
158 return;
159 }
160 allocDurationSinceGc_ = 0.0;
161 newSpaceAllocSizeSinceGC_ = 0;
162 oldSpaceAllocSizeSinceGC_ = 0;
163 nonMovableSpaceAllocSizeSinceGC_ = 0;
164 codeSpaceAllocSizeSinceGC_ = 0;
165 }
166
StopCalculationAfterGC(TriggerGCType gcType)167 void MemController::StopCalculationAfterGC(TriggerGCType gcType)
168 {
169 startCounter_--;
170 if (startCounter_ != 0) {
171 return;
172 }
173
174 gcEndTime_ = GetSystemTimeInMs();
175 allocTimeMs_ = gcEndTime_;
176 if (allocDurationSinceGc_ > 0) {
177 oldSpaceAllocSizeSinceGC_ += heap_->GetEvacuator()->GetPromotedSize();
178 recordedNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
179 recordedOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
180 recordedNonmovableSpaceAllocations_.Push(
181 MakeBytesAndDuration(nonMovableSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
182 recordedCodeSpaceAllocations_.Push(MakeBytesAndDuration(codeSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
183 }
184 allocDurationSinceGc_ = 0.0;
185 newSpaceAllocSizeSinceGC_ = 0;
186 oldSpaceAllocSizeSinceGC_ = 0;
187 nonMovableSpaceAllocSizeSinceGC_ = 0;
188 codeSpaceAllocSizeSinceGC_ = 0;
189
190 hugeObjectAllocSizeSinceGC_ = heap_->GetHugeObjectSpace()->GetHeapObjectSize();
191
192 double duration = gcEndTime_ - gcStartTime_;
193 switch (gcType) {
194 case TriggerGCType::YOUNG_GC:
195 case TriggerGCType::OLD_GC: {
196 if (heap_->IsConcurrentFullMark()) {
197 if (heap_->GetConcurrentMarker()->IsEnabled()) {
198 duration += heap_->GetConcurrentMarker()->GetDuration();
199 }
200 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
201 }
202 break;
203 }
204 case TriggerGCType::FULL_GC: {
205 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
206 break;
207 }
208 default:
209 break;
210 }
211
212 if (heap_->GetEcmaGCStats()->GetGCReason() != GCReason::IDLE) {
213 newSpaceRecordLastTimeSizeIdle_ = heap_->GetNewSpace()->GetHeapObjectSize();
214 oldSpaceRecordLastTimeSizeIdle_ = heap_->GetOldSpace()->GetHeapObjectSize();
215 allocTimeMsIdle_ = gcEndTime_;
216 }
217 }
218
RecordAfterConcurrentMark(MarkType markType,const ConcurrentMarker * marker)219 void MemController::RecordAfterConcurrentMark(MarkType markType, const ConcurrentMarker *marker)
220 {
221 ASSERT(marker != nullptr);
222 double duration = marker->GetDuration();
223 if (markType == MarkType::MARK_FULL) {
224 recordedConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
225 } else if (markType == MarkType::MARK_YOUNG) {
226 recordedSemiConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
227 }
228 }
229
CalculateMarkCompactSpeedPerMS()230 double MemController::CalculateMarkCompactSpeedPerMS()
231 {
232 markCompactSpeedCache_ = CalculateAverageSpeed(recordedMarkCompacts_);
233 if (markCompactSpeedCache_ > 0) {
234 return markCompactSpeedCache_;
235 }
236 return 0;
237 }
238
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer,const BytesAndDuration & initial,const double timeMs)239 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer,
240 const BytesAndDuration &initial, const double timeMs)
241 {
242 BytesAndDuration sum = buffer.Sum(
243 [timeMs](BytesAndDuration a, BytesAndDuration b) {
244 if (timeMs != 0 && a.second >= timeMs) {
245 return a;
246 }
247 return std::make_pair(a.first + b.first, a.second + b.second);
248 },
249 initial);
250 uint64_t bytes = sum.first;
251 double durations = sum.second;
252 if (fabs(durations) <= 1e-6) {
253 return 0;
254 }
255 double speed = bytes / durations;
256 const int maxSpeed = static_cast<int>(1_GB);
257 const int minSpeed = 1;
258 if (speed >= maxSpeed) {
259 return maxSpeed;
260 }
261 if (speed <= minSpeed) {
262 return minSpeed;
263 }
264 return speed;
265 }
266
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer)267 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer)
268 {
269 return CalculateAverageSpeed(buffer, MakeBytesAndDuration(0, 0), 0);
270 }
271
GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const272 double MemController::GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const
273 {
274 size_t allocatedSize = oldSpaceAllocSizeSinceGC_;
275 double duration = allocDurationSinceGc_;
276 return CalculateAverageSpeed(recordedOldSpaceAllocations_,
277 MakeBytesAndDuration(allocatedSize, duration), timeMs);
278 }
279
GetNewSpaceAllocationThroughputPerMS() const280 double MemController::GetNewSpaceAllocationThroughputPerMS() const
281 {
282 return CalculateAverageSpeed(recordedNewSpaceAllocations_);
283 }
284
GetNewSpaceConcurrentMarkSpeedPerMS() const285 double MemController::GetNewSpaceConcurrentMarkSpeedPerMS() const
286 {
287 return CalculateAverageSpeed(recordedSemiConcurrentMarks_);
288 }
289
GetOldSpaceAllocationThroughputPerMS() const290 double MemController::GetOldSpaceAllocationThroughputPerMS() const
291 {
292 return CalculateAverageSpeed(recordedOldSpaceAllocations_);
293 }
294
GetFullSpaceConcurrentMarkSpeedPerMS() const295 double MemController::GetFullSpaceConcurrentMarkSpeedPerMS() const
296 {
297 return CalculateAverageSpeed(recordedConcurrentMarks_);
298 }
299
GetIdleNewSpaceAllocationThroughputPerMS() const300 double MemController::GetIdleNewSpaceAllocationThroughputPerMS() const
301 {
302 return CalculateAverageSpeed(recordedIdleNewSpaceAllocations_);
303 }
304
GetIdleOldSpaceAllocationThroughputPerMS() const305 double MemController::GetIdleOldSpaceAllocationThroughputPerMS() const
306 {
307 return CalculateAverageSpeed(recordedIdleOldSpaceAllocations_);
308 }
309
310 } // namespace panda::ecmascript
311