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
20 namespace panda::ecmascript {
MemController(Heap * heap)21 MemController::MemController(Heap *heap) : heap_(heap), allocTimeMs_(GetSystemTimeInMs())
22 {
23 ASSERT(heap != nullptr);
24 minAllocLimitGrowingStep_ = heap->GetEcmaVM()->GetEcmaParamConfiguration().GetMinAllocLimitGrowingStep();
25 }
26
CalculateAllocLimit(size_t currentSize,size_t minSize,size_t maxSize,size_t newSpaceCapacity,double factor) const27 size_t MemController::CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity,
28 double factor) const
29 {
30 const uint64_t limit = std::max(static_cast<uint64_t>(currentSize * factor),
31 static_cast<uint64_t>(currentSize) + minAllocLimitGrowingStep_) +
32 newSpaceCapacity;
33
34 const uint64_t limitAboveMinSize = std::max<uint64_t>(limit, minSize);
35 const uint64_t halfToMaxSize = (static_cast<uint64_t>(currentSize) + maxSize) / 2;
36 size_t result = static_cast<size_t>(std::min(limitAboveMinSize, halfToMaxSize));
37 result = static_cast<size_t>(std::min(result, maxSize));
38 return result;
39 }
40
CalculateGrowingFactor(double gcSpeed,double mutatorSpeed)41 double MemController::CalculateGrowingFactor(double gcSpeed, double mutatorSpeed)
42 {
43 double maxGrowingFactor = 4.0;
44 double halfMaxGrowingFactor = 2.0;
45 double minGrowingFactor = 1.3;
46 double minimumFactor = 1.1;
47 switch (heap_->GetMemGrowingType()) {
48 case MemGrowingType::HIGH_THROUGHPUT:
49 break;
50 case MemGrowingType::CONSERVATIVE:
51 minGrowingFactor = minimumFactor;
52 maxGrowingFactor = halfMaxGrowingFactor;
53 break;
54 case MemGrowingType::PRESSURE:
55 return minimumFactor;
56 default: // LCOV_EXCL_BR_LINE
57 break;
58 }
59
60 static constexpr double targetMutatorUtilization = 0.97;
61 if (gcSpeed == 0 || mutatorSpeed == 0) {
62 return maxGrowingFactor;
63 }
64
65 const double speedRatio = gcSpeed / mutatorSpeed;
66
67 const double a = speedRatio * (1 - targetMutatorUtilization);
68 const double b = speedRatio * (1 - targetMutatorUtilization) - targetMutatorUtilization;
69
70 double factor = (a < b * maxGrowingFactor) ? a / b : maxGrowingFactor;
71 factor = std::min(maxGrowingFactor, factor);
72 factor = std::max(factor, minGrowingFactor);
73 OPTIONAL_LOG(heap_->GetEcmaVM(), INFO) << "CalculateGrowingFactor gcSpeed"
74 << gcSpeed << " mutatorSpeed" << mutatorSpeed << " factor" << factor;
75 return factor;
76 }
77
StartCalculationBeforeGC()78 void MemController::StartCalculationBeforeGC()
79 {
80 startCounter_++;
81 if (startCounter_ != 1) {
82 return;
83 }
84
85 // It's unnecessary to calculate newSpaceAllocAccumulatedSize. newSpaceAllocBytesSinceGC can be calculated directly.
86 auto newSpace = heap_->GetNewSpace();
87 size_t newSpaceAllocBytesSinceGC = newSpace->GetAllocatedSizeSinceGC(newSpace->GetTop());
88 size_t hugeObjectAllocSizeSinceGC = heap_->GetHugeObjectSpace()->GetHeapObjectSize() - hugeObjectAllocSizeSinceGC_;
89 size_t oldSpaceAllocAccumulatedSize = heap_->GetOldSpace()->GetTotalAllocatedSize();
90 size_t nonMovableSpaceAllocAccumulatedSize = heap_->GetNonMovableSpace()->GetTotalAllocatedSize();
91 size_t codeSpaceAllocAccumulatedSize = heap_->GetMachineCodeSpace()->GetTotalAllocatedSize();
92 double currentTimeInMs = GetSystemTimeInMs();
93 gcStartTime_ = currentTimeInMs;
94 size_t oldSpaceAllocSize = oldSpaceAllocAccumulatedSize - oldSpaceAllocAccumulatedSize_;
95 size_t nonMovableSpaceAllocSize = nonMovableSpaceAllocAccumulatedSize - nonMovableSpaceAllocAccumulatedSize_;
96 size_t codeSpaceAllocSize = codeSpaceAllocAccumulatedSize - codeSpaceAllocAccumulatedSize_;
97
98 double duration = currentTimeInMs - allocTimeMs_;
99 allocTimeMs_ = currentTimeInMs;
100 oldSpaceAllocAccumulatedSize_ = oldSpaceAllocAccumulatedSize;
101 nonMovableSpaceAllocAccumulatedSize_ = nonMovableSpaceAllocAccumulatedSize;
102 codeSpaceAllocAccumulatedSize_ = codeSpaceAllocAccumulatedSize;
103
104 allocDurationSinceGc_ += duration;
105
106 newSpaceAllocSizeSinceGC_ += newSpaceAllocBytesSinceGC;
107 oldSpaceAllocSizeSinceGC_ += oldSpaceAllocSize;
108 oldSpaceAllocSizeSinceGC_ += hugeObjectAllocSizeSinceGC;
109 nonMovableSpaceAllocSizeSinceGC_ += nonMovableSpaceAllocSize;
110 codeSpaceAllocSizeSinceGC_ += codeSpaceAllocSize;
111
112 if (heap_->GetEcmaGCStats()->GetGCReason() != GCReason::IDLE) {
113 recordedIdleNewSpaceAllocations_.Push(MakeBytesAndDuration(
114 heap_->GetNewSpace()->GetHeapObjectSize() - newSpaceRecordLastTimeSizeIdle_,
115 currentTimeInMs - allocTimeMsIdle_));
116 recordedIdleOldSpaceAllocations_.Push(MakeBytesAndDuration(
117 heap_->GetOldSpace()->GetHeapObjectSize() - oldSpaceRecordLastTimeSizeIdle_,
118 currentTimeInMs - allocTimeMsIdle_));
119 }
120 }
121
RecordAllocationForIdle()122 void MemController::RecordAllocationForIdle()
123 {
124 double currentTimeInMs = GetSystemTimeInMs();
125 size_t currentNewSpaceObjectSize = heap_->GetNewSpace()->GetHeapObjectSize();
126 size_t currentOldSpaceObjectSize = heap_->GetOldSpace()->GetHeapObjectSize();
127 double duration = currentTimeInMs - allocTimeMsIdle_;
128 allocTimeMsIdle_ = currentTimeInMs;
129 if (currentNewSpaceObjectSize < newSpaceRecordLastTimeSizeIdle_ ||
130 currentOldSpaceObjectSize < oldSpaceRecordLastTimeSizeIdle_) {
131 newSpaceRecordLastTimeSizeIdle_ = currentNewSpaceObjectSize;
132 oldSpaceRecordLastTimeSizeIdle_ = currentOldSpaceObjectSize;
133 return;
134 }
135
136 size_t newSpaceAllocSizeSinceIdle = currentNewSpaceObjectSize - newSpaceRecordLastTimeSizeIdle_;
137 newSpaceRecordLastTimeSizeIdle_ = currentNewSpaceObjectSize;
138 size_t oldSpaceAllocSizeSinceIdle = currentOldSpaceObjectSize - oldSpaceRecordLastTimeSizeIdle_;
139 oldSpaceRecordLastTimeSizeIdle_ = currentOldSpaceObjectSize;
140 recordedIdleNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceIdle, duration));
141 recordedIdleOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceIdle, duration));
142 }
143
CheckLowAllocationUsageState() const144 bool MemController::CheckLowAllocationUsageState() const
145 {
146 LOG_GC(DEBUG) << "local CheckLowAllocationUsageState NewSpaceAllocBytesPerMS:" <<
147 GetIdleNewSpaceAllocationThroughputPerMS() << ",OldSpaceAllocBytesPerMS:" <<
148 GetIdleOldSpaceAllocationThroughputPerMS();
149 return GetIdleNewSpaceAllocationThroughputPerMS() < LOW_ALLOCATION_RATE_PER_MS &&
150 GetIdleOldSpaceAllocationThroughputPerMS() < LOW_ALLOCATION_RATE_PER_MS;
151 }
152
ResetCalculationWithoutGC()153 void MemController::ResetCalculationWithoutGC()
154 {
155 startCounter_--;
156 if (startCounter_ != 0) {
157 return;
158 }
159 allocDurationSinceGc_ = 0.0;
160 newSpaceAllocSizeSinceGC_ = 0;
161 oldSpaceAllocSizeSinceGC_ = 0;
162 nonMovableSpaceAllocSizeSinceGC_ = 0;
163 codeSpaceAllocSizeSinceGC_ = 0;
164 }
165
StopCalculationAfterGC(TriggerGCType gcType)166 void MemController::StopCalculationAfterGC(TriggerGCType gcType)
167 {
168 startCounter_--;
169 if (startCounter_ != 0) {
170 return;
171 }
172
173 gcEndTime_ = GetSystemTimeInMs();
174 allocTimeMs_ = gcEndTime_;
175 if (allocDurationSinceGc_ > 0) {
176 oldSpaceAllocSizeSinceGC_ += heap_->GetEvacuator()->GetPromotedSize();
177 recordedNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
178 recordedOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
179 recordedNonmovableSpaceAllocations_.Push(
180 MakeBytesAndDuration(nonMovableSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
181 recordedCodeSpaceAllocations_.Push(MakeBytesAndDuration(codeSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
182 }
183 allocDurationSinceGc_ = 0.0;
184 newSpaceAllocSizeSinceGC_ = 0;
185 oldSpaceAllocSizeSinceGC_ = 0;
186 nonMovableSpaceAllocSizeSinceGC_ = 0;
187 codeSpaceAllocSizeSinceGC_ = 0;
188
189 hugeObjectAllocSizeSinceGC_ = heap_->GetHugeObjectSpace()->GetHeapObjectSize();
190
191 double duration = gcEndTime_ - gcStartTime_;
192 switch (gcType) {
193 case TriggerGCType::YOUNG_GC:
194 case TriggerGCType::OLD_GC: {
195 if (heap_->IsConcurrentFullMark()) {
196 if (heap_->GetConcurrentMarker()->IsEnabled()) {
197 duration += heap_->GetConcurrentMarker()->GetDuration();
198 }
199 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
200 }
201 break;
202 }
203 case TriggerGCType::FULL_GC: {
204 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
205 break;
206 }
207 default:
208 break;
209 }
210
211 if (heap_->GetEcmaGCStats()->GetGCReason() != GCReason::IDLE) {
212 newSpaceRecordLastTimeSizeIdle_ = heap_->GetNewSpace()->GetHeapObjectSize();
213 oldSpaceRecordLastTimeSizeIdle_ = heap_->GetOldSpace()->GetHeapObjectSize();
214 allocTimeMsIdle_ = gcEndTime_;
215 }
216 }
217
RecordAfterConcurrentMark(MarkType markType,const ConcurrentMarker * marker)218 void MemController::RecordAfterConcurrentMark(MarkType markType, const ConcurrentMarker *marker)
219 {
220 ASSERT(marker != nullptr);
221 double duration = marker->GetDuration();
222 if (markType == MarkType::MARK_FULL) {
223 recordedConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
224 } else if (markType == MarkType::MARK_YOUNG) {
225 recordedSemiConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
226 }
227 }
228
CalculateMarkCompactSpeedPerMS()229 double MemController::CalculateMarkCompactSpeedPerMS()
230 {
231 markCompactSpeedCache_ = CalculateAverageSpeed(recordedMarkCompacts_);
232 if (markCompactSpeedCache_ > 0) {
233 return markCompactSpeedCache_;
234 }
235 return 0;
236 }
237
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer,const BytesAndDuration & initial,const double timeMs)238 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer,
239 const BytesAndDuration &initial, const double timeMs)
240 {
241 BytesAndDuration sum = buffer.Sum(
242 [timeMs](BytesAndDuration a, BytesAndDuration b) {
243 if (timeMs != 0 && a.second >= timeMs) {
244 return a;
245 }
246 return std::make_pair(a.first + b.first, a.second + b.second);
247 },
248 initial);
249 uint64_t bytes = sum.first;
250 double durations = sum.second;
251 if (fabs(durations) <= 1e-6) {
252 return 0;
253 }
254 double speed = bytes / durations;
255 const int maxSpeed = static_cast<int>(1_GB);
256 const int minSpeed = 1;
257 if (speed >= maxSpeed) {
258 return maxSpeed;
259 }
260 if (speed <= minSpeed) {
261 return minSpeed;
262 }
263 return speed;
264 }
265
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer)266 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer)
267 {
268 return CalculateAverageSpeed(buffer, MakeBytesAndDuration(0, 0), 0);
269 }
270
GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const271 double MemController::GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const
272 {
273 size_t allocatedSize = oldSpaceAllocSizeSinceGC_;
274 double duration = allocDurationSinceGc_;
275 return CalculateAverageSpeed(recordedOldSpaceAllocations_,
276 MakeBytesAndDuration(allocatedSize, duration), timeMs);
277 }
278
GetNewSpaceAllocationThroughputPerMS() const279 double MemController::GetNewSpaceAllocationThroughputPerMS() const
280 {
281 return CalculateAverageSpeed(recordedNewSpaceAllocations_);
282 }
283
GetNewSpaceConcurrentMarkSpeedPerMS() const284 double MemController::GetNewSpaceConcurrentMarkSpeedPerMS() const
285 {
286 return CalculateAverageSpeed(recordedSemiConcurrentMarks_);
287 }
288
GetOldSpaceAllocationThroughputPerMS() const289 double MemController::GetOldSpaceAllocationThroughputPerMS() const
290 {
291 return CalculateAverageSpeed(recordedOldSpaceAllocations_);
292 }
293
GetFullSpaceConcurrentMarkSpeedPerMS() const294 double MemController::GetFullSpaceConcurrentMarkSpeedPerMS() const
295 {
296 return CalculateAverageSpeed(recordedConcurrentMarks_);
297 }
298
GetIdleNewSpaceAllocationThroughputPerMS() const299 double MemController::GetIdleNewSpaceAllocationThroughputPerMS() const
300 {
301 return CalculateAverageSpeed(recordedIdleNewSpaceAllocations_);
302 }
303
GetIdleOldSpaceAllocationThroughputPerMS() const304 double MemController::GetIdleOldSpaceAllocationThroughputPerMS() const
305 {
306 return CalculateAverageSpeed(recordedIdleOldSpaceAllocations_);
307 }
308
309 } // namespace panda::ecmascript
310