Perform context disposal garbage collections only if contexts disposals happen at...
authorhpayer@chromium.org <hpayer@chromium.org>
Fri, 7 Nov 2014 09:36:49 +0000 (09:36 +0000)
committerhpayer@chromium.org <hpayer@chromium.org>
Fri, 7 Nov 2014 09:37:11 +0000 (09:37 +0000)
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

src/heap/gc-idle-time-handler.cc
src/heap/gc-idle-time-handler.h
src/heap/gc-tracer.cc
src/heap/gc-tracer.h
src/heap/heap.cc
test/unittests/heap/gc-idle-time-handler-unittest.cc

index b352f44..b3350d1 100644 (file)
@@ -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();
       }
     }
index edd5e42..1e87e9d 100644 (file)
@@ -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;
index 8a40b53..9d4a991 100644 (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,
                        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
index 4e70f07..2655f1f 100644 (file)
@@ -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_;
 
index 166dd3a..030744d 100644 (file)
@@ -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.
index 55dd6c6..2b476f7 100644 (file)
@@ -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, 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);