• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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