1 /*
2 * Copyright (c) 2021-2024 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 #ifndef ECMASCRIPT_MEM_MEM_CONTROLLER_H
17 #define ECMASCRIPT_MEM_MEM_CONTROLLER_H
18
19 #include <chrono>
20 #include <cmath>
21 #include <limits>
22
23 #include "ecmascript/base/gc_ring_buffer.h"
24 #include "ecmascript/mem/heap.h"
25 #include "ecmascript/mem/mem.h"
26
27 namespace panda::ecmascript {
28
29 constexpr static int MILLISECONDS_PER_SECOND = 1000;
30
31 using BytesAndDuration = std::pair<uint64_t, double>;
32
MakeBytesAndDuration(uint64_t bytes,double duration)33 inline BytesAndDuration MakeBytesAndDuration(uint64_t bytes, double duration)
34 {
35 return std::make_pair(bytes, duration);
36 }
37
38 class MemController {
39 public:
40 explicit MemController(Heap* heap);
41 MemController() = default;
42 ~MemController() = default;
43 NO_COPY_SEMANTIC(MemController);
44 NO_MOVE_SEMANTIC(MemController);
45
GetSystemTimeInMs()46 static double GetSystemTimeInMs()
47 {
48 double currentTime =
49 std::chrono::duration<double>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
50 return currentTime * MILLISECOND_PER_SECOND;
51 }
52
53 size_t CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity,
54 double factor) const;
55
56 double CalculateGrowingFactor(double gcSpeed, double mutatorSpeed);
57
58 void StartCalculationBeforeGC();
59 void StopCalculationAfterGC(TriggerGCType gcType);
60
61 void RecordAfterConcurrentMark(MarkType markType, const ConcurrentMarker *marker);
62
63 double CalculateMarkCompactSpeedPerMS();
64 double GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs = THROUGHPUT_TIME_FRAME_MS) const;
65 double GetEdenSpaceAllocationThroughputPerMS() const;
66 double GetNewSpaceAllocationThroughputPerMS() const;
67 double GetOldSpaceAllocationThroughputPerMS() const;
68 double GetEdenSpaceConcurrentMarkSpeedPerMS() const;
69 double GetNewSpaceConcurrentMarkSpeedPerMS() const;
70 double GetFullSpaceConcurrentMarkSpeedPerMS() const;
71
GetAllocTimeMs()72 double GetAllocTimeMs() const
73 {
74 return allocTimeMs_;
75 }
76
GetOldSpaceAllocAccumulatedSize()77 size_t GetOldSpaceAllocAccumulatedSize() const
78 {
79 return oldSpaceAllocAccumulatedSize_;
80 }
81
GetNonMovableSpaceAllocAccumulatedSize()82 size_t GetNonMovableSpaceAllocAccumulatedSize() const
83 {
84 return nonMovableSpaceAllocAccumulatedSize_;
85 }
86
GetCodeSpaceAllocAccumulatedSize()87 size_t GetCodeSpaceAllocAccumulatedSize() const
88 {
89 return codeSpaceAllocAccumulatedSize_;
90 }
91
GetAllocDurationSinceGc()92 double GetAllocDurationSinceGc() const
93 {
94 return allocDurationSinceGc_;
95 }
96
GetEdenSpaceAllocSizeSinceGC()97 size_t GetEdenSpaceAllocSizeSinceGC() const
98 {
99 return edenSpaceAllocSizeSinceGC_;
100 }
101
GetNewSpaceAllocSizeSinceGC()102 size_t GetNewSpaceAllocSizeSinceGC() const
103 {
104 return newSpaceAllocSizeSinceGC_;
105 }
106
GetOldSpaceAllocSizeSinceGC()107 size_t GetOldSpaceAllocSizeSinceGC() const
108 {
109 return oldSpaceAllocSizeSinceGC_;
110 }
111
GetNonMovableSpaceAllocSizeSinceGC()112 size_t GetNonMovableSpaceAllocSizeSinceGC() const
113 {
114 return nonMovableSpaceAllocSizeSinceGC_;
115 }
116
GetCodeSpaceAllocSizeSinceGC()117 size_t GetCodeSpaceAllocSizeSinceGC() const
118 {
119 return codeSpaceAllocSizeSinceGC_;
120 }
121
GetHugeObjectAllocSizeSinceGC()122 size_t GetHugeObjectAllocSizeSinceGC() const
123 {
124 return hugeObjectAllocSizeSinceGC_;
125 }
126
AddSurvivalRate(double rate)127 void AddSurvivalRate(double rate)
128 {
129 recordedSurvivalRates_.Push(rate);
130 if (UNLIKELY(std::isnan(predictedSurvivalRate_))) {
131 predictedSurvivalRate_ = rate;
132 } else {
133 predictedSurvivalRate_ = ALPHA * rate + (1 - ALPHA) * predictedSurvivalRate_;
134 }
135 }
136
GetAverageSurvivalRate()137 double GetAverageSurvivalRate() const
138 {
139 int count = recordedSurvivalRates_.Count();
140 if (count == 0) {
141 return 0;
142 }
143 double result = recordedSurvivalRates_.Sum([](double x, double y) { return x + y;}, 0.0);
144 return result / count;
145 }
146
AddEdenSurvivalRate(double rate)147 void AddEdenSurvivalRate(double rate)
148 {
149 recordedEdenSurvivalRates_.Push(rate);
150 }
151
GetAverageEdenSurvivalRate()152 double GetAverageEdenSurvivalRate() const
153 {
154 int count = recordedEdenSurvivalRates_.Count();
155 if (count == 0) {
156 return 0;
157 }
158 double result = recordedEdenSurvivalRates_.Sum([](double x, double y) { return x + y;}, 0.0);
159 return result / count;
160 }
161
GetPredictedSurvivalRate()162 double GetPredictedSurvivalRate() const
163 {
164 if (UNLIKELY(std::isnan(predictedSurvivalRate_))) {
165 return 0;
166 }
167 return predictedSurvivalRate_;
168 }
169
ResetRecordedSurvivalRates()170 void ResetRecordedSurvivalRates()
171 {
172 recordedSurvivalRates_.Reset();
173 }
174
175 private:
176 static constexpr int LENGTH = 10;
177 // Decayed weight for predicting survival rate.
178 static constexpr double ALPHA = 0.8;
179 static double CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer);
180 static double CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer,
181 const BytesAndDuration &initial, const double timeMs);
182
183 Heap* heap_;
184 size_t minAllocLimitGrowingStep_ {0};
185
186 double gcStartTime_ {0.0};
187 double gcEndTime_ {0.0};
188
189 // Time and allocation accumulators.
190 double allocTimeMs_ {0.0};
191 size_t oldSpaceAllocAccumulatedSize_ {0};
192 size_t nonMovableSpaceAllocAccumulatedSize_ {0};
193 size_t codeSpaceAllocAccumulatedSize_ {0};
194
195 // Duration and allocation size in last gc.
196 double allocDurationSinceGc_ {0.0};
197 size_t edenSpaceAllocSizeSinceGC_ {0};
198 size_t newSpaceAllocSizeSinceGC_ {0};
199 size_t oldSpaceAllocSizeSinceGC_ {0};
200 size_t nonMovableSpaceAllocSizeSinceGC_ {0};
201 size_t codeSpaceAllocSizeSinceGC_ {0};
202 size_t hugeObjectAllocSizeSinceGC_{0};
203
204 int startCounter_ {0};
205 double markCompactSpeedCache_ {0.0};
206
207 double predictedSurvivalRate_ {std::numeric_limits<double>::quiet_NaN()};
208
209 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedMarkCompacts_;
210 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedEdenSpaceAllocations_;
211 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedNewSpaceAllocations_;
212 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedOldSpaceAllocations_;
213 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedNonmovableSpaceAllocations_;
214 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedCodeSpaceAllocations_;
215
216 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedConcurrentMarks_;
217 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedEdenConcurrentMarks_;
218 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedSemiConcurrentMarks_;
219 base::GCRingBuffer<double, LENGTH> recordedSurvivalRates_;
220 base::GCRingBuffer<double, LENGTH> recordedEdenSurvivalRates_;
221
222 static constexpr double THROUGHPUT_TIME_FRAME_MS = 5000;
223 static constexpr int MILLISECOND_PER_SECOND = 1000;
224 };
225
226 MemController *CreateMemController(Heap *heap, std::string_view gcTriggerType);
227 } // namespace panda::ecmascript
228 #endif // ECMASCRIPT_MEM_MEM_CONTROLLER_H
229