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