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:
parent
ca7feba733
commit
fe8c8c3bc5
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user