• 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 #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