From ae6a0b807574d4349be9c45428960ac7ecd3679a Mon Sep 17 00:00:00 2001 From: ulan Date: Thu, 7 May 2015 07:41:53 -0700 Subject: [PATCH] Add mode to reduce memory usage in idle notification. While the mutator is active, the idle time handler optimizes for latency by doing only incremental steps and scavenges. When the mutator becomes inactive, the idle time handler forces few incremental GCs to reclaim memory and then stops until mutator is active again. BUG=460090 LOG=N Review URL: https://codereview.chromium.org/1105293004 Cr-Commit-Position: refs/heads/master@{#28300} --- src/heap/gc-idle-time-handler.cc | 164 ++++++-- src/heap/gc-idle-time-handler.h | 98 +++-- src/heap/heap.cc | 11 +- test/cctest/test-heap.cc | 31 +- .../heap/gc-idle-time-handler-unittest.cc | 449 +++++++++++++-------- 5 files changed, 479 insertions(+), 274 deletions(-) diff --git a/src/heap/gc-idle-time-handler.cc b/src/heap/gc-idle-time-handler.cc index a3383b9..3e4df7d 100644 --- a/src/heap/gc-idle-time-handler.cc +++ b/src/heap/gc-idle-time-handler.cc @@ -12,8 +12,6 @@ namespace internal { const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9; const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000; const size_t GCIdleTimeHandler::kMaxFinalIncrementalMarkCompactTimeInMs = 1000; -const int GCIdleTimeHandler::kMaxMarkCompactsInIdleRound = 2; -const int GCIdleTimeHandler::kIdleScavengeThreshold = 5; const double GCIdleTimeHandler::kHighContextDisposalRate = 100; const size_t GCIdleTimeHandler::kMinTimeForOverApproximatingWeakClosureInMs = 1; @@ -187,13 +185,56 @@ bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure( } -GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() { - if (idle_times_which_made_no_progress_since_last_idle_round_ >= - kMaxNoProgressIdleTimesPerIdleRound) { +// The idle time handler has three modes and transitions between them +// as shown in the diagram: +// +// kReduceLatency -----> kReduceMemory -----> kDone +// ^ ^ | | +// | | | | +// | +------------------+ | +// | | +// +----------------------------------------+ +// +// In kReduceLatency mode the handler only starts incremental marking +// if can_start_incremental_marking is false. +// In kReduceMemory mode the handler can force a new GC cycle by starting +// incremental marking even if can_start_incremental_marking is false. It can +// cause at most X idle GCs. +// In kDone mode the idle time handler does nothing. +// +// The initial mode is kReduceLatency. +// +// kReduceLatency => kReduceMemory transition happens if there were Y +// consecutive long idle notifications without any mutator GC. This is our +// notion of "mutator is idle". +// +// kReduceMemory => kDone transition happens after X idle GCs. +// +// kReduceMemory => kReduceLatency transition happens if N mutator GCs +// were performed meaning that the mutator is active. +// +// kDone => kReduceLatency transition happens if there were M mutator GCs or +// context was disposed. +// +// X = kMaxIdleMarkCompacts +// Y = kLongIdleNotificationsBeforeMutatorIsIdle +// N = #(idle GCs) +// M = kGCsBeforeMutatorIsActive +GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, + HeapState heap_state) { + Mode next_mode = NextMode(heap_state); + + if (next_mode != mode_) { + mode_ = next_mode; + ResetCounters(); + } + + UpdateCounters(idle_time_in_ms); + + if (mode_ == kDone) { return GCIdleTimeAction::Done(); } else { - idle_times_which_made_no_progress_since_last_idle_round_++; - return GCIdleTimeAction::Nothing(); + return Action(idle_time_in_ms, heap_state, mode_ == kReduceMemory); } } @@ -204,28 +245,23 @@ GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() { // a full GC. // (2) If the new space is almost full and we can afford a Scavenge or if the // next Scavenge will very likely take long, then a Scavenge is performed. -// (3) If there is currently no MarkCompact idle round going on, we start a -// new idle round if enough garbage was created. Otherwise we do not perform -// garbage collection to keep system utilization low. -// (4) If incremental marking is done, we perform a full garbage collection +// (3) If incremental marking is done, we perform a full garbage collection // if we are allowed to still do full garbage collections during this idle // round or if we are not allowed to start incremental marking. Otherwise we // do not perform garbage collection to keep system utilization low. -// (5) If sweeping is in progress and we received a large enough idle time +// (4) If sweeping is in progress and we received a large enough idle time // request, we finalize sweeping here. -// (6) If incremental marking is in progress, we perform a marking step. Note, +// (5) If incremental marking is in progress, we perform a marking step. Note, // that this currently may trigger a full garbage collection. -GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, - HeapState heap_state) { +GCIdleTimeAction GCIdleTimeHandler::Action(double idle_time_in_ms, + const HeapState& heap_state, + bool reduce_memory) { if (static_cast(idle_time_in_ms) <= 0) { - if (heap_state.contexts_disposed > 0) { - StartIdleRound(); - } if (heap_state.incremental_marking_stopped) { if (ShouldDoContextDisposalMarkCompact( heap_state.contexts_disposed, heap_state.contexts_disposal_rate)) { - return GCIdleTimeAction::FullGC(); + return GCIdleTimeAction::FullGC(false); } } return GCIdleTimeAction::Nothing(); @@ -239,37 +275,99 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, return GCIdleTimeAction::Scavenge(); } - if (IsMarkCompactIdleRoundFinished()) { - if (EnoughGarbageSinceLastIdleRound()) { - StartIdleRound(); - } else { - return GCIdleTimeAction::Done(); - } - } - - if (heap_state.incremental_marking_stopped) { + if (heap_state.incremental_marking_stopped && reduce_memory) { if (ShouldDoMarkCompact(static_cast(idle_time_in_ms), heap_state.size_of_objects, heap_state.mark_compact_speed_in_bytes_per_ms)) { - return GCIdleTimeAction::FullGC(); + return GCIdleTimeAction::FullGC(reduce_memory); } } + if (heap_state.sweeping_in_progress) { if (heap_state.sweeping_completed) { return GCIdleTimeAction::FinalizeSweeping(); } else { - return NothingOrDone(); + return GCIdleTimeAction::Nothing(); } } + if (heap_state.incremental_marking_stopped && - !heap_state.can_start_incremental_marking) { - return NothingOrDone(); + !heap_state.can_start_incremental_marking && !reduce_memory) { + return GCIdleTimeAction::Nothing(); } size_t step_size = EstimateMarkingStepSize( static_cast(kIncrementalMarkingStepTimeInMs), heap_state.incremental_marking_speed_in_bytes_per_ms); - return GCIdleTimeAction::IncrementalMarking(step_size); + return GCIdleTimeAction::IncrementalMarking(step_size, reduce_memory); +} + + +void GCIdleTimeHandler::UpdateCounters(double idle_time_in_ms) { + if (mode_ == kReduceLatency) { + int mutator_gcs = scavenges_ + mark_compacts_ - idle_mark_compacts_; + if (mutator_gcs > 0) { + // There was a mutator GC since the last notification. + long_idle_notifications_ = 0; + } + idle_mark_compacts_ = 0; + mark_compacts_ = 0; + scavenges_ = 0; + if (idle_time_in_ms >= kMinLongIdleTime) { + long_idle_notifications_ += + (idle_time_in_ms >= kLargeLongIdleTime) + ? kLongIdleNotificationsBeforeMutatorIsIdle + : 1; + } + } +} + + +void GCIdleTimeHandler::ResetCounters() { + long_idle_notifications_ = 0; + idle_mark_compacts_ = 0; + mark_compacts_ = 0; + scavenges_ = 0; +} + + +bool GCIdleTimeHandler::IsMutatorActive(int contexts_disposed, int gcs) { + return contexts_disposed > 0 || gcs >= kGCsBeforeMutatorIsActive; +} + + +bool GCIdleTimeHandler::IsMutatorIdle(int long_idle_notifications, int gcs) { + return gcs == 0 && + long_idle_notifications >= kLongIdleNotificationsBeforeMutatorIsIdle; +} + + +GCIdleTimeHandler::Mode GCIdleTimeHandler::NextMode( + const HeapState& heap_state) { + DCHECK(mark_compacts_ >= idle_mark_compacts_); + int mutator_gcs = scavenges_ + mark_compacts_ - idle_mark_compacts_; + switch (mode_) { + case kDone: + DCHECK(idle_mark_compacts_ == 0); + if (IsMutatorActive(heap_state.contexts_disposed, mutator_gcs)) { + return kReduceLatency; + } + break; + case kReduceLatency: + if (IsMutatorIdle(long_idle_notifications_, mutator_gcs)) { + return kReduceMemory; + } + break; + case kReduceMemory: + if (idle_mark_compacts_ >= kMaxIdleMarkCompacts) { + return kDone; + } + if (mutator_gcs > idle_mark_compacts_) { + return kReduceLatency; + } + break; + } + return mode_; } } } diff --git a/src/heap/gc-idle-time-handler.h b/src/heap/gc-idle-time-handler.h index ed89b8b..ebb3b1c 100644 --- a/src/heap/gc-idle-time-handler.h +++ b/src/heap/gc-idle-time-handler.h @@ -27,6 +27,7 @@ class GCIdleTimeAction { result.type = DONE; result.parameter = 0; result.additional_work = false; + result.reduce_memory = false; return result; } @@ -35,14 +36,17 @@ class GCIdleTimeAction { result.type = DO_NOTHING; result.parameter = 0; result.additional_work = false; + result.reduce_memory = false; return result; } - static GCIdleTimeAction IncrementalMarking(intptr_t step_size) { + static GCIdleTimeAction IncrementalMarking(intptr_t step_size, + bool reduce_memory) { GCIdleTimeAction result; result.type = DO_INCREMENTAL_MARKING; result.parameter = step_size; result.additional_work = false; + result.reduce_memory = reduce_memory; return result; } @@ -51,14 +55,18 @@ class GCIdleTimeAction { result.type = DO_SCAVENGE; result.parameter = 0; result.additional_work = false; + // TODO(ulan): add reduce_memory argument and shrink new space size if + // reduce_memory = true. + result.reduce_memory = false; return result; } - static GCIdleTimeAction FullGC() { + static GCIdleTimeAction FullGC(bool reduce_memory) { GCIdleTimeAction result; result.type = DO_FULL_GC; result.parameter = 0; result.additional_work = false; + result.reduce_memory = reduce_memory; return result; } @@ -67,6 +75,7 @@ class GCIdleTimeAction { result.type = DO_FINALIZE_SWEEPING; result.parameter = 0; result.additional_work = false; + result.reduce_memory = false; return result; } @@ -75,6 +84,7 @@ class GCIdleTimeAction { GCIdleTimeActionType type; intptr_t parameter; bool additional_work; + bool reduce_memory; }; @@ -111,13 +121,6 @@ class GCIdleTimeHandler { // EstimateFinalIncrementalMarkCompactTime. static const size_t kMaxFinalIncrementalMarkCompactTimeInMs; - // Number of idle mark-compact events, after which idle handler will finish - // idle round. - static const int kMaxMarkCompactsInIdleRound; - - // Number of scavenges that will trigger start of new idle round. - static const int kIdleScavengeThreshold; - // This is the maximum scheduled idle time. Note that it can be more than // 16.66 ms when there is currently no rendering going on. static const size_t kMaxScheduledIdleTime = 50; @@ -141,10 +144,22 @@ class GCIdleTimeHandler { static const size_t kMinTimeForOverApproximatingWeakClosureInMs; - // Number of times we will return a Nothing action per Idle round despite - // having idle time available before we returning a Done action to ensure we - // don't keep scheduling idle tasks and making no progress. - static const int kMaxNoProgressIdleTimesPerIdleRound = 10; + // The number of idle MarkCompact GCs to perform before transitioning to + // the kDone mode. + static const int kMaxIdleMarkCompacts = 3; + + // The number of mutator GCs before transitioning to the kReduceLatency mode. + static const int kGCsBeforeMutatorIsActive = 7; + + // Mutator is considered idle if + // 1) there is an idle notification with time >= kLargeLongIdleTime, + // 2) or there are kLongIdleNotificationsBeforeMutatorIsIdle idle + // notifications + // with time >= kMinLongIdleTime and without any mutator GC in between. + static const int kMinLongIdleTime = kMaxFrameRenderingIdleTime + 1; + static const int kLargeLongIdleTime = 900; + static const int kLongIdleNotificationsBeforeMutatorIsIdle = 20; + class HeapState { public: @@ -167,23 +182,19 @@ class GCIdleTimeHandler { }; GCIdleTimeHandler() - : mark_compacts_since_idle_round_started_(0), - scavenges_since_last_idle_round_(0), - idle_times_which_made_no_progress_since_last_idle_round_(0) {} + : idle_mark_compacts_(0), + mark_compacts_(0), + scavenges_(0), + long_idle_notifications_(0), + mode_(kReduceLatency) {} GCIdleTimeAction Compute(double idle_time_in_ms, HeapState heap_state); - void NotifyIdleMarkCompact() { - if (mark_compacts_since_idle_round_started_ < kMaxMarkCompactsInIdleRound) { - ++mark_compacts_since_idle_round_started_; - if (mark_compacts_since_idle_round_started_ == - kMaxMarkCompactsInIdleRound) { - scavenges_since_last_idle_round_ = 0; - } - } - } + void NotifyIdleMarkCompact() { ++idle_mark_compacts_; } - void NotifyScavenge() { ++scavenges_since_last_idle_round_; } + void NotifyMarkCompact() { ++mark_compacts_; } + + void NotifyScavenge() { ++scavenges_; } static size_t EstimateMarkingStepSize(size_t idle_time_in_ms, size_t marking_speed_in_bytes_per_ms); @@ -212,24 +223,27 @@ class GCIdleTimeHandler { size_t scavenger_speed_in_bytes_per_ms, size_t new_space_allocation_throughput_in_bytes_per_ms); - private: - GCIdleTimeAction NothingOrDone(); + enum Mode { kReduceLatency, kReduceMemory, kDone }; - void StartIdleRound() { - mark_compacts_since_idle_round_started_ = 0; - idle_times_which_made_no_progress_since_last_idle_round_ = 0; - } - bool IsMarkCompactIdleRoundFinished() { - return mark_compacts_since_idle_round_started_ == - kMaxMarkCompactsInIdleRound; - } - bool EnoughGarbageSinceLastIdleRound() { - return scavenges_since_last_idle_round_ >= kIdleScavengeThreshold; - } + Mode mode() { return mode_; } - int mark_compacts_since_idle_round_started_; - int scavenges_since_last_idle_round_; - int idle_times_which_made_no_progress_since_last_idle_round_; + private: + bool IsMutatorActive(int contexts_disposed, int gcs); + bool IsMutatorIdle(int long_idle_notifications, int gcs); + void UpdateCounters(double idle_time_in_ms); + void ResetCounters(); + Mode NextMode(const HeapState& heap_state); + GCIdleTimeAction Action(double idle_time_in_ms, const HeapState& heap_state, + bool reduce_memory); + + int idle_mark_compacts_; + int mark_compacts_; + int scavenges_; + // The number of long idle notifications with no mutator GC happening + // between the notifications. + int long_idle_notifications_; + + Mode mode_; DISALLOW_COPY_AND_ASSIGN(GCIdleTimeHandler); }; diff --git a/src/heap/heap.cc b/src/heap/heap.cc index c145a63..bfd1fb5 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc @@ -915,6 +915,13 @@ bool Heap::CollectGarbage(GarbageCollector collector, const char* gc_reason, if (collector == MARK_COMPACTOR && FLAG_track_detached_contexts) { isolate()->CheckDetachedContextsAfterGC(); } + + if (collector == MARK_COMPACTOR) { + gc_idle_time_handler_.NotifyMarkCompact(); + } else { + gc_idle_time_handler_.NotifyScavenge(); + } + tracer()->Stop(collector); } @@ -1642,8 +1649,6 @@ void Heap::Scavenge() { LOG(isolate_, ResourceEvent("scavenge", "end")); gc_state_ = NOT_IN_GC; - - gc_idle_time_handler_.NotifyScavenge(); } @@ -4573,6 +4578,7 @@ bool Heap::TryFinalizeIdleIncrementalMarking( static_cast(idle_time_in_ms), size_of_objects, final_incremental_mark_compact_speed_in_bytes_per_ms))) { CollectAllGarbage(kNoGCFlags, "idle notification: finalize incremental"); + gc_idle_time_handler_.NotifyIdleMarkCompact(); ReduceNewSpaceSize(is_long_idle_notification); return true; } @@ -4663,6 +4669,7 @@ bool Heap::IdleNotification(double deadline_in_seconds) { break; case DO_INCREMENTAL_MARKING: { if (incremental_marking()->IsStopped()) { + // TODO(ulan): take reduce_memory into account. incremental_marking()->Start(); } double remaining_idle_time_in_ms = 0.0; diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index ad9fe8a..cb08fb4 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -2245,28 +2245,10 @@ TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking) { IncrementalMarking* marking = CcTest::heap()->incremental_marking(); marking->Abort(); marking->Start(); - - // The following two calls will increment CcTest::heap()->global_ic_age(). - const double kLongIdlePauseInSeconds = 1.0; + // The following calls will increment CcTest::heap()->global_ic_age(). CcTest::isolate()->ContextDisposedNotification(); - CcTest::isolate()->IdleNotificationDeadline( - (v8::base::TimeTicks::HighResolutionNow().ToInternalValue() / - static_cast(v8::base::Time::kMicrosecondsPerSecond)) + - kLongIdlePauseInSeconds); - - while (!marking->IsStopped() && !marking->IsComplete()) { - marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); - } - if (!marking->IsStopped() || marking->should_hurry()) { - // We don't normally finish a GC via Step(), we normally finish by - // setting the stack guard and then do the final steps in the stack - // guard interrupt. But here we didn't ask for that, and there is no - // JS code running to trigger the interrupt, so we explicitly finalize - // here. - CcTest::heap()->CollectAllGarbage(Heap::kFinalizeIncrementalMarkingMask, - "Test finalizing incremental mark-sweep"); - } - + SimulateIncrementalMarking(CcTest::heap()); + CcTest::heap()->CollectAllGarbage(); CHECK_EQ(CcTest::heap()->global_ic_age(), f->shared()->ic_age()); CHECK_EQ(0, f->shared()->opt_count()); CHECK_EQ(0, f->shared()->code()->profiler_ticks()); @@ -2305,13 +2287,8 @@ TEST(ResetSharedFunctionInfoCountersDuringMarkSweep) { CcTest::heap()->incremental_marking()->Abort(); // The following two calls will increment CcTest::heap()->global_ic_age(). - // Since incremental marking is off, IdleNotification will do full GC. - const double kLongIdlePauseInSeconds = 1.0; CcTest::isolate()->ContextDisposedNotification(); - CcTest::isolate()->IdleNotificationDeadline( - (v8::base::TimeTicks::HighResolutionNow().ToInternalValue() / - static_cast(v8::base::Time::kMicrosecondsPerSecond)) + - kLongIdlePauseInSeconds); + CcTest::heap()->CollectAllGarbage(); CHECK_EQ(CcTest::heap()->global_ic_age(), f->shared()->ic_age()); CHECK_EQ(0, f->shared()->opt_count()); diff --git a/test/unittests/heap/gc-idle-time-handler-unittest.cc b/test/unittests/heap/gc-idle-time-handler-unittest.cc index 868262b..67ae4e0 100644 --- a/test/unittests/heap/gc-idle-time-handler-unittest.cc +++ b/test/unittests/heap/gc-idle-time-handler-unittest.cc @@ -38,12 +38,63 @@ class GCIdleTimeHandlerTest : public ::testing::Test { return result; } + void TransitionToReduceMemoryMode( + const GCIdleTimeHandler::HeapState& heap_state) { + handler()->NotifyScavenge(); + EXPECT_EQ(GCIdleTimeHandler::kReduceLatency, handler()->mode()); + double idle_time_ms = GCIdleTimeHandler::kMinLongIdleTime; + int limit = GCIdleTimeHandler::kLongIdleNotificationsBeforeMutatorIsIdle; + bool incremental = !heap_state.incremental_marking_stopped || + heap_state.can_start_incremental_marking; + for (int i = 0; i < limit; i++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(incremental ? DO_INCREMENTAL_MARKING : DO_NOTHING, action.type); + } + handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(GCIdleTimeHandler::kReduceMemory, handler()->mode()); + } + + void TransitionToDoneMode(const GCIdleTimeHandler::HeapState& heap_state, + double idle_time_ms, + GCIdleTimeActionType expected) { + EXPECT_EQ(GCIdleTimeHandler::kReduceMemory, handler()->mode()); + int limit = GCIdleTimeHandler::kMaxIdleMarkCompacts; + for (int i = 0; i < limit; i++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(expected, action.type); + EXPECT_TRUE(action.reduce_memory); + handler()->NotifyMarkCompact(); + handler()->NotifyIdleMarkCompact(); + } + handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(GCIdleTimeHandler::kDone, handler()->mode()); + } + + void TransitionToReduceLatencyMode( + const GCIdleTimeHandler::HeapState& heap_state) { + EXPECT_EQ(GCIdleTimeHandler::kDone, handler()->mode()); + int limit = GCIdleTimeHandler::kGCsBeforeMutatorIsActive; + double idle_time_ms = GCIdleTimeHandler::kMinLongIdleTime; + for (int i = 0; i < limit; i++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DONE, action.type); + if (i % 2 == 0) { + handler()->NotifyScavenge(); + } else { + handler()->NotifyMarkCompact(); + } + } + handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(GCIdleTimeHandler::kReduceLatency, handler()->mode()); + } + static const size_t kSizeOfObjects = 100 * MB; static const size_t kMarkCompactSpeed = 200 * KB; static const size_t kMarkingSpeed = 200 * KB; static const size_t kScavengeSpeed = 100 * KB; static const size_t kNewSpaceCapacity = 1 * MB; static const size_t kNewSpaceAllocationThroughput = 10 * KB; + static const int kMaxNotifications = 100; private: GCIdleTimeHandler handler_; @@ -113,9 +164,9 @@ TEST(GCIdleTimeHandler, EstimateMarkCompactTimeMax) { TEST_F(GCIdleTimeHandlerTest, DoScavengeEmptyNewSpace) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - int idle_time_in_ms = 16; + int idle_time_ms = 16; EXPECT_FALSE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_in_ms, heap_state.new_space_capacity, + idle_time_ms, heap_state.new_space_capacity, heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, heap_state.new_space_allocation_throughput_in_bytes_per_ms)); } @@ -124,9 +175,9 @@ TEST_F(GCIdleTimeHandlerTest, DoScavengeEmptyNewSpace) { TEST_F(GCIdleTimeHandlerTest, DoScavengeFullNewSpace) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.used_new_space_size = kNewSpaceCapacity; - int idle_time_in_ms = 16; + int idle_time_ms = 16; EXPECT_TRUE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_in_ms, heap_state.new_space_capacity, + idle_time_ms, heap_state.new_space_capacity, heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, heap_state.new_space_allocation_throughput_in_bytes_per_ms)); } @@ -136,9 +187,9 @@ TEST_F(GCIdleTimeHandlerTest, DoScavengeUnknownScavengeSpeed) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.used_new_space_size = kNewSpaceCapacity; heap_state.scavenge_speed_in_bytes_per_ms = 0; - int idle_time_in_ms = 8; + int idle_time_ms = 8; EXPECT_FALSE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_in_ms, heap_state.new_space_capacity, + idle_time_ms, heap_state.new_space_capacity, heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, heap_state.new_space_allocation_throughput_in_bytes_per_ms)); } @@ -148,9 +199,9 @@ TEST_F(GCIdleTimeHandlerTest, DoScavengeLowScavengeSpeed) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.used_new_space_size = kNewSpaceCapacity; heap_state.scavenge_speed_in_bytes_per_ms = 1 * KB; - int idle_time_in_ms = 16; + int idle_time_ms = 16; EXPECT_FALSE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_in_ms, heap_state.new_space_capacity, + idle_time_ms, heap_state.new_space_capacity, heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, heap_state.new_space_allocation_throughput_in_bytes_per_ms)); } @@ -160,38 +211,38 @@ TEST_F(GCIdleTimeHandlerTest, DoScavengeHighScavengeSpeed) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.used_new_space_size = kNewSpaceCapacity; heap_state.scavenge_speed_in_bytes_per_ms = kNewSpaceCapacity; - int idle_time_in_ms = 16; + int idle_time_ms = 16; EXPECT_TRUE(GCIdleTimeHandler::ShouldDoScavenge( - idle_time_in_ms, heap_state.new_space_capacity, + idle_time_ms, heap_state.new_space_capacity, heap_state.used_new_space_size, heap_state.scavenge_speed_in_bytes_per_ms, heap_state.new_space_allocation_throughput_in_bytes_per_ms)); } TEST_F(GCIdleTimeHandlerTest, ShouldDoMarkCompact) { - size_t idle_time_in_ms = GCIdleTimeHandler::kMaxScheduledIdleTime; - EXPECT_TRUE(GCIdleTimeHandler::ShouldDoMarkCompact(idle_time_in_ms, 0, 0)); + size_t idle_time_ms = GCIdleTimeHandler::kMaxScheduledIdleTime; + EXPECT_TRUE(GCIdleTimeHandler::ShouldDoMarkCompact(idle_time_ms, 0, 0)); } TEST_F(GCIdleTimeHandlerTest, DontDoMarkCompact) { - size_t idle_time_in_ms = 1; + size_t idle_time_ms = 1; EXPECT_FALSE(GCIdleTimeHandler::ShouldDoMarkCompact( - idle_time_in_ms, kSizeOfObjects, kMarkingSpeed)); + idle_time_ms, kSizeOfObjects, kMarkingSpeed)); } TEST_F(GCIdleTimeHandlerTest, ShouldDoFinalIncrementalMarkCompact) { - size_t idle_time_in_ms = 16; + size_t idle_time_ms = 16; EXPECT_TRUE(GCIdleTimeHandler::ShouldDoFinalIncrementalMarkCompact( - idle_time_in_ms, 0, 0)); + idle_time_ms, 0, 0)); } TEST_F(GCIdleTimeHandlerTest, DontDoFinalIncrementalMarkCompact) { - size_t idle_time_in_ms = 1; + size_t idle_time_ms = 1; EXPECT_FALSE(GCIdleTimeHandler::ShouldDoFinalIncrementalMarkCompact( - idle_time_in_ms, kSizeOfObjects, kMarkingSpeed)); + idle_time_ms, kSizeOfObjects, kMarkingSpeed)); } @@ -200,8 +251,11 @@ TEST_F(GCIdleTimeHandlerTest, ContextDisposeLowRate) { heap_state.contexts_disposed = 1; heap_state.incremental_marking_stopped = true; double idle_time_ms = 0; - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_NOTHING, action.type); + for (int mode = 0; mode < 1; mode++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_NOTHING, action.type); + TransitionToReduceMemoryMode(heap_state); + } } @@ -212,22 +266,11 @@ TEST_F(GCIdleTimeHandlerTest, ContextDisposeHighRate) { GCIdleTimeHandler::kHighContextDisposalRate - 1; heap_state.incremental_marking_stopped = true; double idle_time_ms = 0; - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_FULL_GC, action.type); -} - - -TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeLargeIdleTime) { - GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - heap_state.contexts_disposed = 1; - heap_state.contexts_disposal_rate = 1.0; - heap_state.incremental_marking_stopped = true; - heap_state.can_start_incremental_marking = false; - size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; - double idle_time_ms = - static_cast((heap_state.size_of_objects + speed - 1) / speed); - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_FULL_GC, action.type); + for (int mode = 0; mode < 1; mode++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_FULL_GC, action.type); + TransitionToReduceMemoryMode(heap_state); + } } @@ -237,8 +280,11 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeZeroIdleTime) { heap_state.contexts_disposal_rate = 1.0; heap_state.incremental_marking_stopped = true; double idle_time_ms = 0; - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_FULL_GC, action.type); + for (int mode = 0; mode < 1; mode++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_FULL_GC, action.type); + TransitionToReduceMemoryMode(heap_state); + } } @@ -250,8 +296,11 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) { size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; double idle_time_ms = static_cast(heap_state.size_of_objects / speed - 1); - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + for (int mode = 0; mode < 1; mode++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + TransitionToReduceMemoryMode(heap_state); + } } @@ -262,8 +311,11 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime2) { size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; double idle_time_ms = static_cast(heap_state.size_of_objects / speed - 1); - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + for (int mode = 0; mode < 1; mode++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + TransitionToReduceMemoryMode(heap_state); + } } @@ -271,11 +323,14 @@ TEST_F(GCIdleTimeHandlerTest, IncrementalMarking1) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); size_t speed = heap_state.incremental_marking_speed_in_bytes_per_ms; double idle_time_ms = 10; - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); - EXPECT_GT(speed * static_cast(idle_time_ms), - static_cast(action.parameter)); - EXPECT_LT(0, action.parameter); + for (int mode = 0; mode < 1; mode++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + EXPECT_GT(speed * static_cast(idle_time_ms), + static_cast(action.parameter)); + EXPECT_LT(0, action.parameter); + TransitionToReduceMemoryMode(heap_state); + } } @@ -284,11 +339,14 @@ TEST_F(GCIdleTimeHandlerTest, IncrementalMarking2) { heap_state.incremental_marking_stopped = true; size_t speed = heap_state.incremental_marking_speed_in_bytes_per_ms; double idle_time_ms = 10; - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); - EXPECT_GT(speed * static_cast(idle_time_ms), - static_cast(action.parameter)); - EXPECT_LT(0, action.parameter); + for (int mode = 0; mode < 1; mode++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + EXPECT_GT(speed * static_cast(idle_time_ms), + static_cast(action.parameter)); + EXPECT_LT(0, action.parameter); + TransitionToReduceMemoryMode(heap_state); + } } @@ -301,6 +359,9 @@ TEST_F(GCIdleTimeHandlerTest, NotEnoughTime) { static_cast(heap_state.size_of_objects / speed - 1); GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DO_NOTHING, action.type); + TransitionToReduceMemoryMode(heap_state); + action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); } @@ -308,11 +369,16 @@ TEST_F(GCIdleTimeHandlerTest, FinalizeSweeping) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.incremental_marking_stopped = true; heap_state.can_start_incremental_marking = false; - heap_state.sweeping_in_progress = true; - heap_state.sweeping_completed = true; - double idle_time_ms = 10.0; - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_FINALIZE_SWEEPING, action.type); + for (int mode = 0; mode < 1; mode++) { + heap_state.sweeping_in_progress = true; + heap_state.sweeping_completed = true; + double idle_time_ms = 10.0; + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_FINALIZE_SWEEPING, action.type); + heap_state.sweeping_in_progress = false; + heap_state.sweeping_completed = false; + TransitionToReduceMemoryMode(heap_state); + } } @@ -320,11 +386,52 @@ TEST_F(GCIdleTimeHandlerTest, CannotFinalizeSweeping) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.incremental_marking_stopped = true; heap_state.can_start_incremental_marking = false; - heap_state.sweeping_in_progress = true; - heap_state.sweeping_completed = false; - double idle_time_ms = 10.0; - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_NOTHING, action.type); + for (int mode = 0; mode < 1; mode++) { + heap_state.sweeping_in_progress = true; + heap_state.sweeping_completed = false; + double idle_time_ms = 10.0; + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_NOTHING, action.type); + heap_state.sweeping_in_progress = false; + heap_state.sweeping_completed = false; + TransitionToReduceMemoryMode(heap_state); + } +} + + +TEST_F(GCIdleTimeHandlerTest, Scavenge) { + GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); + int idle_time_ms = 10; + for (int mode = 0; mode < 1; mode++) { + heap_state.used_new_space_size = + heap_state.new_space_capacity - + (kNewSpaceAllocationThroughput * idle_time_ms); + GCIdleTimeAction action = + handler()->Compute(static_cast(idle_time_ms), heap_state); + EXPECT_EQ(DO_SCAVENGE, action.type); + heap_state.used_new_space_size = 0; + TransitionToReduceMemoryMode(heap_state); + } +} + + +TEST_F(GCIdleTimeHandlerTest, ScavengeAndDone) { + GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); + int idle_time_ms = 10; + heap_state.can_start_incremental_marking = false; + heap_state.incremental_marking_stopped = true; + for (int mode = 0; mode < 1; mode++) { + heap_state.used_new_space_size = + heap_state.new_space_capacity - + (kNewSpaceAllocationThroughput * idle_time_ms); + GCIdleTimeAction action = + handler()->Compute(static_cast(idle_time_ms), heap_state); + EXPECT_EQ(DO_SCAVENGE, action.type); + heap_state.used_new_space_size = 0; + action = handler()->Compute(static_cast(idle_time_ms), heap_state); + EXPECT_EQ(DO_NOTHING, action.type); + TransitionToReduceMemoryMode(heap_state); + } } @@ -332,30 +439,41 @@ TEST_F(GCIdleTimeHandlerTest, StopEventually1) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.incremental_marking_stopped = true; heap_state.can_start_incremental_marking = false; + double idle_time_ms = GCIdleTimeHandler::kMinLongIdleTime; + bool stopped = false; + for (int i = 0; i < kMaxNotifications && !stopped; i++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + if (action.type == DO_INCREMENTAL_MARKING || action.type == DO_FULL_GC) { + handler()->NotifyMarkCompact(); + handler()->NotifyIdleMarkCompact(); + } + if (action.type == DONE) stopped = true; + } + EXPECT_TRUE(stopped); +} + + +TEST_F(GCIdleTimeHandlerTest, StopEventually2) { + GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); + heap_state.incremental_marking_stopped = true; + heap_state.can_start_incremental_marking = false; size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; double idle_time_ms = static_cast(heap_state.size_of_objects / speed + 1); - for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) { - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_FULL_GC, action.type); - handler()->NotifyIdleMarkCompact(); - } + TransitionToReduceMemoryMode(heap_state); + TransitionToDoneMode(heap_state, idle_time_ms, DO_FULL_GC); GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DONE, action.type); } -TEST_F(GCIdleTimeHandlerTest, StopEventually2) { +TEST_F(GCIdleTimeHandlerTest, StopEventually3) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - double idle_time_ms = 10; - for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) { - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); - // In this case we emulate incremental marking steps that finish with a - // full gc. - handler()->NotifyIdleMarkCompact(); - } + heap_state.incremental_marking_stopped = true; heap_state.can_start_incremental_marking = false; + double idle_time_ms = 10; + TransitionToReduceMemoryMode(heap_state); + TransitionToDoneMode(heap_state, idle_time_ms, DO_INCREMENTAL_MARKING); GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DONE, action.type); } @@ -368,153 +486,144 @@ TEST_F(GCIdleTimeHandlerTest, ContinueAfterStop1) { size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; double idle_time_ms = static_cast(heap_state.size_of_objects / speed + 1); - for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) { - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_FULL_GC, action.type); - handler()->NotifyIdleMarkCompact(); - } + TransitionToReduceMemoryMode(heap_state); + TransitionToDoneMode(heap_state, idle_time_ms, DO_FULL_GC); GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DONE, action.type); - // Emulate mutator work. - for (int i = 0; i < GCIdleTimeHandler::kIdleScavengeThreshold; i++) { - handler()->NotifyScavenge(); - } + TransitionToReduceLatencyMode(heap_state); + heap_state.can_start_incremental_marking = true; action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_FULL_GC, action.type); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + EXPECT_FALSE(action.reduce_memory); + EXPECT_EQ(GCIdleTimeHandler::kReduceLatency, handler()->mode()); } TEST_F(GCIdleTimeHandlerTest, ContinueAfterStop2) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - double idle_time_ms = 10; - for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) { - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - if (action.type == DONE) break; - EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); - // In this case we try to emulate incremental marking steps the finish with - // a full gc. - handler()->NotifyIdleMarkCompact(); - } + heap_state.incremental_marking_stopped = true; heap_state.can_start_incremental_marking = false; + double idle_time_ms = 10; + TransitionToReduceMemoryMode(heap_state); + TransitionToDoneMode(heap_state, idle_time_ms, DO_INCREMENTAL_MARKING); GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DONE, action.type); - // Emulate mutator work. - for (int i = 0; i < GCIdleTimeHandler::kIdleScavengeThreshold; i++) { - handler()->NotifyScavenge(); - } + TransitionToReduceLatencyMode(heap_state); heap_state.can_start_incremental_marking = true; action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + EXPECT_FALSE(action.reduce_memory); + EXPECT_EQ(GCIdleTimeHandler::kReduceLatency, handler()->mode()); } -TEST_F(GCIdleTimeHandlerTest, Scavenge) { +TEST_F(GCIdleTimeHandlerTest, ZeroIdleTimeNothingToDo) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - int idle_time_ms = 10; - heap_state.used_new_space_size = - heap_state.new_space_capacity - - (kNewSpaceAllocationThroughput * idle_time_ms); - GCIdleTimeAction action = - handler()->Compute(static_cast(idle_time_ms), heap_state); - EXPECT_EQ(DO_SCAVENGE, action.type); + for (int i = 0; i < kMaxNotifications; i++) { + GCIdleTimeAction action = handler()->Compute(0, heap_state); + EXPECT_EQ(DO_NOTHING, action.type); + } } -TEST_F(GCIdleTimeHandlerTest, ScavengeAndDone) { +TEST_F(GCIdleTimeHandlerTest, SmallIdleTimeNothingToDo) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - int idle_time_ms = 10; - heap_state.can_start_incremental_marking = false; heap_state.incremental_marking_stopped = true; - heap_state.used_new_space_size = - heap_state.new_space_capacity - - (kNewSpaceAllocationThroughput * idle_time_ms); - GCIdleTimeAction action = - handler()->Compute(static_cast(idle_time_ms), heap_state); - EXPECT_EQ(DO_SCAVENGE, action.type); - heap_state.used_new_space_size = 0; - action = handler()->Compute(static_cast(idle_time_ms), heap_state); - EXPECT_EQ(DO_NOTHING, action.type); -} - - -TEST_F(GCIdleTimeHandlerTest, ZeroIdleTimeNothingToDo) { - GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - double idle_time_ms = 0; - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_NOTHING, action.type); + heap_state.can_start_incremental_marking = false; + for (int i = 0; i < kMaxNotifications; i++) { + GCIdleTimeAction action = handler()->Compute(10, heap_state); + EXPECT_EQ(DO_NOTHING, action.type); + } } -TEST_F(GCIdleTimeHandlerTest, ZeroIdleTimeDoNothingButStartIdleRound) { +TEST_F(GCIdleTimeHandlerTest, StayInReduceLatencyModeBecauseOfScavenges) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - double idle_time_ms = 10; - for (int i = 0; i < GCIdleTimeHandler::kMaxMarkCompactsInIdleRound; i++) { + heap_state.incremental_marking_stopped = true; + heap_state.can_start_incremental_marking = false; + double idle_time_ms = GCIdleTimeHandler::kMinLongIdleTime; + int limit = GCIdleTimeHandler::kLongIdleNotificationsBeforeMutatorIsIdle; + for (int i = 0; i < kMaxNotifications; i++) { GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - if (action.type == DONE) break; - EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); - // In this case we try to emulate incremental marking steps the finish with - // a full gc. - handler()->NotifyIdleMarkCompact(); - } - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - // Emulate mutator work. - for (int i = 0; i < GCIdleTimeHandler::kIdleScavengeThreshold; i++) { - handler()->NotifyScavenge(); + EXPECT_EQ(DO_NOTHING, action.type); + if ((i + 1) % limit == 0) handler()->NotifyScavenge(); + EXPECT_EQ(GCIdleTimeHandler::kReduceLatency, handler()->mode()); } - action = handler()->Compute(0, heap_state); - EXPECT_EQ(DO_NOTHING, action.type); } -TEST_F(GCIdleTimeHandlerTest, KeepDoingDoNothingWithZeroIdleTime) { +TEST_F(GCIdleTimeHandlerTest, StayInReduceLatencyModeBecauseOfMarkCompacts) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - for (int i = 0; i < GCIdleTimeHandler::kMaxNoProgressIdleTimesPerIdleRound; - i++) { - GCIdleTimeAction action = handler()->Compute(0, heap_state); + heap_state.incremental_marking_stopped = true; + heap_state.can_start_incremental_marking = false; + double idle_time_ms = GCIdleTimeHandler::kMinLongIdleTime; + int limit = GCIdleTimeHandler::kLongIdleNotificationsBeforeMutatorIsIdle; + for (int i = 0; i < kMaxNotifications; i++) { + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DO_NOTHING, action.type); + if ((i + 1) % limit == 0) handler()->NotifyMarkCompact(); + EXPECT_EQ(GCIdleTimeHandler::kReduceLatency, handler()->mode()); } - // Should still return DO_NOTHING if we have been given 0 deadline yet. - GCIdleTimeAction action = handler()->Compute(0, heap_state); - EXPECT_EQ(DO_NOTHING, action.type); } -TEST_F(GCIdleTimeHandlerTest, DoneIfNotMakingProgressOnSweeping) { +TEST_F(GCIdleTimeHandlerTest, ReduceMemoryToReduceLatency) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - - // Simulate sweeping being in-progress but not complete. heap_state.incremental_marking_stopped = true; heap_state.can_start_incremental_marking = false; - heap_state.sweeping_in_progress = true; - heap_state.sweeping_completed = false; - double idle_time_ms = 10.0; - for (int i = 0; i < GCIdleTimeHandler::kMaxNoProgressIdleTimesPerIdleRound; - i++) { + double idle_time_ms = GCIdleTimeHandler::kMinLongIdleTime; + int limit = GCIdleTimeHandler::kMaxIdleMarkCompacts; + for (int idle_gc = 0; idle_gc < limit; idle_gc++) { + TransitionToReduceMemoryMode(heap_state); GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + EXPECT_TRUE(action.reduce_memory); + EXPECT_EQ(GCIdleTimeHandler::kReduceMemory, handler()->mode()); + for (int i = 0; i < idle_gc; i++) { + action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + EXPECT_TRUE(action.reduce_memory); + // ReduceMemory mode should tolerate one mutator GC per idle GC. + handler()->NotifyScavenge(); + // Notify idle GC. + handler()->NotifyMarkCompact(); + handler()->NotifyIdleMarkCompact(); + } + // Transition to ReduceLatency mode after doing |idle_gc| idle GCs. + handler()->NotifyScavenge(); + action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DO_NOTHING, action.type); + EXPECT_FALSE(action.reduce_memory); + EXPECT_EQ(GCIdleTimeHandler::kReduceLatency, handler()->mode()); } - // We should return DONE after not making progress for some time. - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DONE, action.type); } -TEST_F(GCIdleTimeHandlerTest, DoneIfNotMakingProgressOnIncrementalMarking) { +TEST_F(GCIdleTimeHandlerTest, ReduceMemoryToDone) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); - - // Simulate incremental marking stopped and not eligible to start. heap_state.incremental_marking_stopped = true; heap_state.can_start_incremental_marking = false; - double idle_time_ms = 10.0; - for (int i = 0; i < GCIdleTimeHandler::kMaxNoProgressIdleTimesPerIdleRound; - i++) { - GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); - EXPECT_EQ(DO_NOTHING, action.type); - } - // We should return DONE after not making progress for some time. + double idle_time_ms = GCIdleTimeHandler::kMinLongIdleTime; + int limit = GCIdleTimeHandler::kMaxIdleMarkCompacts; + TransitionToReduceMemoryMode(heap_state); GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + EXPECT_TRUE(action.reduce_memory); + for (int i = 0; i < limit; i++) { + action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_INCREMENTAL_MARKING, action.type); + EXPECT_TRUE(action.reduce_memory); + EXPECT_EQ(GCIdleTimeHandler::kReduceMemory, handler()->mode()); + // ReduceMemory mode should tolerate one mutator GC per idle GC. + handler()->NotifyScavenge(); + // Notify idle GC. + handler()->NotifyMarkCompact(); + handler()->NotifyIdleMarkCompact(); + } + action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DONE, action.type); } + } // namespace internal } // namespace v8 -- 2.7.4