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/heap-inl.h"
20 #include "ecmascript/mem/parallel_evacuator.h"
21
22 namespace panda::ecmascript {
MemController(Heap * heap)23 MemController::MemController(Heap *heap) : heap_(heap), allocTimeMs_(GetSystemTimeInMs())
24 {
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:
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 newSpaceAllocSizeSinceGC_ += newSpaceAllocBytesSinceGC;
107 oldSpaceAllocSizeSinceGC_ += oldSpaceAllocSize;
108 oldSpaceAllocSizeSinceGC_ += hugeObjectAllocSizeSinceGC;
109 nonMovableSpaceAllocSizeSinceGC_ += nonMovableSpaceAllocSize;
110 codeSpaceAllocSizeSinceGC_ += codeSpaceAllocSize;
111 }
112
StopCalculationAfterGC(TriggerGCType gcType)113 void MemController::StopCalculationAfterGC(TriggerGCType gcType)
114 {
115 startCounter_--;
116 if (startCounter_ != 0) {
117 return;
118 }
119
120 gcEndTime_ = GetSystemTimeInMs();
121 allocTimeMs_ = gcEndTime_;
122 if (allocDurationSinceGc_ > 0) {
123 oldSpaceAllocSizeSinceGC_ += heap_->GetEvacuator()->GetPromotedSize();
124 recordedNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
125 recordedOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
126 recordedNonmovableSpaceAllocations_.Push(
127 MakeBytesAndDuration(nonMovableSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
128 recordedCodeSpaceAllocations_.Push(MakeBytesAndDuration(codeSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
129 }
130 allocDurationSinceGc_ = 0.0;
131 newSpaceAllocSizeSinceGC_ = 0;
132 oldSpaceAllocSizeSinceGC_ = 0;
133 nonMovableSpaceAllocSizeSinceGC_ = 0;
134 codeSpaceAllocSizeSinceGC_ = 0;
135
136 hugeObjectAllocSizeSinceGC_ = heap_->GetHugeObjectSpace()->GetHeapObjectSize();
137
138 double duration = gcEndTime_ - gcStartTime_;
139 switch (gcType) {
140 case TriggerGCType::YOUNG_GC:
141 case TriggerGCType::OLD_GC: {
142 if (heap_->IsFullMark()) {
143 if (heap_->GetConcurrentMarker()->IsEnabled()) {
144 duration += heap_->GetConcurrentMarker()->GetDuration();
145 }
146 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
147 }
148 break;
149 }
150 case TriggerGCType::FULL_GC: {
151 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
152 break;
153 }
154 default:
155 break;
156 }
157 }
158
RecordAfterConcurrentMark(const bool isFull,const ConcurrentMarker * marker)159 void MemController::RecordAfterConcurrentMark(const bool isFull, const ConcurrentMarker *marker)
160 {
161 double duration = marker->GetDuration();
162 if (isFull) {
163 recordedConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
164 } else {
165 recordedSemiConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
166 }
167 }
168
CalculateMarkCompactSpeedPerMS()169 double MemController::CalculateMarkCompactSpeedPerMS()
170 {
171 markCompactSpeedCache_ = CalculateAverageSpeed(recordedMarkCompacts_);
172 if (markCompactSpeedCache_ > 0) {
173 return markCompactSpeedCache_;
174 }
175 return 0;
176 }
177
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer,const BytesAndDuration & initial,const double timeMs)178 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer,
179 const BytesAndDuration &initial, const double timeMs)
180 {
181 BytesAndDuration sum = buffer.Sum(
182 [timeMs](BytesAndDuration a, BytesAndDuration b) {
183 if (timeMs != 0 && a.second >= timeMs) {
184 return a;
185 }
186 return std::make_pair(a.first + b.first, a.second + b.second);
187 },
188 initial);
189 uint64_t bytes = sum.first;
190 double durations = sum.second;
191 if (durations == 0.0) {
192 return 0;
193 }
194 double speed = bytes / durations;
195 const int maxSpeed = static_cast<int>(1_GB);
196 const int minSpeed = 1;
197 if (speed >= maxSpeed) {
198 return maxSpeed;
199 }
200 if (speed <= minSpeed) {
201 return minSpeed;
202 }
203 return speed;
204 }
205
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer)206 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer)
207 {
208 return CalculateAverageSpeed(buffer, MakeBytesAndDuration(0, 0), 0);
209 }
210
GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const211 double MemController::GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const
212 {
213 size_t allocatedSize = oldSpaceAllocSizeSinceGC_;
214 double duration = allocDurationSinceGc_;
215 return CalculateAverageSpeed(recordedOldSpaceAllocations_,
216 MakeBytesAndDuration(allocatedSize, duration), timeMs);
217 }
218
GetNewSpaceAllocationThroughputPerMS() const219 double MemController::GetNewSpaceAllocationThroughputPerMS() const
220 {
221 return CalculateAverageSpeed(recordedNewSpaceAllocations_);
222 }
223
GetNewSpaceConcurrentMarkSpeedPerMS() const224 double MemController::GetNewSpaceConcurrentMarkSpeedPerMS() const
225 {
226 return CalculateAverageSpeed(recordedSemiConcurrentMarks_);
227 }
228
GetOldSpaceAllocationThroughputPerMS() const229 double MemController::GetOldSpaceAllocationThroughputPerMS() const
230 {
231 return CalculateAverageSpeed(recordedOldSpaceAllocations_);
232 }
233
GetFullSpaceConcurrentMarkSpeedPerMS() const234 double MemController::GetFullSpaceConcurrentMarkSpeedPerMS() const
235 {
236 return CalculateAverageSpeed(recordedConcurrentMarks_);
237 }
238 } // namespace panda::ecmascript
239