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
StopCalculationAfterGC(TriggerGCType gcType)116 void MemController::StopCalculationAfterGC(TriggerGCType gcType)
117 {
118 startCounter_--;
119 if (startCounter_ != 0) {
120 return;
121 }
122
123 gcEndTime_ = GetSystemTimeInMs();
124 allocTimeMs_ = gcEndTime_;
125 if (allocDurationSinceGc_ > 0) {
126 oldSpaceAllocSizeSinceGC_ += heap_->GetEvacuator()->GetPromotedSize();
127 recordedEdenSpaceAllocations_.Push(MakeBytesAndDuration(edenSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
128 recordedNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
129 recordedOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
130 recordedNonmovableSpaceAllocations_.Push(
131 MakeBytesAndDuration(nonMovableSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
132 recordedCodeSpaceAllocations_.Push(MakeBytesAndDuration(codeSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
133 }
134 allocDurationSinceGc_ = 0.0;
135 edenSpaceAllocSizeSinceGC_ = 0;
136 newSpaceAllocSizeSinceGC_ = 0;
137 oldSpaceAllocSizeSinceGC_ = 0;
138 nonMovableSpaceAllocSizeSinceGC_ = 0;
139 codeSpaceAllocSizeSinceGC_ = 0;
140
141 hugeObjectAllocSizeSinceGC_ = heap_->GetHugeObjectSpace()->GetHeapObjectSize();
142
143 double duration = gcEndTime_ - gcStartTime_;
144 switch (gcType) {
145 case TriggerGCType::YOUNG_GC:
146 case TriggerGCType::OLD_GC: {
147 if (heap_->IsConcurrentFullMark()) {
148 if (heap_->GetConcurrentMarker()->IsEnabled()) {
149 duration += heap_->GetConcurrentMarker()->GetDuration();
150 }
151 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
152 }
153 break;
154 }
155 case TriggerGCType::FULL_GC: {
156 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
157 break;
158 }
159 default:
160 break;
161 }
162 }
163
RecordAfterConcurrentMark(MarkType markType,const ConcurrentMarker * marker)164 void MemController::RecordAfterConcurrentMark(MarkType markType, const ConcurrentMarker *marker)
165 {
166 double duration = marker->GetDuration();
167 if (markType == MarkType::MARK_FULL) {
168 recordedConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
169 } else if (markType == MarkType::MARK_YOUNG) {
170 recordedSemiConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
171 } else if (markType == MarkType::MARK_EDEN) {
172 recordedEdenConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
173 }
174 }
175
CalculateMarkCompactSpeedPerMS()176 double MemController::CalculateMarkCompactSpeedPerMS()
177 {
178 markCompactSpeedCache_ = CalculateAverageSpeed(recordedMarkCompacts_);
179 if (markCompactSpeedCache_ > 0) {
180 return markCompactSpeedCache_;
181 }
182 return 0;
183 }
184
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer,const BytesAndDuration & initial,const double timeMs)185 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer,
186 const BytesAndDuration &initial, const double timeMs)
187 {
188 BytesAndDuration sum = buffer.Sum(
189 [timeMs](BytesAndDuration a, BytesAndDuration b) {
190 if (timeMs != 0 && a.second >= timeMs) {
191 return a;
192 }
193 return std::make_pair(a.first + b.first, a.second + b.second);
194 },
195 initial);
196 uint64_t bytes = sum.first;
197 double durations = sum.second;
198 if (fabs(durations) <= 1e-6) {
199 return 0;
200 }
201 double speed = bytes / durations;
202 const int maxSpeed = static_cast<int>(1_GB);
203 const int minSpeed = 1;
204 if (speed >= maxSpeed) {
205 return maxSpeed;
206 }
207 if (speed <= minSpeed) {
208 return minSpeed;
209 }
210 return speed;
211 }
212
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer)213 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer)
214 {
215 return CalculateAverageSpeed(buffer, MakeBytesAndDuration(0, 0), 0);
216 }
217
GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const218 double MemController::GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const
219 {
220 size_t allocatedSize = oldSpaceAllocSizeSinceGC_;
221 double duration = allocDurationSinceGc_;
222 return CalculateAverageSpeed(recordedOldSpaceAllocations_,
223 MakeBytesAndDuration(allocatedSize, duration), timeMs);
224 }
225
GetEdenSpaceAllocationThroughputPerMS() const226 double MemController::GetEdenSpaceAllocationThroughputPerMS() const
227 {
228 return CalculateAverageSpeed(recordedEdenSpaceAllocations_);
229 }
230
GetNewSpaceAllocationThroughputPerMS() const231 double MemController::GetNewSpaceAllocationThroughputPerMS() const
232 {
233 return CalculateAverageSpeed(recordedNewSpaceAllocations_);
234 }
235
GetEdenSpaceConcurrentMarkSpeedPerMS() const236 double MemController::GetEdenSpaceConcurrentMarkSpeedPerMS() const
237 {
238 return CalculateAverageSpeed(recordedEdenConcurrentMarks_);
239 }
240
GetNewSpaceConcurrentMarkSpeedPerMS() const241 double MemController::GetNewSpaceConcurrentMarkSpeedPerMS() const
242 {
243 return CalculateAverageSpeed(recordedSemiConcurrentMarks_);
244 }
245
GetOldSpaceAllocationThroughputPerMS() const246 double MemController::GetOldSpaceAllocationThroughputPerMS() const
247 {
248 return CalculateAverageSpeed(recordedOldSpaceAllocations_);
249 }
250
GetFullSpaceConcurrentMarkSpeedPerMS() const251 double MemController::GetFullSpaceConcurrentMarkSpeedPerMS() const
252 {
253 return CalculateAverageSpeed(recordedConcurrentMarks_);
254 }
255 } // namespace panda::ecmascript
256