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
18 #include "ecmascript/mem/concurrent_marker.h"
19 #include "ecmascript/mem/parallel_evacuator.h"
20
21 namespace panda::ecmascript {
MemController(Heap *heap)22 MemController::MemController(Heap *heap) : heap_(heap), allocTimeMs_(GetSystemTimeInMs())
23 {
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:
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 auto edenSpace = heap_->GetEdenSpace();
86 size_t edenSpaceAllocBytesSinceGC = edenSpace->GetAllocatedSizeSinceGC(edenSpace->GetTop());
87 // It's unnecessary to calculate newSpaceAllocAccumulatedSize. newSpaceAllocBytesSinceGC can be calculated directly.
88 auto newSpace = heap_->GetNewSpace();
89 size_t newSpaceAllocBytesSinceGC = newSpace->GetAllocatedSizeSinceGC(newSpace->GetTop());
90 size_t hugeObjectAllocSizeSinceGC = heap_->GetHugeObjectSpace()->GetHeapObjectSize() - hugeObjectAllocSizeSinceGC_;
91 size_t oldSpaceAllocAccumulatedSize = heap_->GetOldSpace()->GetTotalAllocatedSize();
92 size_t nonMovableSpaceAllocAccumulatedSize = heap_->GetNonMovableSpace()->GetTotalAllocatedSize();
93 size_t codeSpaceAllocAccumulatedSize = heap_->GetMachineCodeSpace()->GetTotalAllocatedSize();
94 double currentTimeInMs = GetSystemTimeInMs();
95 gcStartTime_ = currentTimeInMs;
96 size_t oldSpaceAllocSize = oldSpaceAllocAccumulatedSize - oldSpaceAllocAccumulatedSize_;
97 size_t nonMovableSpaceAllocSize = nonMovableSpaceAllocAccumulatedSize - nonMovableSpaceAllocAccumulatedSize_;
98 size_t codeSpaceAllocSize = codeSpaceAllocAccumulatedSize - codeSpaceAllocAccumulatedSize_;
99
100 double duration = currentTimeInMs - allocTimeMs_;
101 allocTimeMs_ = currentTimeInMs;
102 oldSpaceAllocAccumulatedSize_ = oldSpaceAllocAccumulatedSize;
103 nonMovableSpaceAllocAccumulatedSize_ = nonMovableSpaceAllocAccumulatedSize;
104 codeSpaceAllocAccumulatedSize_ = codeSpaceAllocAccumulatedSize;
105
106 allocDurationSinceGc_ += duration;
107
108 edenSpaceAllocSizeSinceGC_ += edenSpaceAllocBytesSinceGC;
109 newSpaceAllocSizeSinceGC_ += newSpaceAllocBytesSinceGC;
110 oldSpaceAllocSizeSinceGC_ += oldSpaceAllocSize;
111 oldSpaceAllocSizeSinceGC_ += hugeObjectAllocSizeSinceGC;
112 nonMovableSpaceAllocSizeSinceGC_ += nonMovableSpaceAllocSize;
113 codeSpaceAllocSizeSinceGC_ += codeSpaceAllocSize;
114
115 if (heap_->GetEcmaGCStats()->GetGCReason() != GCReason::IDLE) {
116 recordedIdleNewSpaceAllocations_.Push(MakeBytesAndDuration(
117 heap_->GetNewSpace()->GetHeapObjectSize() - newSpaceRecordLastTimeSizeIdle_,
118 currentTimeInMs - allocTimeMsIdle_));
119 recordedIdleOldSpaceAllocations_.Push(MakeBytesAndDuration(
120 heap_->GetOldSpace()->GetHeapObjectSize() - oldSpaceRecordLastTimeSizeIdle_,
121 currentTimeInMs - allocTimeMsIdle_));
122 }
123 }
124
RecordAllocationForIdle()125 void MemController::RecordAllocationForIdle()
126 {
127 double currentTimeInMs = GetSystemTimeInMs();
128 size_t currentNewSpaceObjectSize = heap_->GetNewSpace()->GetHeapObjectSize();
129 size_t currentOldSpaceObjectSize = heap_->GetOldSpace()->GetHeapObjectSize();
130 double duration = currentTimeInMs - allocTimeMsIdle_;
131 allocTimeMsIdle_ = currentTimeInMs;
132 if (currentNewSpaceObjectSize < newSpaceRecordLastTimeSizeIdle_ ||
133 currentOldSpaceObjectSize < oldSpaceRecordLastTimeSizeIdle_) {
134 newSpaceRecordLastTimeSizeIdle_ = currentNewSpaceObjectSize;
135 oldSpaceRecordLastTimeSizeIdle_ = currentOldSpaceObjectSize;
136 return;
137 }
138
139 size_t newSpaceAllocSizeSinceIdle = currentNewSpaceObjectSize - newSpaceRecordLastTimeSizeIdle_;
140 newSpaceRecordLastTimeSizeIdle_ = currentNewSpaceObjectSize;
141 size_t oldSpaceAllocSizeSinceIdle = currentOldSpaceObjectSize - oldSpaceRecordLastTimeSizeIdle_;
142 oldSpaceRecordLastTimeSizeIdle_ = currentOldSpaceObjectSize;
143 recordedIdleNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceIdle, duration));
144 recordedIdleOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceIdle, duration));
145 }
146
CheckLowAllocationUsageState() const147 bool MemController::CheckLowAllocationUsageState() const
148 {
149 LOG_GC(DEBUG) << "local CheckLowAllocationUsageState NewSpaceAllocBytesPerMS:" <<
150 GetIdleNewSpaceAllocationThroughputPerMS() << ",OldSpaceAllocBytesPerMS:" <<
151 GetIdleOldSpaceAllocationThroughputPerMS();
152 return GetIdleNewSpaceAllocationThroughputPerMS() < LOW_ALLOCATION_RATE_PER_MS &&
153 GetIdleOldSpaceAllocationThroughputPerMS() < LOW_ALLOCATION_RATE_PER_MS;
154 }
155
StopCalculationAfterGC(TriggerGCType gcType)156 void MemController::StopCalculationAfterGC(TriggerGCType gcType)
157 {
158 startCounter_--;
159 if (startCounter_ != 0) {
160 return;
161 }
162
163 gcEndTime_ = GetSystemTimeInMs();
164 allocTimeMs_ = gcEndTime_;
165 if (allocDurationSinceGc_ > 0) {
166 oldSpaceAllocSizeSinceGC_ += heap_->GetEvacuator()->GetPromotedSize();
167 recordedEdenSpaceAllocations_.Push(MakeBytesAndDuration(edenSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
168 recordedNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
169 recordedOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
170 recordedNonmovableSpaceAllocations_.Push(
171 MakeBytesAndDuration(nonMovableSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
172 recordedCodeSpaceAllocations_.Push(MakeBytesAndDuration(codeSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
173 }
174 allocDurationSinceGc_ = 0.0;
175 edenSpaceAllocSizeSinceGC_ = 0;
176 newSpaceAllocSizeSinceGC_ = 0;
177 oldSpaceAllocSizeSinceGC_ = 0;
178 nonMovableSpaceAllocSizeSinceGC_ = 0;
179 codeSpaceAllocSizeSinceGC_ = 0;
180
181 hugeObjectAllocSizeSinceGC_ = heap_->GetHugeObjectSpace()->GetHeapObjectSize();
182
183 double duration = gcEndTime_ - gcStartTime_;
184 switch (gcType) {
185 case TriggerGCType::YOUNG_GC:
186 case TriggerGCType::OLD_GC: {
187 if (heap_->IsConcurrentFullMark()) {
188 if (heap_->GetConcurrentMarker()->IsEnabled()) {
189 duration += heap_->GetConcurrentMarker()->GetDuration();
190 }
191 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
192 }
193 break;
194 }
195 case TriggerGCType::FULL_GC: {
196 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
197 break;
198 }
199 default:
200 break;
201 }
202
203 if (heap_->GetEcmaGCStats()->GetGCReason() != GCReason::IDLE) {
204 newSpaceRecordLastTimeSizeIdle_ = heap_->GetNewSpace()->GetHeapObjectSize();
205 oldSpaceRecordLastTimeSizeIdle_ = heap_->GetOldSpace()->GetHeapObjectSize();
206 allocTimeMsIdle_ = gcEndTime_;
207 }
208 }
209
RecordAfterConcurrentMark(MarkType markType, const ConcurrentMarker *marker)210 void MemController::RecordAfterConcurrentMark(MarkType markType, const ConcurrentMarker *marker)
211 {
212 double duration = marker->GetDuration();
213 if (markType == MarkType::MARK_FULL) {
214 recordedConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
215 } else if (markType == MarkType::MARK_YOUNG) {
216 recordedSemiConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
217 } else if (markType == MarkType::MARK_EDEN) {
218 recordedEdenConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
219 }
220 }
221
CalculateMarkCompactSpeedPerMS()222 double MemController::CalculateMarkCompactSpeedPerMS()
223 {
224 markCompactSpeedCache_ = CalculateAverageSpeed(recordedMarkCompacts_);
225 if (markCompactSpeedCache_ > 0) {
226 return markCompactSpeedCache_;
227 }
228 return 0;
229 }
230
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer, const BytesAndDuration &initial, const double timeMs)231 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer,
232 const BytesAndDuration &initial, const double timeMs)
233 {
234 BytesAndDuration sum = buffer.Sum(
235 [timeMs](BytesAndDuration a, BytesAndDuration b) {
236 if (timeMs != 0 && a.second >= timeMs) {
237 return a;
238 }
239 return std::make_pair(a.first + b.first, a.second + b.second);
240 },
241 initial);
242 uint64_t bytes = sum.first;
243 double durations = sum.second;
244 if (fabs(durations) <= 1e-6) {
245 return 0;
246 }
247 double speed = bytes / durations;
248 const int maxSpeed = static_cast<int>(1_GB);
249 const int minSpeed = 1;
250 if (speed >= maxSpeed) {
251 return maxSpeed;
252 }
253 if (speed <= minSpeed) {
254 return minSpeed;
255 }
256 return speed;
257 }
258
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer)259 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer)
260 {
261 return CalculateAverageSpeed(buffer, MakeBytesAndDuration(0, 0), 0);
262 }
263
GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const264 double MemController::GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const
265 {
266 size_t allocatedSize = oldSpaceAllocSizeSinceGC_;
267 double duration = allocDurationSinceGc_;
268 return CalculateAverageSpeed(recordedOldSpaceAllocations_,
269 MakeBytesAndDuration(allocatedSize, duration), timeMs);
270 }
271
GetEdenSpaceAllocationThroughputPerMS() const272 double MemController::GetEdenSpaceAllocationThroughputPerMS() const
273 {
274 return CalculateAverageSpeed(recordedEdenSpaceAllocations_);
275 }
276
GetNewSpaceAllocationThroughputPerMS() const277 double MemController::GetNewSpaceAllocationThroughputPerMS() const
278 {
279 return CalculateAverageSpeed(recordedNewSpaceAllocations_);
280 }
281
GetEdenSpaceConcurrentMarkSpeedPerMS() const282 double MemController::GetEdenSpaceConcurrentMarkSpeedPerMS() const
283 {
284 return CalculateAverageSpeed(recordedEdenConcurrentMarks_);
285 }
286
GetNewSpaceConcurrentMarkSpeedPerMS() const287 double MemController::GetNewSpaceConcurrentMarkSpeedPerMS() const
288 {
289 return CalculateAverageSpeed(recordedSemiConcurrentMarks_);
290 }
291
GetOldSpaceAllocationThroughputPerMS() const292 double MemController::GetOldSpaceAllocationThroughputPerMS() const
293 {
294 return CalculateAverageSpeed(recordedOldSpaceAllocations_);
295 }
296
GetFullSpaceConcurrentMarkSpeedPerMS() const297 double MemController::GetFullSpaceConcurrentMarkSpeedPerMS() const
298 {
299 return CalculateAverageSpeed(recordedConcurrentMarks_);
300 }
301
GetIdleNewSpaceAllocationThroughputPerMS() const302 double MemController::GetIdleNewSpaceAllocationThroughputPerMS() const
303 {
304 return CalculateAverageSpeed(recordedIdleNewSpaceAllocations_);
305 }
306
GetIdleOldSpaceAllocationThroughputPerMS() const307 double MemController::GetIdleOldSpaceAllocationThroughputPerMS() const
308 {
309 return CalculateAverageSpeed(recordedIdleOldSpaceAllocations_);
310 }
311
312 } // namespace panda::ecmascript
313