 Chromium Code Reviews
 Chromium Code Reviews Issue 1105293004:
  Add mode to reduce memory usage in idle notification.  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@master
    
  
    Issue 1105293004:
  Add mode to reduce memory usage in idle notification.  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@master| Index: src/heap/gc-idle-time-handler.cc | 
| diff --git a/src/heap/gc-idle-time-handler.cc b/src/heap/gc-idle-time-handler.cc | 
| index a3383b9c0c0dcde34864a7fbbafef853d24d5d55..9f181d850c7fc4cc115e5314fee9988fbd320679 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 does only incremental steps and does not | 
| 
Hannes Payer (out of office)
2015/05/04 11:20:36
does only start incremental marking if can_...
 
ulan
2015/05/05 12:10:13
Done.
 | 
| +// start 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 is there were N mutator GCs | 
| 
Hannes Payer (out of office)
2015/05/04 11:20:36
happens if N mutator GCs were performed meaning th
 
ulan
2015/05/05 12:10:13
Done.
 | 
| +// 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 Step(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::Step(double idle_time_in_ms, | 
| 
Hannes Payer (out of office)
2015/05/04 11:20:36
Can we find a better name here, to not confuse it
 
ulan
2015/05/05 12:10:13
Done.
 | 
| + const HeapState& heap_state, | 
| + bool reduce_memory) { | 
| if (static_cast<int>(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,92 @@ 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<size_t>(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<size_t>(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) { | 
| + ResetCounters(); | 
| + } | 
| + long_idle_notifications_ += (idle_time_in_ms >= kMaxLongIdleTime) | 
| + ? kLongIdleNotificationsBeforeMutatorIsIdle | 
| 
Hannes Payer (out of office)
2015/05/04 11:20:36
why + kLongIdleNotificationsBeforeMutatorIsIdle?
 
ulan
2015/05/05 12:10:13
So that the next call to idle notification immedia
 
Hannes Payer (out of office)
2015/05/05 20:51:16
OK, makes sense for that case.
 | 
| + : (idle_time_in_ms >= kMinLongIdleTime); | 
| + } | 
| +} | 
| + | 
| + | 
| +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_; | 
| } | 
| } | 
| } |