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:
parent
98d4a8bd31
commit
c0ac2ab9d0
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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_;
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user