Start incremental marking in long idle notification for background tab

disregarding the allocation throughput.

BUG=chromium:506132
LOG=NO

Review URL: https://codereview.chromium.org/1213313004

Cr-Commit-Position: refs/heads/master@{#29515}
This commit is contained in:
ulan 2015-07-07 05:30:52 -07:00 committed by Commit bot
parent ca7feba733
commit fe8c8c3bc5
5 changed files with 129 additions and 12 deletions

View File

@ -4956,6 +4956,19 @@ void Heap::IdleNotificationEpilogue(GCIdleTimeAction action,
}
void Heap::CheckAndNotifyBackgroundIdleNotification(double idle_time_in_ms,
double now_ms) {
if (idle_time_in_ms >= GCIdleTimeHandler::kMinBackgroundIdleTime) {
MemoryReducer::Event event;
event.type = MemoryReducer::kBackgroundIdleNotification;
event.time_ms = now_ms;
event.can_start_incremental_gc = incremental_marking()->IsStopped() &&
incremental_marking()->CanBeActivated();
memory_reducer_.NotifyBackgroundIdleNotification(event);
}
}
double Heap::MonotonicallyIncreasingTimeInMs() {
return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() *
static_cast<double>(base::Time::kMillisecondsPerSecond);
@ -4980,6 +4993,8 @@ bool Heap::IdleNotification(double deadline_in_seconds) {
double start_ms = MonotonicallyIncreasingTimeInMs();
double idle_time_in_ms = deadline_in_ms - start_ms;
CheckAndNotifyBackgroundIdleNotification(idle_time_in_ms, start_ms);
tracer()->SampleAllocation(start_ms, NewSpaceAllocationCounter(),
OldGenerationAllocationCounter());

View File

@ -2279,6 +2279,8 @@ class Heap {
void IdleNotificationEpilogue(GCIdleTimeAction action,
GCIdleTimeHandler::HeapState heap_state,
double start_ms, double deadline_in_ms);
void CheckAndNotifyBackgroundIdleNotification(double idle_time_in_ms,
double now_ms);
void ClearObjectStats(bool clear_last_time_stats = false);

View File

@ -82,6 +82,26 @@ void MemoryReducer::NotifyContextDisposed(const Event& event) {
}
void MemoryReducer::NotifyBackgroundIdleNotification(const Event& event) {
DCHECK_EQ(kBackgroundIdleNotification, event.type);
Action old_action = state_.action;
int old_started_gcs = state_.started_gcs;
state_ = Step(state_, event);
if (old_action == kWait && state_.action == kWait &&
old_started_gcs + 1 == state_.started_gcs) {
DCHECK(heap()->incremental_marking()->IsStopped());
DCHECK(FLAG_incremental_marking);
heap()->StartIdleIncrementalMarking();
if (FLAG_trace_gc_verbose) {
PrintIsolate(heap()->isolate(),
"Memory reducer: started GC #%d"
" (background idle)\n",
state_.started_gcs);
}
}
}
// For specification of this function see the comment for MemoryReducer class.
MemoryReducer::State MemoryReducer::Step(const State& state,
const Event& event) {
@ -90,24 +110,40 @@ MemoryReducer::State MemoryReducer::Step(const State& state,
}
switch (state.action) {
case kDone:
if (event.type == kTimer) {
if (event.type == kTimer || event.type == kBackgroundIdleNotification) {
return state;
} else {
DCHECK(event.type == kContextDisposed || event.type == kMarkCompact);
return State(kWait, 0, event.time_ms + kLongDelayMs);
}
case kWait:
if (event.type == kContextDisposed) {
return state;
} else if (event.type == kTimer && event.can_start_incremental_gc &&
event.low_allocation_rate) {
if (state.next_gc_start_ms <= event.time_ms) {
return State(kRun, state.started_gcs + 1, 0.0);
} else {
switch (event.type) {
case kContextDisposed:
return state;
}
} else {
return State(kWait, state.started_gcs, event.time_ms + kLongDelayMs);
case kTimer:
if (state.started_gcs >= kMaxNumberOfGCs) {
return State(kDone, 0, 0.0);
} else if (event.can_start_incremental_gc &&
event.low_allocation_rate) {
if (state.next_gc_start_ms <= event.time_ms) {
return State(kRun, state.started_gcs + 1, 0.0);
} else {
return state;
}
} else {
return State(kWait, state.started_gcs,
event.time_ms + kLongDelayMs);
}
case kBackgroundIdleNotification:
if (event.can_start_incremental_gc &&
state.started_gcs < kMaxNumberOfGCs) {
return State(kWait, state.started_gcs + 1,
event.time_ms + kLongDelayMs);
} else {
return state;
}
case kMarkCompact:
return State(kWait, state.started_gcs, event.time_ms + kLongDelayMs);
}
case kRun:
if (event.type != kMarkCompact) {

View File

@ -48,6 +48,15 @@ class Heap;
// - in the timer callback if the mutator allocation rate is high or
// incremental GC is in progress.
//
// WAIT n x -> WAIT (n+1) happens:
// - on background idle notification, which signals that we can start
// incremental marking even if the allocation rate is high.
// The MemoryReducer starts incremental marking on this transition but still
// has a pending timer task.
//
// WAIT n x -> DONE happens:
// - in the timer callback if n >= kMaxNumberOfGCs.
//
// WAIT n x -> RUN (n+1) happens:
// - in the timer callback if the mutator allocation rate is low
// and now_ms >= x and there is no incremental GC in progress.
@ -81,6 +90,7 @@ class MemoryReducer {
kTimer,
kMarkCompact,
kContextDisposed,
kBackgroundIdleNotification
};
struct Event {
@ -95,8 +105,8 @@ class MemoryReducer {
// Callbacks.
void NotifyTimer(const Event& event);
void NotifyMarkCompact(const Event& event);
void NotifyScavenge(const Event& event);
void NotifyContextDisposed(const Event& event);
void NotifyBackgroundIdleNotification(const Event& event);
// The step function that computes the next state from the current state and
// the incoming event.
static State Step(const State& state, const Event& event);

View File

@ -82,6 +82,16 @@ MemoryReducer::Event ContextDisposedEvent(double time_ms) {
}
MemoryReducer::Event BackgroundIdleNotificationEvent(
double time_ms, bool can_start_incremental_gc = true) {
MemoryReducer::Event event;
event.type = MemoryReducer::kBackgroundIdleNotification;
event.time_ms = time_ms;
event.can_start_incremental_gc = can_start_incremental_gc;
return event;
}
TEST(MemoryReducer, FromDoneToDone) {
MemoryReducer::State state0(DoneState()), state1(DoneState());
@ -93,6 +103,9 @@ TEST(MemoryReducer, FromDoneToDone) {
state1 = MemoryReducer::Step(state0, TimerEventPendingGC(0));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
state1 = MemoryReducer::Step(state0, BackgroundIdleNotificationEvent(0));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
}
@ -153,6 +166,23 @@ TEST(MemoryReducer, FromWaitToWait) {
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
state1 = MemoryReducer::Step(state0, BackgroundIdleNotificationEvent(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs + 1, state1.started_gcs);
state1 =
MemoryReducer::Step(state0, BackgroundIdleNotificationEvent(2000, false));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
state0.started_gcs = MemoryReducer::kMaxNumberOfGCs;
state1 = MemoryReducer::Step(state0, BackgroundIdleNotificationEvent(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
}
@ -169,6 +199,30 @@ TEST(MemoryReducer, FromWaitToRun) {
}
TEST(MemoryReducer, FromWaitToDone) {
if (!FLAG_incremental_marking) return;
MemoryReducer::State state0(WaitState(2, 0.0)), state1(DoneState());
state0.started_gcs = MemoryReducer::kMaxNumberOfGCs;
state1 = MemoryReducer::Step(state0, TimerEventLowAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
state1 = MemoryReducer::Step(state0, TimerEventHighAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
state1 = MemoryReducer::Step(state0, TimerEventPendingGC(2000));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
}
TEST(MemoryReducer, FromRunToRun) {
if (!FLAG_incremental_marking) return;