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 int GCIdleTimeHandler::kMaxMarkCompactsInIdleRound = 7;
|
||||
const int GCIdleTimeHandler::kIdleScavengeThreshold = 5;
|
||||
const double GCIdleTimeHandler::kHighContextDisposalRate = 100;
|
||||
|
||||
|
||||
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
|
||||
// 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 or we received a context
|
||||
// disposal event. Otherwise we do not perform garbage collection to keep
|
||||
// system utilization low.
|
||||
// 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
|
||||
// if context was disposed or 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.
|
||||
// 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
|
||||
// request, we finalize sweeping here.
|
||||
// (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 (heap_state.incremental_marking_stopped) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -162,7 +162,7 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(size_t idle_time_in_ms,
|
||||
}
|
||||
|
||||
if (IsMarkCompactIdleRoundFinished()) {
|
||||
if (EnoughGarbageSinceLastIdleRound() || heap_state.contexts_disposed > 0) {
|
||||
if (EnoughGarbageSinceLastIdleRound()) {
|
||||
StartIdleRound();
|
||||
} else {
|
||||
return GCIdleTimeAction::Done();
|
||||
@ -170,11 +170,8 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(size_t idle_time_in_ms,
|
||||
}
|
||||
|
||||
if (heap_state.incremental_marking_stopped) {
|
||||
// TODO(jochen): Remove context disposal dependant logic.
|
||||
if (ShouldDoMarkCompact(idle_time_in_ms, heap_state.size_of_objects,
|
||||
heap_state.mark_compact_speed_in_bytes_per_ms) ||
|
||||
(heap_state.size_of_objects < kSmallHeapSize &&
|
||||
heap_state.contexts_disposed > 0)) {
|
||||
heap_state.mark_compact_speed_in_bytes_per_ms)) {
|
||||
// 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
|
||||
// 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.
|
||||
int remaining_mark_sweeps =
|
||||
kMaxMarkCompactsInIdleRound - mark_compacts_since_idle_round_started_;
|
||||
if (heap_state.contexts_disposed > 0 ||
|
||||
(idle_time_in_ms > kMaxFrameRenderingIdleTime &&
|
||||
(remaining_mark_sweeps <= 2 ||
|
||||
!heap_state.can_start_incremental_marking))) {
|
||||
if (idle_time_in_ms > kMaxFrameRenderingIdleTime &&
|
||||
(remaining_mark_sweeps <= 2 ||
|
||||
!heap_state.can_start_incremental_marking)) {
|
||||
return GCIdleTimeAction::FullGC();
|
||||
}
|
||||
}
|
||||
|
@ -117,8 +117,12 @@ class GCIdleTimeHandler {
|
||||
// lower bound for the scavenger speed.
|
||||
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 {
|
||||
int contexts_disposed;
|
||||
double contexts_disposal_rate;
|
||||
size_t size_of_objects;
|
||||
bool incremental_marking_stopped;
|
||||
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,
|
||||
const char* collector_reason)
|
||||
: 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) {
|
||||
cumulative_incremental_marking_steps_++;
|
||||
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("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ",
|
||||
NewSpaceAllocationThroughputInBytesPerMillisecond());
|
||||
PrintF("context_disposal_rate=%.1f ", ContextDisposalRateInMilliseconds());
|
||||
|
||||
if (current_.type == Event::SCAVENGER) {
|
||||
PrintF("steps_count=%d ", current_.incremental_marking_steps);
|
||||
@ -476,5 +487,21 @@ intptr_t GCTracer::NewSpaceAllocationThroughputInBytesPerMillisecond() const {
|
||||
|
||||
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
|
||||
|
@ -145,6 +145,19 @@ class GCTracer {
|
||||
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 {
|
||||
public:
|
||||
enum Type { SCAVENGER = 0, MARK_COMPACTOR = 1, START = 2 };
|
||||
@ -241,6 +254,9 @@ class GCTracer {
|
||||
|
||||
typedef RingBuffer<AllocationEvent, kRingBufferMaxSize> AllocationEventBuffer;
|
||||
|
||||
typedef RingBuffer<ContextDisposalEvent, kRingBufferMaxSize>
|
||||
ContextDisposalEventBuffer;
|
||||
|
||||
explicit GCTracer(Heap* heap);
|
||||
|
||||
// Start collecting data.
|
||||
@ -253,6 +269,8 @@ class GCTracer {
|
||||
// Log an allocation throughput event.
|
||||
void AddNewSpaceAllocationTime(double duration, intptr_t allocation_in_bytes);
|
||||
|
||||
void AddContextDisposalTime(double time);
|
||||
|
||||
// Log an incremental marking step.
|
||||
void AddIncrementalMarkingStep(double duration, intptr_t bytes);
|
||||
|
||||
@ -322,6 +340,12 @@ class GCTracer {
|
||||
// Returns 0 if no events have been recorded.
|
||||
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:
|
||||
// Print one detailed trace line in name=value format.
|
||||
// TODO(ernstm): Move to Heap.
|
||||
@ -359,6 +383,8 @@ class GCTracer {
|
||||
// RingBuffer for allocation events.
|
||||
AllocationEventBuffer allocation_events_;
|
||||
|
||||
ContextDisposalEventBuffer context_disposal_events_;
|
||||
|
||||
// Cumulative number of incremental marking steps since creation of tracer.
|
||||
int cumulative_incremental_marking_steps_;
|
||||
|
||||
|
@ -865,6 +865,7 @@ int Heap::NotifyContextDisposed() {
|
||||
}
|
||||
flush_monomorphic_ics_ = true;
|
||||
AgeInlineCaches();
|
||||
tracer()->AddContextDisposalTime(base::OS::TimeCurrentMillis());
|
||||
return ++contexts_disposed_;
|
||||
}
|
||||
|
||||
@ -4363,6 +4364,8 @@ bool Heap::IdleNotification(int idle_time_in_ms) {
|
||||
|
||||
GCIdleTimeHandler::HeapState heap_state;
|
||||
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.incremental_marking_stopped = incremental_marking()->IsStopped();
|
||||
// TODO(ulan): Start incremental marking only for large heaps.
|
||||
|
@ -22,6 +22,7 @@ class GCIdleTimeHandlerTest : public ::testing::Test {
|
||||
GCIdleTimeHandler::HeapState DefaultHeapState() {
|
||||
GCIdleTimeHandler::HeapState result;
|
||||
result.contexts_disposed = 0;
|
||||
result.contexts_disposal_rate = GCIdleTimeHandler::kHighContextDisposalRate;
|
||||
result.size_of_objects = kSizeOfObjects;
|
||||
result.incremental_marking_stopped = false;
|
||||
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();
|
||||
heap_state.contexts_disposed = 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, 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;
|
||||
int idle_time_ms =
|
||||
static_cast<int>((heap_state.size_of_objects + speed - 1) / speed);
|
||||
@ -194,6 +219,7 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeLargeIdleTime) {
|
||||
TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeZeroIdleTime) {
|
||||
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.size_of_objects = GCIdleTimeHandler::kSmallHeapSize / 2;
|
||||
int idle_time_ms = 0;
|
||||
@ -205,6 +231,7 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeZeroIdleTime) {
|
||||
TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) {
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
heap_state.contexts_disposed = 1;
|
||||
heap_state.contexts_disposal_rate = 1.0;
|
||||
heap_state.incremental_marking_stopped = true;
|
||||
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);
|
||||
@ -216,6 +243,7 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) {
|
||||
TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime2) {
|
||||
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
|
||||
heap_state.contexts_disposed = 1;
|
||||
heap_state.contexts_disposal_rate = 1.0;
|
||||
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);
|
||||
GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
|
||||
|
Loading…
Reference in New Issue
Block a user