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/parallel_evacuator.h" 20 21namespace panda::ecmascript { 22MemController::MemController(Heap *heap) : heap_(heap), allocTimeMs_(GetSystemTimeInMs()) 23{ 24 minAllocLimitGrowingStep_ = heap->GetEcmaVM()->GetEcmaParamConfiguration().GetMinAllocLimitGrowingStep(); 25} 26 27size_t MemController::CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity, 28 double factor) const 29{ 30 const uint64_t limit = std::max(static_cast<uint64_t>(currentSize * factor), 31 static_cast<uint64_t>(currentSize) + minAllocLimitGrowingStep_) + 32 newSpaceCapacity; 33 34 const uint64_t limitAboveMinSize = std::max<uint64_t>(limit, minSize); 35 const uint64_t halfToMaxSize = (static_cast<uint64_t>(currentSize) + maxSize) / 2; 36 size_t result = static_cast<size_t>(std::min(limitAboveMinSize, halfToMaxSize)); 37 result = static_cast<size_t>(std::min(result, maxSize)); 38 return result; 39} 40 41double MemController::CalculateGrowingFactor(double gcSpeed, double mutatorSpeed) 42{ 43 double maxGrowingFactor = 4.0; 44 double halfMaxGrowingFactor = 2.0; 45 double minGrowingFactor = 1.3; 46 double minimumFactor = 1.1; 47 switch (heap_->GetMemGrowingType()) { 48 case MemGrowingType::HIGH_THROUGHPUT: 49 break; 50 case MemGrowingType::CONSERVATIVE: 51 minGrowingFactor = minimumFactor; 52 maxGrowingFactor = halfMaxGrowingFactor; 53 break; 54 case MemGrowingType::PRESSURE: 55 return minimumFactor; 56 default: 57 break; 58 } 59 60 static constexpr double targetMutatorUtilization = 0.97; 61 if (gcSpeed == 0 || mutatorSpeed == 0) { 62 return maxGrowingFactor; 63 } 64 65 const double speedRatio = gcSpeed / mutatorSpeed; 66 67 const double a = speedRatio * (1 - targetMutatorUtilization); 68 const double b = speedRatio * (1 - targetMutatorUtilization) - targetMutatorUtilization; 69 70 double factor = (a < b * maxGrowingFactor) ? a / b : maxGrowingFactor; 71 factor = std::min(maxGrowingFactor, factor); 72 factor = std::max(factor, minGrowingFactor); 73 OPTIONAL_LOG(heap_->GetEcmaVM(), INFO) << "CalculateGrowingFactor gcSpeed" 74 << gcSpeed << " mutatorSpeed" << mutatorSpeed << " factor" << factor; 75 return factor; 76} 77 78void MemController::StartCalculationBeforeGC() 79{ 80 startCounter_++; 81 if (startCounter_ != 1) { 82 return; 83 } 84 85 auto edenSpace = heap_->GetEdenSpace(); 86 size_t edenSpaceAllocBytesSinceGC = edenSpace->GetAllocatedSizeSinceGC(edenSpace->GetTop()); 87 // It's unnecessary to calculate newSpaceAllocAccumulatedSize. newSpaceAllocBytesSinceGC can be calculated directly. 88 auto newSpace = heap_->GetNewSpace(); 89 size_t newSpaceAllocBytesSinceGC = newSpace->GetAllocatedSizeSinceGC(newSpace->GetTop()); 90 size_t hugeObjectAllocSizeSinceGC = heap_->GetHugeObjectSpace()->GetHeapObjectSize() - hugeObjectAllocSizeSinceGC_; 91 size_t oldSpaceAllocAccumulatedSize = heap_->GetOldSpace()->GetTotalAllocatedSize(); 92 size_t nonMovableSpaceAllocAccumulatedSize = heap_->GetNonMovableSpace()->GetTotalAllocatedSize(); 93 size_t codeSpaceAllocAccumulatedSize = heap_->GetMachineCodeSpace()->GetTotalAllocatedSize(); 94 double currentTimeInMs = GetSystemTimeInMs(); 95 gcStartTime_ = currentTimeInMs; 96 size_t oldSpaceAllocSize = oldSpaceAllocAccumulatedSize - oldSpaceAllocAccumulatedSize_; 97 size_t nonMovableSpaceAllocSize = nonMovableSpaceAllocAccumulatedSize - nonMovableSpaceAllocAccumulatedSize_; 98 size_t codeSpaceAllocSize = codeSpaceAllocAccumulatedSize - codeSpaceAllocAccumulatedSize_; 99 100 double duration = currentTimeInMs - allocTimeMs_; 101 allocTimeMs_ = currentTimeInMs; 102 oldSpaceAllocAccumulatedSize_ = oldSpaceAllocAccumulatedSize; 103 nonMovableSpaceAllocAccumulatedSize_ = nonMovableSpaceAllocAccumulatedSize; 104 codeSpaceAllocAccumulatedSize_ = codeSpaceAllocAccumulatedSize; 105 106 allocDurationSinceGc_ += duration; 107 108 edenSpaceAllocSizeSinceGC_ += edenSpaceAllocBytesSinceGC; 109 newSpaceAllocSizeSinceGC_ += newSpaceAllocBytesSinceGC; 110 oldSpaceAllocSizeSinceGC_ += oldSpaceAllocSize; 111 oldSpaceAllocSizeSinceGC_ += hugeObjectAllocSizeSinceGC; 112 nonMovableSpaceAllocSizeSinceGC_ += nonMovableSpaceAllocSize; 113 codeSpaceAllocSizeSinceGC_ += codeSpaceAllocSize; 114 115 if (heap_->GetEcmaGCStats()->GetGCReason() != GCReason::IDLE) { 116 recordedIdleNewSpaceAllocations_.Push(MakeBytesAndDuration( 117 heap_->GetNewSpace()->GetHeapObjectSize() - newSpaceRecordLastTimeSizeIdle_, 118 currentTimeInMs - allocTimeMsIdle_)); 119 recordedIdleOldSpaceAllocations_.Push(MakeBytesAndDuration( 120 heap_->GetOldSpace()->GetHeapObjectSize() - oldSpaceRecordLastTimeSizeIdle_, 121 currentTimeInMs - allocTimeMsIdle_)); 122 } 123} 124 125void MemController::RecordAllocationForIdle() 126{ 127 double currentTimeInMs = GetSystemTimeInMs(); 128 size_t currentNewSpaceObjectSize = heap_->GetNewSpace()->GetHeapObjectSize(); 129 size_t currentOldSpaceObjectSize = heap_->GetOldSpace()->GetHeapObjectSize(); 130 double duration = currentTimeInMs - allocTimeMsIdle_; 131 allocTimeMsIdle_ = currentTimeInMs; 132 if (currentNewSpaceObjectSize < newSpaceRecordLastTimeSizeIdle_ || 133 currentOldSpaceObjectSize < oldSpaceRecordLastTimeSizeIdle_) { 134 newSpaceRecordLastTimeSizeIdle_ = currentNewSpaceObjectSize; 135 oldSpaceRecordLastTimeSizeIdle_ = currentOldSpaceObjectSize; 136 return; 137 } 138 139 size_t newSpaceAllocSizeSinceIdle = currentNewSpaceObjectSize - newSpaceRecordLastTimeSizeIdle_; 140 newSpaceRecordLastTimeSizeIdle_ = currentNewSpaceObjectSize; 141 size_t oldSpaceAllocSizeSinceIdle = currentOldSpaceObjectSize - oldSpaceRecordLastTimeSizeIdle_; 142 oldSpaceRecordLastTimeSizeIdle_ = currentOldSpaceObjectSize; 143 recordedIdleNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceIdle, duration)); 144 recordedIdleOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceIdle, duration)); 145} 146 147bool MemController::CheckLowAllocationUsageState() const 148{ 149 LOG_GC(DEBUG) << "local CheckLowAllocationUsageState NewSpaceAllocBytesPerMS:" << 150 GetIdleNewSpaceAllocationThroughputPerMS() << ",OldSpaceAllocBytesPerMS:" << 151 GetIdleOldSpaceAllocationThroughputPerMS(); 152 return GetIdleNewSpaceAllocationThroughputPerMS() < LOW_ALLOCATION_RATE_PER_MS && 153 GetIdleOldSpaceAllocationThroughputPerMS() < LOW_ALLOCATION_RATE_PER_MS; 154} 155 156void MemController::StopCalculationAfterGC(TriggerGCType gcType) 157{ 158 startCounter_--; 159 if (startCounter_ != 0) { 160 return; 161 } 162 163 gcEndTime_ = GetSystemTimeInMs(); 164 allocTimeMs_ = gcEndTime_; 165 if (allocDurationSinceGc_ > 0) { 166 oldSpaceAllocSizeSinceGC_ += heap_->GetEvacuator()->GetPromotedSize(); 167 recordedEdenSpaceAllocations_.Push(MakeBytesAndDuration(edenSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); 168 recordedNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); 169 recordedOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); 170 recordedNonmovableSpaceAllocations_.Push( 171 MakeBytesAndDuration(nonMovableSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); 172 recordedCodeSpaceAllocations_.Push(MakeBytesAndDuration(codeSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); 173 } 174 allocDurationSinceGc_ = 0.0; 175 edenSpaceAllocSizeSinceGC_ = 0; 176 newSpaceAllocSizeSinceGC_ = 0; 177 oldSpaceAllocSizeSinceGC_ = 0; 178 nonMovableSpaceAllocSizeSinceGC_ = 0; 179 codeSpaceAllocSizeSinceGC_ = 0; 180 181 hugeObjectAllocSizeSinceGC_ = heap_->GetHugeObjectSpace()->GetHeapObjectSize(); 182 183 double duration = gcEndTime_ - gcStartTime_; 184 switch (gcType) { 185 case TriggerGCType::YOUNG_GC: 186 case TriggerGCType::OLD_GC: { 187 if (heap_->IsConcurrentFullMark()) { 188 if (heap_->GetConcurrentMarker()->IsEnabled()) { 189 duration += heap_->GetConcurrentMarker()->GetDuration(); 190 } 191 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration)); 192 } 193 break; 194 } 195 case TriggerGCType::FULL_GC: { 196 recordedMarkCompacts_.Push(MakeBytesAndDuration(heap_->GetHeapObjectSize(), duration)); 197 break; 198 } 199 default: 200 break; 201 } 202 203 if (heap_->GetEcmaGCStats()->GetGCReason() != GCReason::IDLE) { 204 newSpaceRecordLastTimeSizeIdle_ = heap_->GetNewSpace()->GetHeapObjectSize(); 205 oldSpaceRecordLastTimeSizeIdle_ = heap_->GetOldSpace()->GetHeapObjectSize(); 206 allocTimeMsIdle_ = gcEndTime_; 207 } 208} 209 210void MemController::RecordAfterConcurrentMark(MarkType markType, const ConcurrentMarker *marker) 211{ 212 double duration = marker->GetDuration(); 213 if (markType == MarkType::MARK_FULL) { 214 recordedConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration)); 215 } else if (markType == MarkType::MARK_YOUNG) { 216 recordedSemiConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration)); 217 } else if (markType == MarkType::MARK_EDEN) { 218 recordedEdenConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration)); 219 } 220} 221 222double MemController::CalculateMarkCompactSpeedPerMS() 223{ 224 markCompactSpeedCache_ = CalculateAverageSpeed(recordedMarkCompacts_); 225 if (markCompactSpeedCache_ > 0) { 226 return markCompactSpeedCache_; 227 } 228 return 0; 229} 230 231double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer, 232 const BytesAndDuration &initial, const double timeMs) 233{ 234 BytesAndDuration sum = buffer.Sum( 235 [timeMs](BytesAndDuration a, BytesAndDuration b) { 236 if (timeMs != 0 && a.second >= timeMs) { 237 return a; 238 } 239 return std::make_pair(a.first + b.first, a.second + b.second); 240 }, 241 initial); 242 uint64_t bytes = sum.first; 243 double durations = sum.second; 244 if (fabs(durations) <= 1e-6) { 245 return 0; 246 } 247 double speed = bytes / durations; 248 const int maxSpeed = static_cast<int>(1_GB); 249 const int minSpeed = 1; 250 if (speed >= maxSpeed) { 251 return maxSpeed; 252 } 253 if (speed <= minSpeed) { 254 return minSpeed; 255 } 256 return speed; 257} 258 259double MemController::CalculateAverageSpeed(const base::GCRingBuffer<BytesAndDuration, LENGTH> &buffer) 260{ 261 return CalculateAverageSpeed(buffer, MakeBytesAndDuration(0, 0), 0); 262} 263 264double MemController::GetCurrentOldSpaceAllocationThroughputPerMS(double timeMs) const 265{ 266 size_t allocatedSize = oldSpaceAllocSizeSinceGC_; 267 double duration = allocDurationSinceGc_; 268 return CalculateAverageSpeed(recordedOldSpaceAllocations_, 269 MakeBytesAndDuration(allocatedSize, duration), timeMs); 270} 271 272double MemController::GetEdenSpaceAllocationThroughputPerMS() const 273{ 274 return CalculateAverageSpeed(recordedEdenSpaceAllocations_); 275} 276 277double MemController::GetNewSpaceAllocationThroughputPerMS() const 278{ 279 return CalculateAverageSpeed(recordedNewSpaceAllocations_); 280} 281 282double MemController::GetEdenSpaceConcurrentMarkSpeedPerMS() const 283{ 284 return CalculateAverageSpeed(recordedEdenConcurrentMarks_); 285} 286 287double MemController::GetNewSpaceConcurrentMarkSpeedPerMS() const 288{ 289 return CalculateAverageSpeed(recordedSemiConcurrentMarks_); 290} 291 292double MemController::GetOldSpaceAllocationThroughputPerMS() const 293{ 294 return CalculateAverageSpeed(recordedOldSpaceAllocations_); 295} 296 297double MemController::GetFullSpaceConcurrentMarkSpeedPerMS() const 298{ 299 return CalculateAverageSpeed(recordedConcurrentMarks_); 300} 301 302double MemController::GetIdleNewSpaceAllocationThroughputPerMS() const 303{ 304 return CalculateAverageSpeed(recordedIdleNewSpaceAllocations_); 305} 306 307double MemController::GetIdleOldSpaceAllocationThroughputPerMS() const 308{ 309 return CalculateAverageSpeed(recordedIdleOldSpaceAllocations_); 310} 311 312} // namespace panda::ecmascript 313