From 09aebe2aa7553c9d4af1e9295baf21437ec35d7c Mon Sep 17 00:00:00 2001 From: xiongluo Date: Wed, 22 Dec 2021 21:30:46 -1000 Subject: [PATCH] ioptimize gc trigger Signed-off-by: xiongluo --- ecmascript/base/cyclic_buffer.h | 80 +++++++++ ecmascript/mem/allocator-inl.h | 4 + ecmascript/mem/allocator.h | 6 + ecmascript/mem/concurrent_marker.cpp | 8 + ecmascript/mem/concurrent_marker.h | 19 +- ecmascript/mem/ecma_heap_manager-inl.h | 5 +- ecmascript/mem/heap-inl.h | 24 +++ ecmascript/mem/heap.cpp | 128 +++++++++++--- ecmascript/mem/heap.h | 13 +- ecmascript/mem/mem.h | 6 +- ecmascript/mem/mem_controller.cpp | 210 ++++++++++++++++++++++- ecmascript/mem/mem_controller.h | 120 ++++++++++++- ecmascript/mem/mix_space_collector.cpp | 1 + ecmascript/mem/parallel_evacuation.cpp | 8 + ecmascript/mem/parallel_evacuation.h | 7 + ecmascript/mem/space.cpp | 47 +++++ ecmascript/mem/space.h | 4 + ecmascript/tests/BUILD.gn | 28 +++ ecmascript/tests/mem_controller_test.cpp | 124 +++++++++++++ test/resource/js_runtime/ohos_test.xml | 5 + 20 files changed, 809 insertions(+), 38 deletions(-) create mode 100644 ecmascript/base/cyclic_buffer.h create mode 100644 ecmascript/tests/mem_controller_test.cpp diff --git a/ecmascript/base/cyclic_buffer.h b/ecmascript/base/cyclic_buffer.h new file mode 100644 index 0000000000..38985abe2e --- /dev/null +++ b/ecmascript/base/cyclic_buffer.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_BASE_CYCLIC_BUFFER_H +#define ECMASCRIPT_BASE_CYCLIC_BUFFER_H + +#include + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::base { +template +class CyclicBuffer { +public: + CyclicBuffer() = default; + ~CyclicBuffer() = default; + NO_COPY_SEMANTIC(CyclicBuffer); + NO_MOVE_SEMANTIC(CyclicBuffer); + + static constexpr int LENGTH = 10; + + void Push(const T &value) + { + if (count_ == LENGTH) { + elements_[start_++] = value; + if (start_ == LENGTH) { + start_ = 0; + } + } else { + ASSERT(start_ == 0); + elements_[count_++] = value; + } + } + + int Count() const + { + return count_; + } + template + T Sum(Callback callback, const T &initial) const + { + int j = start_ + count_ - 1; + if (j >= LENGTH) { + j -= LENGTH; + } + T result = initial; + for (int i = 0; i < count_; i++) { + result = callback(result, elements_[j]); + if (--j == -1) { + j += LENGTH; + } + } + return result; + } + + void Reset() + { + start_ = count_ = 0; + } + +private: + std::array elements_; + int start_ {0}; + int count_ {0}; +}; +} // namespace panda::ecmascript::base + +#endif // ECMASCRIPT_BASE_CYCLIC_BUFFER_H \ No newline at end of file diff --git a/ecmascript/mem/allocator-inl.h b/ecmascript/mem/allocator-inl.h index 167dbf36cd..722bda7b1b 100644 --- a/ecmascript/mem/allocator-inl.h +++ b/ecmascript/mem/allocator-inl.h @@ -110,10 +110,12 @@ uintptr_t FreeListAllocator::Allocate(size_t size) auto ret = bpAllocator_.Allocate(size); if (LIKELY(ret != 0)) { FreeObject::FillFreeObject(heap_->GetEcmaVM(), bpAllocator_.GetTop(), bpAllocator_.Available()); + allocationSizeAccumulator_ += size; return ret; } FreeObject *object = freeList_->Allocator(size); if (LIKELY(object != nullptr && !object->IsEmpty())) { + allocationSizeAccumulator_ += size; return Allocate(object, size); } @@ -122,6 +124,7 @@ uintptr_t FreeListAllocator::Allocate(size_t size) heap_->GetSweeper()->FillSweptRegion(type_); object = freeList_->Allocator(size); if (LIKELY(object != nullptr && !object->IsEmpty())) { + allocationSizeAccumulator_ += size; return Allocate(object, size); } @@ -129,6 +132,7 @@ uintptr_t FreeListAllocator::Allocate(size_t size) heap_->GetSweeper()->WaitingTaskFinish(type_); object = freeList_->Allocator(size); if (LIKELY(object != nullptr && !object->IsEmpty())) { + allocationSizeAccumulator_ += size; return Allocate(object, size); } } diff --git a/ecmascript/mem/allocator.h b/ecmascript/mem/allocator.h index e5b48e308d..fdbe0f882b 100644 --- a/ecmascript/mem/allocator.h +++ b/ecmascript/mem/allocator.h @@ -120,6 +120,11 @@ public: sweeping_ = sweeping; } + size_t GetAllocatedSize() const + { + return allocationSizeAccumulator_; + } + private: inline uintptr_t Allocate(FreeObject *object, size_t size); @@ -128,6 +133,7 @@ private: Heap *heap_{nullptr}; MemSpaceType type_ = OLD_SPACE; bool sweeping_ = false; + size_t allocationSizeAccumulator_ = 0; }; } // namespace panda::ecmascript diff --git a/ecmascript/mem/concurrent_marker.cpp b/ecmascript/mem/concurrent_marker.cpp index 053181afc6..df23eb0e47 100644 --- a/ecmascript/mem/concurrent_marker.cpp +++ b/ecmascript/mem/concurrent_marker.cpp @@ -37,11 +37,15 @@ ConcurrentMarker::ConcurrentMarker(Heap *heap) void ConcurrentMarker::ConcurrentMarking() { ECMA_GC_LOG() << "ConcurrentMarker: Concurrent Mark Begin"; + heap_->Prepare(); thread_->SetMarkStatus(MarkStatus::MARKING); // SelectCSet if (!heap_->IsOnlyMarkSemi()) { ECMA_GC_LOG() << "ConcurrentMarker: not only semi, need mark all"; // select CSet in HPPGC + heapObjectSize_ = heap_->GetHeapObjectSize(); + } else { + heapObjectSize_ = heap_->GetNewSpace()->GetHeapObjectSize(); } InitializeMarking(); Platform::GetCurrentPlatform()->PostTask(std::make_unique(heap_)); @@ -91,6 +95,7 @@ void ConcurrentMarker::Reset(bool isClearCSet) FinishPhase(); thread_->SetMarkStatus(MarkStatus::NOT_BEGIN_MARK); notifyMarkingFinished_ = false; + duration_ = 0.0; if (isClearCSet) { // Mix space gc clear cset when evacuation allocator finalize const_cast(heap_->GetOldSpace())->ClearRegionFromCSet(); @@ -126,9 +131,12 @@ void ConcurrentMarker::InitializeMarking() bool ConcurrentMarker::MarkerTask::Run(uint32_t threadId) { + MemController* controller = heap_->GetMemController(); + double startTimeInMs = controller->GetSystemTimeInMs(); heap_->GetNonMovableMarker()->ProcessMarkStack(threadId); heap_->WaitRunningTaskFinished(); heap_->GetConcurrentMarker()->MarkingFinished(); + heap_->GetConcurrentMarker()->SetDuration(controller->GetSystemTimeInMs() - startTimeInMs); return true; } diff --git a/ecmascript/mem/concurrent_marker.h b/ecmascript/mem/concurrent_marker.h index 313a8b5fc5..2492712d1e 100644 --- a/ecmascript/mem/concurrent_marker.h +++ b/ecmascript/mem/concurrent_marker.h @@ -42,6 +42,16 @@ public: void WaitConcurrentMarkingFinished(); // call in main thread void Reset(bool isClearCSet = true); + double GetDuration() const + { + return duration_; + } + + double GetHeapObjectSize() const + { + return heapObjectSize_; + } + private: NO_COPY_SEMANTIC(ConcurrentMarker); NO_MOVE_SEMANTIC(ConcurrentMarker); @@ -59,6 +69,11 @@ private: Heap *heap_ {nullptr}; }; + void SetDuration(double duration) + { + duration_ = duration; + } + void InitializeMarking(); void MarkingFinished(); @@ -68,6 +83,8 @@ private: // obtain from heap WorkerHelper *workList_ {nullptr}; + size_t heapObjectSize_ {0}; + double duration_ {0.0}; bool notifyMarkingFinished_ {false}; // notify js-thread that marking is finished and need sweep bool vmThreadWaitMarkingFinished_ {false}; // jsMainThread waiting for concurrentGC FINISHED @@ -75,4 +92,4 @@ private: os::memory::ConditionVariable waitMarkingFinishedCV_; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_MEM_CONCURRENT_MARKER_H +#endif // ECMASCRIPT_MEM_CONCURRENT_MARKER_H \ No newline at end of file diff --git a/ecmascript/mem/ecma_heap_manager-inl.h b/ecmascript/mem/ecma_heap_manager-inl.h index bb8cdfeaf7..84ae1b7772 100644 --- a/ecmascript/mem/ecma_heap_manager-inl.h +++ b/ecmascript/mem/ecma_heap_manager-inl.h @@ -53,7 +53,10 @@ TaggedObject *EcmaHeapManager::AllocateYoungGenerationOrHugeObject(JSHClass *hcl } object = reinterpret_cast(newSpaceAllocator_.Allocate(size)); if (UNLIKELY(object == nullptr)) { - heap_->CollectGarbage(TriggerGCType::SEMI_GC); + if (!heap_->FillNewSpaceAndTryGC(&newSpaceAllocator_)) { + LOG_ECMA_MEM(FATAL) << "OOM : extend failed" << __LINE__; + UNREACHABLE(); + } object = reinterpret_cast(newSpaceAllocator_.Allocate(size)); if (UNLIKELY(object == nullptr)) { heap_->CollectGarbage(TriggerGCType::OLD_GC); diff --git a/ecmascript/mem/heap-inl.h b/ecmascript/mem/heap-inl.h index 33fb608e4d..72ee1f6e98 100644 --- a/ecmascript/mem/heap-inl.h +++ b/ecmascript/mem/heap-inl.h @@ -82,6 +82,16 @@ bool Heap::FillNewSpaceAndTryGC(BumpPointerAllocator *spaceAllocator, bool allow spaceAllocator->Reset(toSpace_); TryTriggerConcurrentMarking(allowGc); return true; + } else if (toSpace_->GetCommittedSize() == SEMI_SPACE_SIZE_CAPACITY + && !GetEcmaVM()->GetAssociatedJSThread()->IsNotBeginMark()) { + size_t maxCapacity = (SEMI_SPACE_SIZE_CAPACITY + SEMI_SPACE_OVERSHOOT_SIZE) + > MAX_SEMI_SPACE_SIZE_STARTUP ? MAX_SEMI_SPACE_SIZE_STARTUP + : (SEMI_SPACE_SIZE_CAPACITY + SEMI_SPACE_OVERSHOOT_SIZE); + toSpace_->SetMaximumCapacity(maxCapacity); + if (toSpace_->Expand(spaceAllocator->GetTop())) { + spaceAllocator->Reset(toSpace_); + return true; + } } if (allowGc) { CollectGarbage(TriggerGCType::SEMI_GC); @@ -237,6 +247,20 @@ void Heap::ClearSlotsRange(Region *current, uintptr_t freeStart, uintptr_t freeE set->ClearRange(freeStart, freeEnd); } } + +size_t Heap::GetCommittedSize() const +{ + size_t result = toSpace_->GetCommittedSize() + oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize() + + nonMovableSpace_->GetCommittedSize() + machineCodeSpace_->GetCommittedSize(); + return result; +} + +size_t Heap::GetHeapObjectSize() const +{ + size_t result = toSpace_->GetHeapObjectSize() + oldSpace_->GetHeapObjectSize() + + nonMovableSpace_->GetHeapObjectSize() + machineCodeSpace_->GetCommittedSize(); + return result; +} } // namespace panda::ecmascript #endif // ECMASCRIPT_MEM_HEAP_INL_H diff --git a/ecmascript/mem/heap.cpp b/ecmascript/mem/heap.cpp index 36bfb91de6..3c98b0dad4 100644 --- a/ecmascript/mem/heap.cpp +++ b/ecmascript/mem/heap.cpp @@ -39,7 +39,7 @@ Heap::Heap(EcmaVM *ecmaVm) : ecmaVm_(ecmaVm), regionFactory_(ecmaVm->GetRegionFa void Heap::Initialize() { - memController_ = CreateMemController("no-gc-for-start-up"); + memController_ = CreateMemController(this, "no-gc-for-start-up"); if (memController_->IsInAppStartup()) { toSpace_ = new SemiSpace(this, DEFAULT_SEMI_SPACE_SIZE, MAX_SEMI_SPACE_SIZE_STARTUP); @@ -182,45 +182,57 @@ void Heap::CollectGarbage(TriggerGCType gcType) # if ECMASCRIPT_SWITCH_GC_MODE_TO_COMPRESS_GC gcType = TriggerGCType::COMPRESS_FULL_GC; #endif + + bool isInAppStartUp = memController_->IsInAppStartup(); + if (isCompressGCRequested_ && GetEcmaVM()->GetAssociatedJSThread()->IsNotBeginMark() + && gcType != TriggerGCType::COMPRESS_FULL_GC && !isInAppStartUp) { + gcType = TriggerGCType::COMPRESS_FULL_GC; + isCompressGCRequested_ = false; + } + memController_->StartCalculationBeforeGC(); + ECMA_GC_LOG() << "isOnlySemi_" <IsInAppStartup()) { + if (isInAppStartUp) { SetFromSpaceMaximumCapacity(SEMI_SPACE_SIZE_CAPACITY); SetNewSpaceMaximumCapacity(SEMI_SPACE_SIZE_CAPACITY); compressCollector_->RunPhases(); ResetAppStartup(); } else { - if (!CheckAndTriggerCompressGC()) { - mixSpaceCollector_->RunPhases(); - if (!sweeper_->IsConcurrentSweepEnabled()) { - RecomputeLimits(); - } - } + mixSpaceCollector_->RunPhases(); } break; case TriggerGCType::OLD_GC: mixSpaceCollector_->RunPhases(); - if (!sweeper_->IsConcurrentSweepEnabled()) { - RecomputeLimits(); - } break; case TriggerGCType::NON_MOVE_GC: case TriggerGCType::HUGE_GC: case TriggerGCType::MACHINE_CODE_GC: mixSpaceCollector_->RunPhases(); - if (!sweeper_->IsConcurrentSweepEnabled()) { - RecomputeLimits(); - } break; case TriggerGCType::COMPRESS_FULL_GC: compressCollector_->RunPhases(); - RecomputeLimits(); break; default: UNREACHABLE(); break; } + if (gcType == TriggerGCType::COMPRESS_FULL_GC || + (gcType != TriggerGCType::SEMI_GC && !isOnlySemi_ &&!sweeper_->IsConcurrentSweepEnabled())) { + RecomputeLimits(); + } + memController_->StopCalculationAfterGC(gcType); + + if (toSpace_->GetMaximumCapacity() != SEMI_SPACE_SIZE_CAPACITY || + toSpace_->GetCommittedSize() > SEMI_SPACE_SIZE_CAPACITY * 4 / 3) { + size_t capacity = std::max(toSpace_->GetCommittedSize() * 3 / 4, SEMI_SPACE_SIZE_CAPACITY); + capacity = std::min(capacity, MAX_SEMI_SPACE_SIZE_STARTUP); + toSpace_->SetMaximumCapacity(capacity); + } + # if ECMASCRIPT_ENABLE_GC_LOG ecmaVm_->GetEcmaGCStats()->PrintStatisticResult(); #endif @@ -270,12 +282,17 @@ size_t Heap::VerifyHeapObjects() const void Heap::RecomputeLimits() { - size_t oldSpaceSize = oldSpace_->GetHeapObjectSize() + hugeObjectSpace_->GetCommittedSize(); - size_t newSpaceCapacity = toSpace_->GetMaximumCapacity(); + double gcSpeed = memController_->CalculateMarkCompactSpeedPerMS(); + double mutatorSpeed = memController_->GetCurrentOldSpaceAllocationThroughtputPerMS(); + size_t oldSpaceSize = oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize(); + size_t newSpaceCapacity = toSpace_->GetCommittedSize(); - constexpr double GrowingFactor = 1.1; + double growingFactor = memController_->CalculateGrowingFactor(gcSpeed, mutatorSpeed); auto newOldSpaceLimit = memController_->CalculateAllocLimit(oldSpaceSize, DEFAULT_OLD_SPACE_SIZE, - MAX_OLD_SPACE_SIZE, newSpaceCapacity, GrowingFactor); + MAX_OLD_SPACE_SIZE, newSpaceCapacity, growingFactor); + auto newGlobalSpaceLimit = memController_->CalculateAllocLimit(GetCommittedSize(), DEFAULT_HEAP_SIZE, + MAX_HEAP_SIZE, newSpaceCapacity, growingFactor); + globalSpaceAllocLimit_ = newGlobalSpaceLimit; oldSpaceAllocLimit_ = newOldSpaceLimit; } @@ -287,6 +304,7 @@ bool Heap::CheckConcurrentMark(JSThread *thread) WaitConcurrentMarkingFinished(); ECMA_GC_LOG() << "wait concurrent marking finish pause time " << clockScope.TotalSpentTime(); } + memController_->RecordAfterConcurrentMark(isOnlySemi_, concurrentMarker_); return true; } return false; @@ -294,17 +312,77 @@ bool Heap::CheckConcurrentMark(JSThread *thread) void Heap::TryTriggerConcurrentMarking(bool allowGc) { - if (allowGc && - toSpace_->GetCommittedSize() > SEMI_SPACE_TRIGGER_CONCURRENT_MARK && - GetEcmaVM()->GetJSThread()->IsNotBeginMark() && - !GetMemController()->IsInAppStartup()) { + if (!concurrentMarkingEnable_ || !allowGc || !GetEcmaVM()->GetAssociatedJSThread()->IsNotBeginMark() + || GetMemController()->IsInAppStartup()) { + return; + } + bool isFullMarkNeeded = false; + double oldSpaceMarkDuration = 0; + double newSpaceMarkDuration = 0; + double newSpaceRemainSize = 0; + double newSpaceAllocDurationToLimit = 0; + double oldSpaceAllocDurationToLimit = 0; + double oldSpaceAllocSpeed = memController_->GetOldSpaceAllocationThroughtPerMS(); + double oldSpaceConcurrentMarkSpeed = memController_->GetFullSpaceConcurrentMarkSpeedPerMS(); + size_t oldSpaceCommittedSize = oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize(); + size_t globalSpaceCommittedSize = GetCommittedSize(); + if (oldSpaceConcurrentMarkSpeed == 0 || oldSpaceAllocSpeed == 0) { + if (oldSpaceCommittedSize >= OLD_SPACE_LIMIT_BEGIN) { + isOnlySemi_ = false; + ECMA_GC_LOG() << "Trigger the first full mark"; + TriggerConcurrentMarking(); + } + } else { + if (oldSpaceCommittedSize >= oldSpaceAllocLimit_ * 0.8 || + globalSpaceCommittedSize >= globalSpaceAllocLimit_ * 0.8) { + isFullMarkNeeded = true; + } + oldSpaceAllocDurationToLimit = (oldSpaceAllocLimit_- oldSpaceCommittedSize) / oldSpaceAllocSpeed; + oldSpaceMarkDuration = GetHeapObjectSize() / oldSpaceConcurrentMarkSpeed; + double oldSpaceRemainSize = (oldSpaceAllocDurationToLimit - oldSpaceMarkDuration) * oldSpaceAllocSpeed; + if (oldSpaceRemainSize > 0 && oldSpaceRemainSize < DEFAULT_REGION_SIZE) { + isFullMarkNeeded = true; + } + } + + double newSpaceAllocSpeed = memController_->GetNewSpaceAllocationThroughtPerMS(); + double newSpaceConcurrentMarkSpeed = memController_->GetNewSpaceConcurrentMarkSpeedPerMS(); + + if (newSpaceConcurrentMarkSpeed == 0 || newSpaceAllocSpeed == 0) { + if (toSpace_->GetCommittedSize() >= SEMI_SPACE_TRIGGER_CONCURRENT_MARK) { + isOnlySemi_ = true; + TriggerConcurrentMarking(); + ECMA_GC_LOG() << "Trigger the first semi mark"; + } + return; + } + newSpaceAllocDurationToLimit = (toSpace_->GetMaximumCapacity() - toSpace_->GetCommittedSize()) + / newSpaceAllocSpeed; + newSpaceMarkDuration = toSpace_->GetHeapObjectSize() / newSpaceConcurrentMarkSpeed; + newSpaceRemainSize = (newSpaceAllocDurationToLimit - newSpaceMarkDuration) * newSpaceAllocSpeed; + + if (isFullMarkNeeded) { + if (oldSpaceMarkDuration < newSpaceAllocDurationToLimit + && oldSpaceMarkDuration < oldSpaceAllocDurationToLimit) { + isOnlySemi_ = false; + TriggerConcurrentMarking(); + ECMA_GC_LOG() << "Trigger full mark"; + } else { + if (oldSpaceCommittedSize >= oldSpaceAllocLimit_ || globalSpaceCommittedSize >= globalSpaceAllocLimit_) { + isCompressGCRequested_ = true; + ECMA_GC_LOG() << "Request compress GC"; + } + } + } else if (newSpaceRemainSize < DEFAULT_REGION_SIZE) { + isOnlySemi_ = true; TriggerConcurrentMarking(); + ECMA_GC_LOG() << "Trigger semi mark"; } } void Heap::TriggerConcurrentMarking() { - if (concurrentMarkingEnable_) { + if (concurrentMarkingEnable_ && !isCompressGCRequested_) { concurrentMarker_->ConcurrentMarking(); } } @@ -327,7 +405,7 @@ bool Heap::CheckAndTriggerOldGC() bool Heap::CheckAndTriggerCompressGC() { - if ((oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize()) <= oldSpaceAllocLimit_) { + if (GetCommittedSize() <= globalSpaceAllocLimit_) { return false; } CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); diff --git a/ecmascript/mem/heap.h b/ecmascript/mem/heap.h index 3abe50da97..588398d8bc 100644 --- a/ecmascript/mem/heap.h +++ b/ecmascript/mem/heap.h @@ -278,7 +278,7 @@ public: void RecomputeLimits(); - const MemController *GetMemController() const + MemController *GetMemController() const { return memController_; } @@ -288,6 +288,11 @@ public: oldSpaceAllocLimit_ = limit; } + void SetGlobalSpaceAllocLimit(size_t limit) + { + globalSpaceAllocLimit_ = limit; + } + inline void ResetAppStartup(); size_t VerifyHeapObjects() const; @@ -350,6 +355,10 @@ public: return compressGcMarker_; } + inline size_t GetCommittedSize() const; + + inline size_t GetHeapObjectSize() const; + private: void IncreaseTaskCount(); @@ -392,6 +401,7 @@ private: HeapTracker *tracker_ {nullptr}; MemController *memController_ {nullptr}; size_t oldSpaceAllocLimit_ {OLD_SPACE_LIMIT_BEGIN}; + size_t globalSpaceAllocLimit_ {GLOBAL_SPACE_LIMIT_BEGIN}; ChunkVector *derivedPointers_ {nullptr}; #if ECMASCRIPT_ENABLE_HEAP_VERIFY bool isVerifying_ {false}; @@ -406,6 +416,7 @@ private: bool concurrentMarkingEnable_ {true}; bool isOnlySemi_ {true}; + bool isCompressGCRequested_ {false}; inline void SetMaximumCapacity(SemiSpace *space, size_t maximumCapacity); }; } // namespace panda::ecmascript diff --git a/ecmascript/mem/mem.h b/ecmascript/mem/mem.h index 2c5ab0ad1c..e245174978 100644 --- a/ecmascript/mem/mem.h +++ b/ecmascript/mem/mem.h @@ -39,13 +39,16 @@ static constexpr size_t MIN_AllOC_LIMIT_GROWING_STEP = 2 * 1024 * 1024; static constexpr size_t SEMI_SPACE_SIZE_CAPACITY = 3 * 1024 * 1024; static constexpr size_t MAX_SEMI_SPACE_SIZE_STARTUP = 3 * 1024 * 1024; static constexpr size_t SEMI_SPACE_TRIGGER_CONCURRENT_MARK = 2.5 * 1024 * 1024; +static constexpr size_t SEMI_SPACE_OVERSHOOT_SIZE = 2 * 1024 * 1024; #else static constexpr size_t SEMI_SPACE_SIZE_CAPACITY = 4 * 1024 * 1024; static constexpr size_t MAX_SEMI_SPACE_SIZE_STARTUP = 16 * 1024 * 1024; static constexpr size_t SEMI_SPACE_TRIGGER_CONCURRENT_MARK = 3.5 * 1024 * 1024; +static constexpr size_t SEMI_SPACE_OVERSHOOT_SIZE = 2 * 1024 * 1024; #endif static constexpr size_t OLD_SPACE_LIMIT_BEGIN = 10 * 1024 * 1024; +static constexpr size_t GLOBAL_SPACE_LIMIT_BEGIN = 20 * 1024 * 1024; static constexpr size_t DEFAULT_OLD_SPACE_SIZE = 2 * 1024 * 1024; static constexpr size_t MAX_OLD_SPACE_SIZE = 256 * 1024 * 1024; @@ -63,6 +66,7 @@ static constexpr size_t DEFAULT_MACHINE_CODE_SPACE_LIMIT = 1024 * 1024; static constexpr size_t MAX_MACHINE_CODE_SPACE_SIZE = 8 * 1024 * 1024; static constexpr size_t MAX_HEAP_SIZE = 256 * 1024 * 1024; +static constexpr size_t DEFAULT_HEAP_SIZE = 5 * 1024 * 1024; static constexpr size_t DEFAULT_REGION_SIZE = 1U << REGION_SIZE_LOG2; static constexpr size_t DEFAULT_REGION_MASK = DEFAULT_REGION_SIZE - 1; @@ -74,7 +78,7 @@ static constexpr size_t DEFAULT_MARK_STACK_SIZE = 4 * 1024; // They will never be moved to huge object space. So we take half of a regular // region as the border of regular objects. static constexpr size_t MAX_32BIT_OBJECT_SPACE_SIZE = 1 * 1024 * 1024 * 1024; -static constexpr size_t MAX_REGULAR_HEAP_OBJECT_SIZE = 1U << (REGION_SIZE_LOG2 - 1); +static constexpr size_t MAX_REGULAR_HEAP_OBJECT_SIZE = DEFAULT_MACHINE_CODE_SPACE_SIZE * 2 / 3; static constexpr size_t MAX_HUGE_OBJECT_SIZE = 256 * 1024 * 1024; static constexpr size_t MAX_HUGE_OBJECT_SPACE_SIZE = 256 * 1024 * 1024; static constexpr size_t LARGE_BITMAP_MIN_SIZE = static_cast(MemAlignment::MEM_ALIGN_OBJECT) diff --git a/ecmascript/mem/mem_controller.cpp b/ecmascript/mem/mem_controller.cpp index c04d6077ca..e5e0dcf79c 100644 --- a/ecmascript/mem/mem_controller.cpp +++ b/ecmascript/mem/mem_controller.cpp @@ -13,17 +13,22 @@ * limitations under the License. */ -#include "mem_controller.h" +#include "ecmascript/mem/mem_controller.h" + +#include "ecmascript/mem/concurrent_marker.h" +#include "ecmascript/mem/ecma_heap_manager.h" +#include "ecmascript/mem/heap-inl.h" +#include "ecmascript/mem/parallel_evacuation.h" namespace panda::ecmascript { -MemController::MemController(bool isInAppStartup) : isInAppStartup_(isInAppStartup) {} +MemController::MemController(Heap *heap, bool isInAppStartup) : heap_(heap), isInAppStartup_(isInAppStartup) {} double MemController::CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity, double factor) const { const uint64_t limit = std::max(static_cast(currentSize * factor), static_cast(currentSize) + MIN_AllOC_LIMIT_GROWING_STEP) + - SEMI_SPACE_SIZE_CAPACITY; + newSpaceCapacity; const uint64_t limitAboveMinSize = std::max(limit, minSize); const uint64_t halfToMaxSize = (static_cast(currentSize) + maxSize) / 2; @@ -31,21 +36,214 @@ double MemController::CalculateAllocLimit(size_t currentSize, size_t minSize, si return result; } -MemController *CreateMemController(std::string_view gcTriggerType) +double MemController::CalculateGrowingFactor(double gcSpeed, double mutatorSpeed) +{ + static constexpr double minGrowingFactor = 1.3; + static constexpr double maxGrowingFactor = 2.0; + static constexpr double targetMutatorUtilization = 0.97; + if (gcSpeed == 0 || mutatorSpeed == 0) { + return maxGrowingFactor; + } + + const double speedRatio = gcSpeed / mutatorSpeed; + + const double a = speedRatio * (1 - targetMutatorUtilization); + const double b = speedRatio * (1 - targetMutatorUtilization) - targetMutatorUtilization; + + double factor = (a < b * maxGrowingFactor) ? a / b : maxGrowingFactor; + factor = std::min(maxGrowingFactor, factor); + factor = std::max(factor, minGrowingFactor); + return factor; +} + +void MemController::StartCalculationBeforeGC() +{ + startCounter_++; + if (startCounter_ != 1) { + return; + } + + auto heapManager = heap_->GetHeapManager(); + size_t newSpaceAllocBytesSinceGC = heap_->GetNewSpace()->GetAllocatedSizeSinceGC(); + size_t oldSpaceAllocAccumulatorSize = heapManager->GetOldSpaceAllocator().GetAllocatedSize() + + heap_->GetHugeObjectSpace()->GetAllocatedSizeSinceGC() + + heap_->GetEvacuation()->GetPromotedAccumulatorSize(); + size_t nonMovableSpaceAllocAccumulatorSize = heapManager->GetNonMovableSpaceAllocator().GetAllocatedSize(); + size_t codeSpaceAllocAccumulatorSize = heapManager->GetMachineCodeSpaceAllocator().GetAllocatedSize(); + if (allocTimeMs_ == 0) { + allocTimeMs_ = GetSystemTimeInMs(); + newSpaceAllocAccumulatorSize_ = newSpaceAllocBytesSinceGC; + oldSpaceAllocAccumulatorSize_ = oldSpaceAllocAccumulatorSize; + nonMovableSpaceAllocAccumulatorSize_ = nonMovableSpaceAllocAccumulatorSize; + codeSpaceAllocAccumulatorSize_ = codeSpaceAllocAccumulatorSize; + return; + } + double currentTimeInMs = GetSystemTimeInMs(); + gcStartTime_ = currentTimeInMs; + size_t newSpaceAllocSize = heap_->GetNewSpace()->GetAllocatedSizeSinceGC(); + size_t oldSpaceAllocSize = oldSpaceAllocAccumulatorSize - oldSpaceAllocAccumulatorSize_; + size_t nonMovableSpaceAllocSize = nonMovableSpaceAllocAccumulatorSize - nonMovableSpaceAllocAccumulatorSize_; + size_t codeSpaceAllocSize = codeSpaceAllocAccumulatorSize - codeSpaceAllocAccumulatorSize_; + + double duration = currentTimeInMs - allocTimeMs_; + allocTimeMs_ = currentTimeInMs; + newSpaceAllocAccumulatorSize_ += newSpaceAllocSize; + oldSpaceAllocAccumulatorSize_ = oldSpaceAllocAccumulatorSize; + nonMovableSpaceAllocAccumulatorSize_ = nonMovableSpaceAllocAccumulatorSize; + codeSpaceAllocAccumulatorSize_ = codeSpaceAllocAccumulatorSize; + allocDurationSinceGc_ += duration; + newSpaceAllocSizeSinceGC_ += newSpaceAllocSize; + oldSpaceAllocSizeSinceGC_ += oldSpaceAllocSize; + nonMovableSpaceAllocSizeSinceGC_ += nonMovableSpaceAllocSize; + codeSpaceAllocSizeSinceGC_ += codeSpaceAllocSize; +} + +void MemController::StopCalculationAfterGC(TriggerGCType gcType) +{ + startCounter_--; + if (startCounter_ != 0) { + return; + } + + gcEndTime_ = GetSystemTimeInMs(); + allocTimeMs_ = gcEndTime_; + if (allocDurationSinceGc_ > 0) { + recordedNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); + recordedOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); + recordedNonmovableSpaceAllocations_.Push( + MakeBytesAndDuration(nonMovableSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); + recordedCodeSpaceAllocations_.Push(MakeBytesAndDuration(codeSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); + } + allocDurationSinceGc_ = 0.0; + newSpaceAllocSizeSinceGC_ = 0; + oldSpaceAllocSizeSinceGC_ = 0; + nonMovableSpaceAllocSizeSinceGC_ = 0; + codeSpaceAllocSizeSinceGC_ = 0; + + double duration = gcEndTime_ - gcStartTime_; + + switch (gcType) { + case TriggerGCType::SEMI_GC: + break; + case TriggerGCType::NON_MOVE_GC: + case TriggerGCType::HUGE_GC: + case TriggerGCType::MACHINE_CODE_GC: + break; + case TriggerGCType::OLD_GC: + case TriggerGCType::COMPRESS_FULL_GC: { + size_t heapObjectSize = heap_->GetHeapObjectSize(); + recordedMarkCompacts_.Push(MakeBytesAndDuration(heapObjectSize, duration)); + break; + } + default: + UNREACHABLE(); + break; + } +} + +void MemController::RecordAfterConcurrentMark(const bool isSemi, const ConcurrentMarker *marker) +{ + double duration = marker->GetDuration(); + if (isSemi) { + recordedSemiConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration)); + } else { + recordedConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration)); + } +} + +double MemController::CalculateMarkCompactSpeedPerMS() +{ + if (markCompactSpeedCache_ > 0) { + return markCompactSpeedCache_; + } + markCompactSpeedCache_ = CalculateAverageSpeed(recordedMarkCompacts_); + if (markCompactSpeedCache_ > 0) { + return markCompactSpeedCache_; + } + return 0; +} + +double MemController::CalculateAverageSpeed(const base::CyclicBuffer &buffer, + const BytesAndDuration &initial, const double timeMs) +{ + BytesAndDuration sum = buffer.Sum( + [timeMs](BytesAndDuration a, BytesAndDuration b) { + if (timeMs != 0 && a.second >= timeMs) { + return a; + } + return std::make_pair(a.first + b.first, a.second + b.second); + }, + initial); + uint64_t bytes = sum.first; + double durations = sum.second; + if (durations == 0.0) { + return 0; + } + double speed = bytes / durations; + const int maxSpeed = 1024 * 1024 * 1024; + const int minSpeed = 1; + if (speed >= maxSpeed) { + return maxSpeed; + } + if (speed <= minSpeed) { + return minSpeed; + } + return speed; +} + +double MemController::CalculateAverageSpeed(const base::CyclicBuffer &buffer) +{ + return CalculateAverageSpeed(buffer, MakeBytesAndDuration(0, 0), 0); +} + +double MemController::GetCurrentOldSpaceAllocationThroughtputPerMS(double timeMs) const +{ + size_t allocatedSize = oldSpaceAllocSizeSinceGC_; + double duration = allocDurationSinceGc_; + return CalculateAverageSpeed(recordedOldSpaceAllocations_, + MakeBytesAndDuration(allocatedSize, duration), timeMs); +} + +double MemController::GetNewSpaceAllocationThroughtPerMS() const +{ + return CalculateAverageSpeed(recordedNewSpaceAllocations_); +} + +double MemController::GetNewSpaceConcurrentMarkSpeedPerMS() const +{ + return CalculateAverageSpeed(recordedSemiConcurrentMarks_); +} + +double MemController::GetOldSpaceAllocationThroughtPerMS() const +{ + return CalculateAverageSpeed(recordedOldSpaceAllocations_); +} + +double MemController::GetFullSpaceConcurrentMarkSpeedPerMS() const +{ + return CalculateAverageSpeed(recordedConcurrentMarks_); +} + +MemController *CreateMemController(Heap *heap, std::string_view gcTriggerType) { auto triggerType = GCTriggerType::INVALID_TRIGGER; if (gcTriggerType == "no-gc-for-start-up") { triggerType = GCTriggerType::NO_GC_FOR_STARTUP; } else if (gcTriggerType == "heap-trigger") { triggerType = GCTriggerType::HEAP_TRIGGER; + } else if (gcTriggerType == "allocation-speed") { + triggerType = GCTriggerType::ALLOCATION_SPEED; } MemController *ret{nullptr}; switch (triggerType) { // NOLINT(hicpp-multiway-paths-covered) case GCTriggerType::NO_GC_FOR_STARTUP: - ret = new MemController(true); + ret = new MemController(heap, true); break; case GCTriggerType::HEAP_TRIGGER: - ret = new MemController(false); + ret = new MemController(heap, false); + break; + case GCTriggerType::ALLOCATION_SPEED: + ret = new MemController(heap, true); break; default: break; diff --git a/ecmascript/mem/mem_controller.h b/ecmascript/mem/mem_controller.h index f67ed204d1..2d5276be1d 100644 --- a/ecmascript/mem/mem_controller.h +++ b/ecmascript/mem/mem_controller.h @@ -16,6 +16,10 @@ #ifndef ECMASCRIPT_MEM_MEM_CONTROLLER_H #define ECMASCRIPT_MEM_MEM_CONTROLLER_H +#include + +#include "ecmascript/base/cyclic_buffer.h" +#include "ecmascript/mem/heap.h" #include "ecmascript/mem/mem.h" namespace panda::ecmascript { @@ -25,6 +29,7 @@ enum class GCTriggerType { INVALID_TRIGGER, NO_GC_FOR_STARTUP, // guarantee no gc for app startup HEAP_TRIGGER, + ALLOCATION_SPEED, }; using BytesAndDuration = std::pair; @@ -36,15 +41,24 @@ inline BytesAndDuration MakeBytesAndDuration(uint64_t bytes, double duration) class MemController { public: - explicit MemController(bool isInAppStartUp = false); + explicit MemController(Heap* heap, bool isInAppStartUp = false); MemController() = default; ~MemController() = default; NO_COPY_SEMANTIC(MemController); NO_MOVE_SEMANTIC(MemController); + static double GetSystemTimeInMs() + { + double currentTime = + std::chrono::duration(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + return currentTime * MILLISECOND_PER_SECOND; + } + double CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity, double factor) const; + double CalculateGrowingFactor(double gcSpeed, double mutatorSpeed); + inline bool IsInAppStartup() const { return isInAppStartup_; @@ -55,10 +69,110 @@ public: isInAppStartup_ = false; } + void StartCalculationBeforeGC(); + void StopCalculationAfterGC(TriggerGCType gcType); + + void RecordAfterConcurrentMark(const bool isSemi, const ConcurrentMarker *marker); + + double CalculateMarkCompactSpeedPerMS(); + double GetCurrentOldSpaceAllocationThroughtputPerMS(double timeMs = THROUGHPUT_TIME_FRAME_MS) const; + double GetNewSpaceAllocationThroughtPerMS() const; + double GetOldSpaceAllocationThroughtPerMS() const; + double GetNewSpaceConcurrentMarkSpeedPerMS() const; + double GetFullSpaceConcurrentMarkSpeedPerMS() const; + + double GetAllocTimeMs() const + { + return allocTimeMs_; + } + + size_t GetNewSpaceAllocAccumulatorSize() const + { + return newSpaceAllocAccumulatorSize_; + } + + size_t GetOldSpaceAllocAccumulatorSize() const + { + return oldSpaceAllocAccumulatorSize_; + } + + size_t GetNonMovableSpaceAllocAccumulatorSize() const + { + return nonMovableSpaceAllocAccumulatorSize_; + } + + size_t GetCodeSpaceAllocAccumulatorSize() const + { + return codeSpaceAllocAccumulatorSize_; + } + + double GetAllocDurationSinceGc() const + { + return allocDurationSinceGc_; + } + + size_t GetNewSpaceAllocSizeSinceGC() const + { + return newSpaceAllocSizeSinceGC_; + } + + size_t GetOldSpaceAllocSizeSinceGC() const + { + return oldSpaceAllocSizeSinceGC_; + } + + size_t GetNonMovableSpaceAllocSizeSinceGC() const + { + return nonMovableSpaceAllocSizeSinceGC_; + } + + size_t GetCodeSpaceAllocSizeSinceGC() const + { + return codeSpaceAllocSizeSinceGC_; + } + private: - bool isInAppStartup_{false}; + static double CalculateAverageSpeed(const base::CyclicBuffer &buffer); + static double CalculateAverageSpeed(const base::CyclicBuffer &buffer, + const BytesAndDuration &initial, const double timeMs); + + Heap* heap_; + + double gcStartTime_ {0.0}; + double gcEndTime_ {0.0}; + + // Time and allocation accumulators. + double allocTimeMs_ {0.0}; + size_t newSpaceAllocAccumulatorSize_ {0}; + size_t oldSpaceAllocAccumulatorSize_ {0}; + size_t nonMovableSpaceAllocAccumulatorSize_ {0}; + size_t codeSpaceAllocAccumulatorSize_ {0}; + + // Duration and allocation size in last gc. + double allocDurationSinceGc_ {0.0}; + size_t newSpaceAllocSizeSinceGC_ {0}; + size_t oldSpaceAllocSizeSinceGC_ {0}; + size_t nonMovableSpaceAllocSizeSinceGC_ {0}; + size_t codeSpaceAllocSizeSinceGC_ {0}; + + int startCounter_ {0}; + + double markCompactSpeedCache_ {0.0}; + + base::CyclicBuffer recordedMarkCompacts_; + base::CyclicBuffer recordedNewSpaceAllocations_; + base::CyclicBuffer recordedOldSpaceAllocations_; + base::CyclicBuffer recordedNonmovableSpaceAllocations_; + base::CyclicBuffer recordedCodeSpaceAllocations_; + + base::CyclicBuffer recordedConcurrentMarks_; + base::CyclicBuffer recordedSemiConcurrentMarks_; + bool isInAppStartup_ {false}; + + static constexpr double THROUGHPUT_TIME_FRAME_MS = 5000; + static constexpr int MILLISECOND_PER_SECOND = 1000; }; -MemController *CreateMemController(std::string_view gcTriggerType); +MemController *CreateMemController(Heap *heap, std::string_view gcTriggerType); } // namespace panda::ecmascript #endif // ECMASCRIPT_MEM_MEM_CONTROLLER_H diff --git a/ecmascript/mem/mix_space_collector.cpp b/ecmascript/mem/mix_space_collector.cpp index e9a47bd592..26c4fe66b9 100644 --- a/ecmascript/mem/mix_space_collector.cpp +++ b/ecmascript/mem/mix_space_collector.cpp @@ -40,6 +40,7 @@ void MixSpaceCollector::RunPhases() ClockScope clockScope; concurrentMark_ = heap_->CheckConcurrentMark(thread); + ECMA_GC_LOG() << "concurrentMark_" << concurrentMark_; // SelectCSet InitializePhase(); MarkingPhase(); diff --git a/ecmascript/mem/parallel_evacuation.cpp b/ecmascript/mem/parallel_evacuation.cpp index 41776e6ace..999737fb2f 100644 --- a/ecmascript/mem/parallel_evacuation.cpp +++ b/ecmascript/mem/parallel_evacuation.cpp @@ -90,10 +90,16 @@ void ParallelEvacuation::EvacuateRegion(TlabAllocator *allocator, Region *region if (IsPromoteComplete(region)) { bool ret = false; if (isPromoted) { + if (region->InYoungGeneration()) { + promotedAccumulatorSize_.fetch_add(region->Available()); + } ret = evacuationAllocator_->AddRegionToOld(region); } else { ret = evacuationAllocator_->AddRegionToYoung(region); if (!ret) { + if (region->InYoungGeneration()) { + promotedAccumulatorSize_.fetch_add(region->Available()); + } ret = evacuationAllocator_->AddRegionToOld(region); } } @@ -112,10 +118,12 @@ void ParallelEvacuation::EvacuateRegion(TlabAllocator *allocator, Region *region uintptr_t address = 0; if (isPromoted || (region->HasAgeMark() && ToUintPtr(mem) < ageMark_)) { address = allocator->Allocate(size, SpaceAlloc::OLD_SPACE); + promotedAccumulatorSize_.fetch_add(size); } else { address = allocator->Allocate(size, SpaceAlloc::YOUNG_SPACE); if (address == 0) { address = allocator->Allocate(size, SpaceAlloc::OLD_SPACE); + promotedAccumulatorSize_.fetch_add(size); } } LOG_IF(address == 0, FATAL, RUNTIME) << "Evacuate object failed:" << size; diff --git a/ecmascript/mem/parallel_evacuation.h b/ecmascript/mem/parallel_evacuation.h index 5c0d7a9900..a8de56b2d9 100644 --- a/ecmascript/mem/parallel_evacuation.h +++ b/ecmascript/mem/parallel_evacuation.h @@ -39,6 +39,12 @@ public: void Initialize(); void Finalize(); void Evacuate(); + + size_t GetPromotedAccumulatorSize() const + { + return promotedAccumulatorSize_; + } + private: static constexpr double MIN_OBJECT_SURVIVAL_RATE = 0.8; @@ -169,6 +175,7 @@ private: std::vector> fragments_; std::vector sweptList_; std::atomic_int parallel_ = 0; + std::atomic promotedAccumulatorSize_ = 0; os::memory::Mutex mutex_; os::memory::ConditionVariable condition_; }; diff --git a/ecmascript/mem/space.cpp b/ecmascript/mem/space.cpp index 49fd0e0c66..9f2928239a 100644 --- a/ecmascript/mem/space.cpp +++ b/ecmascript/mem/space.cpp @@ -229,6 +229,33 @@ void SemiSpace::IterateOverObjects(const std::functionBelowAgeMark()) { + return 0; + } + auto top = GetHeap()->GetHeapManager()->GetNewSpaceAllocator().GetTop(); + size_t result = 0; + if (last->HasAgeMark()) { + result = last->GetAllocatedBytes(top) - last->GetAllocatedBytes(ageMark_); + return result; + } else { + result = last->GetAllocatedBytes(top); + } + EnumerateRegions([this, &result, &last](Region *current) { + if (!current->HasAgeMark() && !current->BelowAgeMark()) { + if (current != last) { + result += current->GetAllocatedBytes(); + } + } else if (current->HasAgeMark()) { + result += current->GetAllocatedBytes(); + result -= current->GetAllocatedBytes(this->GetAgeMark()); + } + }); + return result; +} + OldSpace::OldSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) : Space(heap, MemSpaceType::OLD_SPACE, initialCapacity, maximumCapacity) { @@ -545,6 +572,17 @@ void HugeObjectSpace::IterateOverObjects(const std::functionBelowAgeMark()) { + result += region->GetSize(); + } + }); + return result; +} + MachineCodeSpace::MachineCodeSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) : Space(heap, MemSpaceType::MACHINE_CODE_SPACE, initialCapacity, maximumCapacity) { @@ -567,4 +605,13 @@ bool MachineCodeSpace::Expand() LOG_ECMA_MEM(DEBUG) << "MachineCodeSpace::Expand() SetCodeExecutableAndReadable" << res; return true; } + +size_t MachineCodeSpace::GetHeapObjectSize() const +{ + size_t result; + size_t availableSize = GetHeap()->GetHeapManager()->GetMachineCodeSpaceAllocator().GetAvailableSize(); + size_t regionSize = GetRegionList().GetLength() * DEFAULT_REGION_SIZE; + result = regionSize - availableSize; + return result; +} } // namespace panda::ecmascript diff --git a/ecmascript/mem/space.h b/ecmascript/mem/space.h index 186244be44..1c232dcc1f 100644 --- a/ecmascript/mem/space.h +++ b/ecmascript/mem/space.h @@ -205,6 +205,8 @@ public: bool IsLive(TaggedObject *object) const; void IterateOverObjects(const std::function &objectVisitor) const; + size_t GetAllocatedSizeSinceGC() const; + private: uintptr_t ageMark_; }; @@ -274,6 +276,7 @@ public: bool ContainObject(TaggedObject *object) const; bool IsLive(TaggedObject *object) const; void IterateOverObjects(const std::function &objectVisitor) const; + size_t GetAllocatedSizeSinceGC() const; }; class MachineCodeSpace : public Space { @@ -284,6 +287,7 @@ public: NO_COPY_SEMANTIC(MachineCodeSpace); NO_MOVE_SEMANTIC(MachineCodeSpace); // Note: Expand(), ContainObject(), IsLive() left for define bool Expand(); + size_t GetHeapObjectSize() const; }; } // namespace panda::ecmascript diff --git a/ecmascript/tests/BUILD.gn b/ecmascript/tests/BUILD.gn index eb4e3953b0..6a190185cb 100644 --- a/ecmascript/tests/BUILD.gn +++ b/ecmascript/tests/BUILD.gn @@ -880,6 +880,34 @@ host_unittest_action("TaggedValueTest") { } } +host_unittest_action("MemControllerTest") { + module_out_path = module_output_path + + sources = [ + # test file + "mem_controller_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_common_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "//ark/js_runtime:libark_jsruntime_test", + sdk_libc_secshared_dep, + ] + + if (!is_standard_system) { + deps += [ "$ark_root/runtime:libarkruntime" ] + } +} + + host_unittest_action("WeakRefGenGcTest") { module_out_path = module_output_path diff --git a/ecmascript/tests/mem_controller_test.cpp b/ecmascript/tests/mem_controller_test.cpp new file mode 100644 index 0000000000..19087bd2b2 --- /dev/null +++ b/ecmascript/tests/mem_controller_test.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_thread.h" + +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/space.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class MemControllerTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(MemControllerTest, AllocationVerify) +{ +#if !defined(NDEBUG) + auto ecmaVm = thread->GetEcmaVM(); + auto heap = const_cast(ecmaVm->GetHeap()); + auto objectFactory = ecmaVm->GetFactory(); + auto memController = heap->GetMemController(); + + heap->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + + for (int i = 0; i < 1024; i++) { + // old space object + [[maybe_unused]] auto oldArray = objectFactory->NewTaggedArray(128, JSTaggedValue::Undefined(), + MemSpaceType::OLD_SPACE); + } + sleep(5); + heap->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + double mutatorSpeed1 = memController->GetCurrentOldSpaceAllocationThroughtputPerMS(0); + for (int i = 0; i < 1024; i++) { + // old space object + [[maybe_unused]] auto oldArray = objectFactory->NewTaggedArray(128, JSTaggedValue::Undefined(), + MemSpaceType::OLD_SPACE); + } + sleep(10); + + heap->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + double mutatorSpeed2 = memController->GetCurrentOldSpaceAllocationThroughtputPerMS(0); + ASSERT_TRUE(mutatorSpeed2 < mutatorSpeed1); +#endif +} + +HWTEST_F_L0(MemControllerTest, VerifyMutatorSpeed) +{ + auto ecmaVm = thread->GetEcmaVM(); + auto heap = const_cast(ecmaVm->GetHeap()); + auto objectFactory = ecmaVm->GetFactory(); + auto memController = heap->GetMemController(); + + heap->CollectGarbage(TriggerGCType::SEMI_GC); + size_t newSpaceAllocatedSizeBefore = memController->GetNewSpaceAllocAccumulatorSize(); + size_t oldSpaceAllocatedSizeBefore = memController->GetOldSpaceAllocAccumulatorSize(); + size_t nonMovableSpaceAllocatedSizeBefore = memController->GetNonMovableSpaceAllocAccumulatorSize(); + double allocDurationBefore = memController->GetAllocTimeMs(); + sleep(1); + + // new space object + auto newArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::SEMI_SPACE); + // old space object + auto oldArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + // non movable object + auto nonMovableArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::NON_MOVABLE); + + heap->CollectGarbage(TriggerGCType::SEMI_GC); + + size_t newSpaceAllocatedSizeAfter = memController->GetNewSpaceAllocAccumulatorSize(); + size_t oldSpaceAllocatedSizeAfter = memController->GetOldSpaceAllocAccumulatorSize(); + size_t nonMovableSpaceAllocatedSizeAfter = memController->GetNonMovableSpaceAllocAccumulatorSize(); + double allocDurationAfter = memController->GetAllocTimeMs(); + + ASSERT_TRUE(allocDurationAfter - allocDurationBefore > 1000); + ASSERT_TRUE(newSpaceAllocatedSizeAfter - newSpaceAllocatedSizeBefore == newArray->GetClass()->GetObjectSize()); + ASSERT_TRUE(oldSpaceAllocatedSizeAfter - oldSpaceAllocatedSizeBefore == oldArray->GetClass()->GetObjectSize()); + ASSERT_TRUE(nonMovableSpaceAllocatedSizeAfter - nonMovableSpaceAllocatedSizeBefore + == nonMovableArray->GetClass()->GetObjectSize()); +} +} // namespace panda::test diff --git a/test/resource/js_runtime/ohos_test.xml b/test/resource/js_runtime/ohos_test.xml index 7cdac7994d..1fa4041df3 100644 --- a/test/resource/js_runtime/ohos_test.xml +++ b/test/resource/js_runtime/ohos_test.xml @@ -169,6 +169,11 @@