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