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