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_evacuation.h"
21
22 namespace panda::ecmascript {
MemController(Heap * heap)23 MemController::MemController(Heap *heap) : heap_(heap), allocTimeMs_(GetSystemTimeInMs()) {}
24
CalculateAllocLimit(size_t currentSize,size_t minSize,size_t maxSize,size_t newSpaceCapacity,double factor) const25 double MemController::CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity,
26 double factor) const
27 {
28 const uint64_t limit = std::max(static_cast<uint64_t>(currentSize * factor),
29 static_cast<uint64_t>(currentSize) + MIN_AllOC_LIMIT_GROWING_STEP) +
30 newSpaceCapacity;
31
32 const uint64_t limitAboveMinSize = std::max<uint64_t>(limit, minSize);
33 const uint64_t halfToMaxSize = (static_cast<uint64_t>(currentSize) + maxSize) / 2;
34 const auto result = static_cast<size_t>(std::min(limitAboveMinSize, halfToMaxSize));
35 return result;
36 }
37
CalculateGrowingFactor(double gcSpeed,double mutatorSpeed)38 double MemController::CalculateGrowingFactor(double gcSpeed, double mutatorSpeed)
39 {
40 static constexpr double minGrowingFactor = 1.3;
41 static constexpr double maxGrowingFactor = 4.0;
42 static constexpr double targetMutatorUtilization = 0.97;
43 if (gcSpeed == 0 || mutatorSpeed == 0) {
44 return maxGrowingFactor;
45 }
46
47 const double speedRatio = gcSpeed / mutatorSpeed;
48
49 const double a = speedRatio * (1 - targetMutatorUtilization);
50 const double b = speedRatio * (1 - targetMutatorUtilization) - targetMutatorUtilization;
51
52 double factor = (a < b * maxGrowingFactor) ? a / b : maxGrowingFactor;
53 factor = std::min(maxGrowingFactor, factor);
54 factor = std::max(factor, minGrowingFactor);
55 OPTIONAL_LOG(heap_->GetEcmaVM(), ERROR, ECMASCRIPT) << "CalculateGrowingFactor gcSpeed"
56 << gcSpeed << " mutatorSpeed" << mutatorSpeed << " factor" << factor;
57 return factor;
58 }
59
StartCalculationBeforeGC()60 void MemController::StartCalculationBeforeGC()
61 {
62 startCounter_++;
63 if (startCounter_ != 1) {
64 return;
65 }
66
67 // It's unnecessary to calculate newSpaceAllocAccumulatorSize. newSpaceAllocBytesSinceGC can be calculated directly.
68 size_t newSpaceAllocBytesSinceGC = heap_->GetNewSpace()->GetAllocatedSizeSinceGC();
69 size_t hugeObjectAllocSizeSinceGC = heap_->GetHugeObjectSpace()->GetHeapObjectSize() - hugeObjectAllocSizeSinceGC_;
70 size_t oldSpaceAllocAccumulatorSize = heap_->GetOldSpace()->GetTotalAllocatedSize();
71 size_t nonMovableSpaceAllocAccumulatorSize = heap_->GetNonMovableSpace()->GetTotalAllocatedSize();
72 size_t codeSpaceAllocAccumulatorSize = heap_->GetMachineCodeSpace()->GetTotalAllocatedSize();
73 double currentTimeInMs = GetSystemTimeInMs();
74 gcStartTime_ = currentTimeInMs;
75 size_t oldSpaceAllocSize = oldSpaceAllocAccumulatorSize - oldSpaceAllocAccumulatorSize_;
76 size_t nonMovableSpaceAllocSize = nonMovableSpaceAllocAccumulatorSize - nonMovableSpaceAllocAccumulatorSize_;
77 size_t codeSpaceAllocSize = codeSpaceAllocAccumulatorSize - codeSpaceAllocAccumulatorSize_;
78
79 double duration = currentTimeInMs - allocTimeMs_;
80 allocTimeMs_ = currentTimeInMs;
81 oldSpaceAllocAccumulatorSize_ = oldSpaceAllocAccumulatorSize;
82 nonMovableSpaceAllocAccumulatorSize_ = nonMovableSpaceAllocAccumulatorSize;
83 codeSpaceAllocAccumulatorSize_ = codeSpaceAllocAccumulatorSize;
84
85 allocDurationSinceGc_ += duration;
86 newSpaceAllocSizeSinceGC_ += newSpaceAllocBytesSinceGC;
87 oldSpaceAllocSizeSinceGC_ += oldSpaceAllocSize;
88 oldSpaceAllocSizeSinceGC_ += hugeObjectAllocSizeSinceGC;
89 nonMovableSpaceAllocSizeSinceGC_ += nonMovableSpaceAllocSize;
90 codeSpaceAllocSizeSinceGC_ += codeSpaceAllocSize;
91 }
92
StopCalculationAfterGC(TriggerGCType gcType)93 void MemController::StopCalculationAfterGC(TriggerGCType gcType)
94 {
95 startCounter_--;
96 if (startCounter_ != 0) {
97 return;
98 }
99
100 gcEndTime_ = GetSystemTimeInMs();
101 allocTimeMs_ = gcEndTime_;
102 if (allocDurationSinceGc_ > 0) {
103 oldSpaceAllocSizeSinceGC_ += heap_->GetEvacuation()->GetPromotedSize();
104 recordedNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
105 recordedOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
106 recordedNonmovableSpaceAllocations_.Push(
107 MakeBytesAndDuration(nonMovableSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
108 recordedCodeSpaceAllocations_.Push(MakeBytesAndDuration(codeSpaceAllocSizeSinceGC_, allocDurationSinceGc_));
109 }
110 allocDurationSinceGc_ = 0.0;
111 newSpaceAllocSizeSinceGC_ = 0;
112 oldSpaceAllocSizeSinceGC_ = 0;
113 nonMovableSpaceAllocSizeSinceGC_ = 0;
114 codeSpaceAllocSizeSinceGC_ = 0;
115
116 hugeObjectAllocSizeSinceGC_ = heap_->GetHugeObjectSpace()->GetHeapObjectSize();
117
118 double duration = gcEndTime_ - gcStartTime_;
119 switch (gcType) {
120 case TriggerGCType::SEMI_GC:
121 case TriggerGCType::OLD_GC: {
122 if (heap_->IsFullMark()) {
123 if (heap_->ConcurrentMarkingEnable()) {
124 duration += heap_->GetConcurrentMarker()->GetDuration();
125 }
126 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
127 }
128 break;
129 }
130 case TriggerGCType::FULL_GC: {
131 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration));
132 break;
133 }
134 default:
135 break;
136 }
137 }
138
RecordAfterConcurrentMark(const bool isFull,const ConcurrentMarker * marker)139 void MemController::RecordAfterConcurrentMark(const bool isFull, const ConcurrentMarker *marker)
140 {
141 double duration = marker->GetDuration();
142 if (isFull) {
143 recordedConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
144 } else {
145 recordedSemiConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration));
146 }
147 }
148
CalculateMarkCompactSpeedPerMS()149 double MemController::CalculateMarkCompactSpeedPerMS()
150 {
151 markCompactSpeedCache_ = CalculateAverageSpeed(recordedMarkCompacts_);
152 if (markCompactSpeedCache_ > 0) {
153 return markCompactSpeedCache_;
154 }
155 return 0;
156 }
157
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer,const BytesAndDuration & initial,const double timeMs)158 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer,
159 const BytesAndDuration &initial, const double timeMs)
160 {
161 BytesAndDuration sum = buffer.Sum(
162 [timeMs](BytesAndDuration a, BytesAndDuration b) {
163 if (timeMs != 0 && a.second >= timeMs) {
164 return a;
165 }
166 return std::make_pair(a.first + b.first, a.second + b.second);
167 },
168 initial);
169 uint64_t bytes = sum.first;
170 double durations = sum.second;
171 if (durations == 0.0) {
172 return 0;
173 }
174 double speed = bytes / durations;
175 const int maxSpeed = 1024 * 1024 * 1024;
176 const int minSpeed = 1;
177 if (speed >= maxSpeed) {
178 return maxSpeed;
179 }
180 if (speed <= minSpeed) {
181 return minSpeed;
182 }
183 return speed;
184 }
185
CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration,LENGTH> & buffer)186 double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer)
187 {
188 return CalculateAverageSpeed(buffer, MakeBytesAndDuration(0, 0), 0);
189 }
190
GetCurrentOldSpaceAllocationThroughtputPerMS(double timeMs) const191 double MemController::GetCurrentOldSpaceAllocationThroughtputPerMS(double timeMs) const
192 {
193 size_t allocatedSize = oldSpaceAllocSizeSinceGC_;
194 double duration = allocDurationSinceGc_;
195 return CalculateAverageSpeed(recordedOldSpaceAllocations_,
196 MakeBytesAndDuration(allocatedSize, duration), timeMs);
197 }
198
GetNewSpaceAllocationThroughtPerMS() const199 double MemController::GetNewSpaceAllocationThroughtPerMS() const
200 {
201 return CalculateAverageSpeed(recordedNewSpaceAllocations_);
202 }
203
GetNewSpaceConcurrentMarkSpeedPerMS() const204 double MemController::GetNewSpaceConcurrentMarkSpeedPerMS() const
205 {
206 return CalculateAverageSpeed(recordedSemiConcurrentMarks_);
207 }
208
GetOldSpaceAllocationThroughtPerMS() const209 double MemController::GetOldSpaceAllocationThroughtPerMS() const
210 {
211 return CalculateAverageSpeed(recordedOldSpaceAllocations_);
212 }
213
GetFullSpaceConcurrentMarkSpeedPerMS() const214 double MemController::GetFullSpaceConcurrentMarkSpeedPerMS() const
215 {
216 return CalculateAverageSpeed(recordedConcurrentMarks_);
217 }
218 } // namespace panda::ecmascript
219