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 27namespace panda::ecmascript { 28 29constexpr static int MILLISECONDS_PER_SECOND = 1000; 30 31using BytesAndDuration = std::pair<uint64_t, double>; 32 33class MemController { 34public: 35 explicit MemController(Heap* heap); 36 MemController() = default; 37 ~MemController() = default; 38 NO_COPY_SEMANTIC(MemController); 39 NO_MOVE_SEMANTIC(MemController); 40 41 static double GetSystemTimeInMs() 42 { 43 double currentTime = 44 std::chrono::duration<double>(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 45 return currentTime * MILLISECOND_PER_SECOND; 46 } 47 48 size_t CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity, 49 double factor) const; 50 51 double CalculateGrowingFactor(double gcSpeed, double mutatorSpeed); 52 53 void StartCalculationBeforeGC(); 54 void StopCalculationAfterGC(TriggerGCType gcType); 55 56 void RecordAllocationForIdle(); 57 double GetIdleNewSpaceAllocationThroughputPerMS() const; 58 double GetIdleOldSpaceAllocationThroughputPerMS() const; 59 bool CheckLowAllocationUsageState() const; 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 72 double GetAllocTimeMs() const 73 { 74 return allocTimeMs_; 75 } 76 77 size_t GetOldSpaceAllocAccumulatedSize() const 78 { 79 return oldSpaceAllocAccumulatedSize_; 80 } 81 82 size_t GetNonMovableSpaceAllocAccumulatedSize() const 83 { 84 return nonMovableSpaceAllocAccumulatedSize_; 85 } 86 87 size_t GetCodeSpaceAllocAccumulatedSize() const 88 { 89 return codeSpaceAllocAccumulatedSize_; 90 } 91 92 double GetAllocDurationSinceGc() const 93 { 94 return allocDurationSinceGc_; 95 } 96 97 size_t GetEdenSpaceAllocSizeSinceGC() const 98 { 99 return edenSpaceAllocSizeSinceGC_; 100 } 101 102 size_t GetNewSpaceAllocSizeSinceGC() const 103 { 104 return newSpaceAllocSizeSinceGC_; 105 } 106 107 size_t GetOldSpaceAllocSizeSinceGC() const 108 { 109 return oldSpaceAllocSizeSinceGC_; 110 } 111 112 size_t GetNonMovableSpaceAllocSizeSinceGC() const 113 { 114 return nonMovableSpaceAllocSizeSinceGC_; 115 } 116 117 size_t GetCodeSpaceAllocSizeSinceGC() const 118 { 119 return codeSpaceAllocSizeSinceGC_; 120 } 121 122 size_t GetHugeObjectAllocSizeSinceGC() const 123 { 124 return hugeObjectAllocSizeSinceGC_; 125 } 126 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 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 147 void AddEdenSurvivalRate(double rate) 148 { 149 recordedEdenSurvivalRates_.Push(rate); 150 } 151 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 162 double GetPredictedSurvivalRate() const 163 { 164 if (UNLIKELY(std::isnan(predictedSurvivalRate_))) { 165 return 0; 166 } 167 return predictedSurvivalRate_; 168 } 169 170 void ResetRecordedSurvivalRates() 171 { 172 recordedSurvivalRates_.Reset(); 173 } 174 175private: 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 // Records data at idle time points. 205 double allocTimeMsIdle_ {0.0}; 206 size_t newSpaceRecordLastTimeSizeIdle_ {0}; 207 size_t oldSpaceRecordLastTimeSizeIdle_ {0}; 208 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedIdleNewSpaceAllocations_; 209 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedIdleOldSpaceAllocations_; 210 211 int startCounter_ {0}; 212 double markCompactSpeedCache_ {0.0}; 213 214 double predictedSurvivalRate_ {std::numeric_limits<double>::quiet_NaN()}; 215 216 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedMarkCompacts_; 217 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedEdenSpaceAllocations_; 218 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedNewSpaceAllocations_; 219 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedOldSpaceAllocations_; 220 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedNonmovableSpaceAllocations_; 221 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedCodeSpaceAllocations_; 222 223 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedConcurrentMarks_; 224 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedEdenConcurrentMarks_; 225 base::GCRingBuffer<BytesAndDuration, LENGTH> recordedSemiConcurrentMarks_; 226 base::GCRingBuffer<double, LENGTH> recordedSurvivalRates_; 227 base::GCRingBuffer<double, LENGTH> recordedEdenSurvivalRates_; 228 229 static constexpr double THROUGHPUT_TIME_FRAME_MS = 5000; 230 static constexpr int MILLISECOND_PER_SECOND = 1000; 231}; 232 233MemController *CreateMemController(Heap *heap, std::string_view gcTriggerType); 234} // namespace panda::ecmascript 235#endif // ECMASCRIPT_MEM_MEM_CONTROLLER_H 236