const size_t GCIdleTimeHandler::kMinTimeForFinalizeSweeping = 100;
const int GCIdleTimeHandler::kMaxMarkCompactsInIdleRound = 7;
const int GCIdleTimeHandler::kIdleScavengeThreshold = 5;
+const double GCIdleTimeHandler::kHighContextDisposalRate = 100;
void GCIdleTimeAction::Print() {
// (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,
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();
}
}
}
if (IsMarkCompactIdleRoundFinished()) {
- if (EnoughGarbageSinceLastIdleRound() || heap_state.contexts_disposed > 0) {
+ if (EnoughGarbageSinceLastIdleRound()) {
StartIdleRound();
} else {
return GCIdleTimeAction::Done();
}
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.
// 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();
}
}
// 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;
}
+GCTracer::ContextDisposalEvent::ContextDisposalEvent(double time) {
+ time_ = time;
+}
+
+
GCTracer::Event::Event(Type type, const char* gc_reason,
const char* collector_reason)
: type(type),
}
+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;
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);
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
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 };
typedef RingBuffer<AllocationEvent, kRingBufferMaxSize> AllocationEventBuffer;
+ typedef RingBuffer<ContextDisposalEvent, kRingBufferMaxSize>
+ ContextDisposalEventBuffer;
+
explicit GCTracer(Heap* heap);
// Start collecting data.
// 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);
// 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.
// 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_;
}
flush_monomorphic_ics_ = true;
AgeInlineCaches();
+ tracer()->AddContextDisposalTime(base::OS::TimeCurrentMillis());
return ++contexts_disposed_;
}
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.
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;
}
+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);
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;
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);
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);