Perform context disposal garbage collections only if contexts disposals happen at a high rate.

BUG=
R=ulan@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#25208}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25208 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
hpayer@chromium.org 2014-11-07 09:36:49 +00:00
parent 98d4a8bd31
commit c0ac2ab9d0
6 changed files with 102 additions and 18 deletions

View File

@ -14,6 +14,7 @@ const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000;
const size_t GCIdleTimeHandler::kMinTimeForFinalizeSweeping = 100; const size_t GCIdleTimeHandler::kMinTimeForFinalizeSweeping = 100;
const int GCIdleTimeHandler::kMaxMarkCompactsInIdleRound = 7; const int GCIdleTimeHandler::kMaxMarkCompactsInIdleRound = 7;
const int GCIdleTimeHandler::kIdleScavengeThreshold = 5; const int GCIdleTimeHandler::kIdleScavengeThreshold = 5;
const double GCIdleTimeHandler::kHighContextDisposalRate = 100;
void GCIdleTimeAction::Print() { void GCIdleTimeAction::Print() {
@ -129,14 +130,12 @@ bool GCIdleTimeHandler::ShouldDoMarkCompact(
// (2) If the new space is almost full and we can affort a Scavenge or if the // (2) If the new space is almost full and we can affort a Scavenge or if the
// next Scavenge will very likely take long, then a Scavenge is performed. // 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 // (3) If there is currently no MarkCompact idle round going on, we start a
// new idle round if enough garbage was created or we received a context // new idle round if enough garbage was created. Otherwise we do not perform
// disposal event. Otherwise we do not perform garbage collection to keep // garbage collection to keep system utilization low.
// system utilization low.
// (4) If incremental marking is done, we perform a full garbage collection // (4) If incremental marking is done, we perform a full garbage collection
// if context was disposed or if we are allowed to still do full garbage // if we are allowed to still do full garbage collections during this idle
// collections during this idle round or if we are not allowed to start // round or if we are not allowed to start incremental marking. Otherwise we
// incremental marking. Otherwise we do not perform garbage collection to // do not perform garbage collection to keep system utilization low.
// keep system utilization low.
// (5) If sweeping is in progress and we received a large enough idle time // (5) If sweeping is in progress and we received a large enough idle time
// request, we finalize sweeping here. // request, we finalize sweeping here.
// (6) If incremental marking is in progress, we perform a marking step. Note, // (6) If incremental marking is in progress, we perform a marking step. Note,
@ -146,7 +145,8 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(size_t idle_time_in_ms,
if (idle_time_in_ms == 0) { if (idle_time_in_ms == 0) {
if (heap_state.incremental_marking_stopped) { if (heap_state.incremental_marking_stopped) {
if (heap_state.size_of_objects < kSmallHeapSize && if (heap_state.size_of_objects < kSmallHeapSize &&
heap_state.contexts_disposed > 0) { heap_state.contexts_disposed > 0 &&
heap_state.contexts_disposal_rate < kHighContextDisposalRate) {
return GCIdleTimeAction::FullGC(); return GCIdleTimeAction::FullGC();
} }
} }
@ -162,7 +162,7 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(size_t idle_time_in_ms,
} }
if (IsMarkCompactIdleRoundFinished()) { if (IsMarkCompactIdleRoundFinished()) {
if (EnoughGarbageSinceLastIdleRound() || heap_state.contexts_disposed > 0) { if (EnoughGarbageSinceLastIdleRound()) {
StartIdleRound(); StartIdleRound();
} else { } else {
return GCIdleTimeAction::Done(); return GCIdleTimeAction::Done();
@ -170,11 +170,8 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(size_t idle_time_in_ms,
} }
if (heap_state.incremental_marking_stopped) { if (heap_state.incremental_marking_stopped) {
// TODO(jochen): Remove context disposal dependant logic.
if (ShouldDoMarkCompact(idle_time_in_ms, heap_state.size_of_objects, if (ShouldDoMarkCompact(idle_time_in_ms, heap_state.size_of_objects,
heap_state.mark_compact_speed_in_bytes_per_ms) || heap_state.mark_compact_speed_in_bytes_per_ms)) {
(heap_state.size_of_objects < kSmallHeapSize &&
heap_state.contexts_disposed > 0)) {
// If there are no more than two GCs left in this idle round and we are // If there are no more than two GCs left in this idle round and we are
// allowed to do a full GC, then make those GCs full in order to compact // allowed to do a full GC, then make those GCs full in order to compact
// the code space. // the code space.
@ -182,10 +179,9 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(size_t idle_time_in_ms,
// can get rid of this special case and always start incremental marking. // can get rid of this special case and always start incremental marking.
int remaining_mark_sweeps = int remaining_mark_sweeps =
kMaxMarkCompactsInIdleRound - mark_compacts_since_idle_round_started_; kMaxMarkCompactsInIdleRound - mark_compacts_since_idle_round_started_;
if (heap_state.contexts_disposed > 0 || if (idle_time_in_ms > kMaxFrameRenderingIdleTime &&
(idle_time_in_ms > kMaxFrameRenderingIdleTime && (remaining_mark_sweeps <= 2 ||
(remaining_mark_sweeps <= 2 || !heap_state.can_start_incremental_marking)) {
!heap_state.can_start_incremental_marking))) {
return GCIdleTimeAction::FullGC(); return GCIdleTimeAction::FullGC();
} }
} }

View File

@ -117,8 +117,12 @@ class GCIdleTimeHandler {
// lower bound for the scavenger speed. // lower bound for the scavenger speed.
static const size_t kInitialConservativeScavengeSpeed = 100 * KB; static const size_t kInitialConservativeScavengeSpeed = 100 * KB;
// If contexts are disposed at a higher rate a full gc is triggered.
static const double kHighContextDisposalRate;
struct HeapState { struct HeapState {
int contexts_disposed; int contexts_disposed;
double contexts_disposal_rate;
size_t size_of_objects; size_t size_of_objects;
bool incremental_marking_stopped; bool incremental_marking_stopped;
bool can_start_incremental_marking; bool can_start_incremental_marking;

View File

@ -26,6 +26,11 @@ GCTracer::AllocationEvent::AllocationEvent(double duration,
} }
GCTracer::ContextDisposalEvent::ContextDisposalEvent(double time) {
time_ = time;
}
GCTracer::Event::Event(Type type, const char* gc_reason, GCTracer::Event::Event(Type type, const char* gc_reason,
const char* collector_reason) const char* collector_reason)
: type(type), : type(type),
@ -207,6 +212,11 @@ void GCTracer::AddNewSpaceAllocationTime(double duration,
} }
void GCTracer::AddContextDisposalTime(double time) {
context_disposal_events_.push_front(ContextDisposalEvent(time));
}
void GCTracer::AddIncrementalMarkingStep(double duration, intptr_t bytes) { void GCTracer::AddIncrementalMarkingStep(double duration, intptr_t bytes) {
cumulative_incremental_marking_steps_++; cumulative_incremental_marking_steps_++;
cumulative_incremental_marking_bytes_ += bytes; cumulative_incremental_marking_bytes_ += bytes;
@ -319,6 +329,7 @@ void GCTracer::PrintNVP() const {
PrintF("semi_space_copy_rate=%.1f%% ", heap_->semi_space_copied_rate_); PrintF("semi_space_copy_rate=%.1f%% ", heap_->semi_space_copied_rate_);
PrintF("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ", PrintF("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ",
NewSpaceAllocationThroughputInBytesPerMillisecond()); NewSpaceAllocationThroughputInBytesPerMillisecond());
PrintF("context_disposal_rate=%.1f ", ContextDisposalRateInMilliseconds());
if (current_.type == Event::SCAVENGER) { if (current_.type == Event::SCAVENGER) {
PrintF("steps_count=%d ", current_.incremental_marking_steps); PrintF("steps_count=%d ", current_.incremental_marking_steps);
@ -476,5 +487,21 @@ intptr_t GCTracer::NewSpaceAllocationThroughputInBytesPerMillisecond() const {
return static_cast<intptr_t>(bytes / durations); return static_cast<intptr_t>(bytes / durations);
} }
double GCTracer::ContextDisposalRateInMilliseconds() const {
if (context_disposal_events_.size() == 0) return 0.0;
double begin = context_disposal_events_.begin()->time_;
double end = 0.0;
ContextDisposalEventBuffer::const_iterator iter =
context_disposal_events_.begin();
while (iter != context_disposal_events_.end()) {
end = iter->time_;
++iter;
}
return (begin - end) / context_disposal_events_.size();
}
} }
} // namespace v8::internal } // namespace v8::internal

View File

@ -145,6 +145,19 @@ class GCTracer {
intptr_t allocation_in_bytes_; intptr_t allocation_in_bytes_;
}; };
class ContextDisposalEvent {
public:
// Default constructor leaves the event uninitialized.
ContextDisposalEvent() {}
explicit ContextDisposalEvent(double time);
// Time when context disposal event happened.
double time_;
};
class Event { class Event {
public: public:
enum Type { SCAVENGER = 0, MARK_COMPACTOR = 1, START = 2 }; enum Type { SCAVENGER = 0, MARK_COMPACTOR = 1, START = 2 };
@ -241,6 +254,9 @@ class GCTracer {
typedef RingBuffer<AllocationEvent, kRingBufferMaxSize> AllocationEventBuffer; typedef RingBuffer<AllocationEvent, kRingBufferMaxSize> AllocationEventBuffer;
typedef RingBuffer<ContextDisposalEvent, kRingBufferMaxSize>
ContextDisposalEventBuffer;
explicit GCTracer(Heap* heap); explicit GCTracer(Heap* heap);
// Start collecting data. // Start collecting data.
@ -253,6 +269,8 @@ class GCTracer {
// Log an allocation throughput event. // Log an allocation throughput event.
void AddNewSpaceAllocationTime(double duration, intptr_t allocation_in_bytes); void AddNewSpaceAllocationTime(double duration, intptr_t allocation_in_bytes);
void AddContextDisposalTime(double time);
// Log an incremental marking step. // Log an incremental marking step.
void AddIncrementalMarkingStep(double duration, intptr_t bytes); void AddIncrementalMarkingStep(double duration, intptr_t bytes);
@ -322,6 +340,12 @@ class GCTracer {
// Returns 0 if no events have been recorded. // Returns 0 if no events have been recorded.
intptr_t NewSpaceAllocationThroughputInBytesPerMillisecond() const; intptr_t NewSpaceAllocationThroughputInBytesPerMillisecond() const;
// Computes the context disposal rate in milliseconds. It takes the time
// frame of the first and last context disposal event and devides it by the
// number of recorded events.
// Returns 0 if no events have been recorded.
double ContextDisposalRateInMilliseconds() const;
private: private:
// Print one detailed trace line in name=value format. // Print one detailed trace line in name=value format.
// TODO(ernstm): Move to Heap. // TODO(ernstm): Move to Heap.
@ -359,6 +383,8 @@ class GCTracer {
// RingBuffer for allocation events. // RingBuffer for allocation events.
AllocationEventBuffer allocation_events_; AllocationEventBuffer allocation_events_;
ContextDisposalEventBuffer context_disposal_events_;
// Cumulative number of incremental marking steps since creation of tracer. // Cumulative number of incremental marking steps since creation of tracer.
int cumulative_incremental_marking_steps_; int cumulative_incremental_marking_steps_;

View File

@ -865,6 +865,7 @@ int Heap::NotifyContextDisposed() {
} }
flush_monomorphic_ics_ = true; flush_monomorphic_ics_ = true;
AgeInlineCaches(); AgeInlineCaches();
tracer()->AddContextDisposalTime(base::OS::TimeCurrentMillis());
return ++contexts_disposed_; return ++contexts_disposed_;
} }
@ -4363,6 +4364,8 @@ bool Heap::IdleNotification(int idle_time_in_ms) {
GCIdleTimeHandler::HeapState heap_state; GCIdleTimeHandler::HeapState heap_state;
heap_state.contexts_disposed = contexts_disposed_; heap_state.contexts_disposed = contexts_disposed_;
heap_state.contexts_disposal_rate =
tracer()->ContextDisposalRateInMilliseconds();
heap_state.size_of_objects = static_cast<size_t>(SizeOfObjects()); heap_state.size_of_objects = static_cast<size_t>(SizeOfObjects());
heap_state.incremental_marking_stopped = incremental_marking()->IsStopped(); heap_state.incremental_marking_stopped = incremental_marking()->IsStopped();
// TODO(ulan): Start incremental marking only for large heaps. // TODO(ulan): Start incremental marking only for large heaps.

View File

@ -22,6 +22,7 @@ class GCIdleTimeHandlerTest : public ::testing::Test {
GCIdleTimeHandler::HeapState DefaultHeapState() { GCIdleTimeHandler::HeapState DefaultHeapState() {
GCIdleTimeHandler::HeapState result; GCIdleTimeHandler::HeapState result;
result.contexts_disposed = 0; result.contexts_disposed = 0;
result.contexts_disposal_rate = GCIdleTimeHandler::kHighContextDisposalRate;
result.size_of_objects = kSizeOfObjects; result.size_of_objects = kSizeOfObjects;
result.incremental_marking_stopped = false; result.incremental_marking_stopped = false;
result.can_start_incremental_marking = true; result.can_start_incremental_marking = true;
@ -179,10 +180,34 @@ TEST_F(GCIdleTimeHandlerTest, DontDoMarkCompact) {
} }
TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeLargeIdleTime) { TEST_F(GCIdleTimeHandlerTest, ContextDisposeLowRate) {
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
heap_state.contexts_disposed = 1; heap_state.contexts_disposed = 1;
heap_state.incremental_marking_stopped = true; heap_state.incremental_marking_stopped = true;
int idle_time_ms = 0;
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
EXPECT_EQ(DO_NOTHING, action.type);
}
TEST_F(GCIdleTimeHandlerTest, ContextDisposeHighRate) {
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
heap_state.contexts_disposed = 1;
heap_state.contexts_disposal_rate =
GCIdleTimeHandler::kHighContextDisposalRate - 1;
heap_state.incremental_marking_stopped = true;
int idle_time_ms = 0;
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
EXPECT_EQ(DO_NOTHING, 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; size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
int idle_time_ms = int idle_time_ms =
static_cast<int>((heap_state.size_of_objects + speed - 1) / speed); static_cast<int>((heap_state.size_of_objects + speed - 1) / speed);
@ -194,6 +219,7 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeLargeIdleTime) {
TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeZeroIdleTime) { TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeZeroIdleTime) {
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
heap_state.contexts_disposed = 1; heap_state.contexts_disposed = 1;
heap_state.contexts_disposal_rate = 1.0;
heap_state.incremental_marking_stopped = true; heap_state.incremental_marking_stopped = true;
heap_state.size_of_objects = GCIdleTimeHandler::kSmallHeapSize / 2; heap_state.size_of_objects = GCIdleTimeHandler::kSmallHeapSize / 2;
int idle_time_ms = 0; int idle_time_ms = 0;
@ -205,6 +231,7 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeZeroIdleTime) {
TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) { TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) {
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
heap_state.contexts_disposed = 1; heap_state.contexts_disposed = 1;
heap_state.contexts_disposal_rate = 1.0;
heap_state.incremental_marking_stopped = true; heap_state.incremental_marking_stopped = true;
size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed - 1); int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed - 1);
@ -216,6 +243,7 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) {
TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime2) { TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime2) {
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
heap_state.contexts_disposed = 1; heap_state.contexts_disposed = 1;
heap_state.contexts_disposal_rate = 1.0;
size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms;
int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed - 1); int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed - 1);
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);