Move idle notification handling to GCIdleTimeHandler.
authorulan@chromium.org <ulan@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 21 Aug 2014 14:42:22 +0000 (14:42 +0000)
committerulan@chromium.org <ulan@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 21 Aug 2014 14:42:22 +0000 (14:42 +0000)
BUG=
R=hpayer@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23282 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
src/heap/heap.h
test/heap-unittests/heap-unittest.cc

index 5ff4017..a2e08b2 100644 (file)
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 
 #include "src/heap/gc-idle-time-handler.h"
+#include "src/heap/gc-tracer.h"
+#include "src/utils.h"
 
 namespace v8 {
 namespace internal {
 
-
 const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9;
+const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000000;
 
 
 size_t GCIdleTimeHandler::EstimateMarkingStepSize(
@@ -16,14 +18,13 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize(
   DCHECK(idle_time_in_ms > 0);
 
   if (marking_speed_in_bytes_per_ms == 0) {
-    marking_speed_in_bytes_per_ms =
-        GCIdleTimeHandler::kInitialConservativeMarkingSpeed;
+    marking_speed_in_bytes_per_ms = kInitialConservativeMarkingSpeed;
   }
 
   size_t marking_step_size = marking_speed_in_bytes_per_ms * idle_time_in_ms;
   if (marking_step_size / marking_speed_in_bytes_per_ms != idle_time_in_ms) {
     // In the case of an overflow we return maximum marking step size.
-    return GCIdleTimeHandler::kMaximumMarkingStepSize;
+    return kMaximumMarkingStepSize;
   }
 
   if (marking_step_size > kMaximumMarkingStepSize)
@@ -32,5 +33,51 @@ size_t GCIdleTimeHandler::EstimateMarkingStepSize(
   return static_cast<size_t>(marking_step_size *
                              GCIdleTimeHandler::kConservativeTimeRatio);
 }
+
+
+size_t GCIdleTimeHandler::EstimateMarkCompactTime(
+    size_t size_of_objects, size_t mark_compact_speed_in_bytes_per_ms) {
+  if (mark_compact_speed_in_bytes_per_ms == 0) {
+    mark_compact_speed_in_bytes_per_ms = kInitialConservativeMarkCompactSpeed;
+  }
+  size_t result = size_of_objects / mark_compact_speed_in_bytes_per_ms;
+  return Min(result, kMaxMarkCompactTimeInMs);
+}
+
+
+GCIdleTimeAction GCIdleTimeHandler::Compute(int idle_time_in_ms,
+                                            int contexts_disposed,
+                                            size_t size_of_objects,
+                                            bool incremental_marking_stopped,
+                                            GCTracer* gc_tracer) {
+  if (IsIdleRoundFinished()) {
+    if (EnoughGarbageSinceLastIdleRound() || contexts_disposed > 0) {
+      StartIdleRound();
+    } else {
+      return GCIdleTimeAction::Nothing();
+    }
+  }
+  if (incremental_marking_stopped) {
+    size_t speed =
+        static_cast<size_t>(gc_tracer->MarkCompactSpeedInBytesPerMillisecond());
+    if (idle_time_in_ms >=
+        static_cast<int>(EstimateMarkCompactTime(size_of_objects, speed))) {
+      // 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.
+      // TODO(ulan): Once we enable code compaction for incremental marking, we
+      // can get rid of this special case and always start incremental marking.
+      int remaining_mark_sweeps =
+          kMaxMarkCompactsInIdleRound - mark_compacts_since_idle_round_started_;
+      if (contexts_disposed > 0 || remaining_mark_sweeps <= 2) {
+        return GCIdleTimeAction::FullGC();
+      }
+    }
+  }
+  intptr_t speed = gc_tracer->IncrementalMarkingSpeedInBytesPerMillisecond();
+  size_t step_size =
+      static_cast<size_t>(EstimateMarkingStepSize(idle_time_in_ms, speed));
+  return GCIdleTimeAction::IncrementalMarking(step_size);
+}
 }
 }
index 609fdb1..76cdb9f 100644 (file)
 namespace v8 {
 namespace internal {
 
+enum GCIdleTimeActionType {
+  DO_NOTHING,
+  DO_INCREMENTAL_MARKING,
+  DO_SCAVENGE,
+  DO_FULL_GC
+};
+
+
+class GCIdleTimeAction {
+ public:
+  static GCIdleTimeAction Nothing() {
+    GCIdleTimeAction result;
+    result.type = DO_NOTHING;
+    result.parameter = 0;
+    return result;
+  }
+  static GCIdleTimeAction IncrementalMarking(intptr_t step_size) {
+    GCIdleTimeAction result;
+    result.type = DO_INCREMENTAL_MARKING;
+    result.parameter = step_size;
+    return result;
+  }
+  static GCIdleTimeAction Scavenge() {
+    GCIdleTimeAction result;
+    result.type = DO_SCAVENGE;
+    result.parameter = 0;
+    return result;
+  }
+  static GCIdleTimeAction FullGC() {
+    GCIdleTimeAction result;
+    result.type = DO_FULL_GC;
+    result.parameter = 0;
+    return result;
+  }
+
+  GCIdleTimeActionType type;
+  intptr_t parameter;
+};
+
+class GCTracer;
+
 // The idle time handler makes decisions about which garbage collection
 // operations are executing during IdleNotification.
 class GCIdleTimeHandler {
  public:
-  static size_t EstimateMarkingStepSize(size_t idle_time_in_ms,
-                                        size_t marking_speed_in_bytes_per_ms);
-
   // If we haven't recorded any incremental marking events yet, we carefully
   // mark with a conservative lower bound for the marking speed.
   static const size_t kInitialConservativeMarkingSpeed = 100 * KB;
@@ -28,7 +66,55 @@ class GCIdleTimeHandler {
   // idle_time_in_ms. Hence, we conservatively prune our workload estimate.
   static const double kConservativeTimeRatio;
 
+  // If we haven't recorded any mark-compact events yet, we use
+  // conservative lower bound for the mark-compact speed.
+  static const size_t kInitialConservativeMarkCompactSpeed = 2 * MB;
+
+  // Maximum mark-compact time returned by EstimateMarkCompactTime.
+  static const size_t kMaxMarkCompactTimeInMs;
+
+  GCIdleTimeHandler()
+      : mark_compacts_since_idle_round_started_(0),
+        scavenges_since_last_idle_round_(0) {}
+
+  GCIdleTimeAction Compute(int idle_time_in_ms, int contexts_disposed,
+                           size_t size_of_objects,
+                           bool incremental_marking_stopped,
+                           GCTracer* gc_tracer);
+
+  void NotifyIdleMarkCompact() {
+    if (mark_compacts_since_idle_round_started_ < kMaxMarkCompactsInIdleRound) {
+      ++mark_compacts_since_idle_round_started_;
+      if (mark_compacts_since_idle_round_started_ ==
+          kMaxMarkCompactsInIdleRound) {
+        scavenges_since_last_idle_round_ = 0;
+      }
+    }
+  }
+
+  void NotifyScavenge() { ++scavenges_since_last_idle_round_; }
+
+  static size_t EstimateMarkingStepSize(size_t idle_time_in_ms,
+                                        size_t marking_speed_in_bytes_per_ms);
+
+  static size_t EstimateMarkCompactTime(
+      size_t size_of_objects, size_t mark_compact_speed_in_bytes_per_ms);
+
  private:
+  void StartIdleRound() { mark_compacts_since_idle_round_started_ = 0; }
+  bool IsIdleRoundFinished() {
+    return mark_compacts_since_idle_round_started_ ==
+           kMaxMarkCompactsInIdleRound;
+  }
+  bool EnoughGarbageSinceLastIdleRound() {
+    return scavenges_since_last_idle_round_ >= kIdleScavengeThreshold;
+  }
+
+  static const int kMaxMarkCompactsInIdleRound = 7;
+  static const int kIdleScavengeThreshold = 5;
+  int mark_compacts_since_idle_round_started_;
+  int scavenges_since_last_idle_round_;
+
   DISALLOW_COPY_AND_ASSIGN(GCIdleTimeHandler);
 };
 
index a0f1c46..035e3ee 100644 (file)
@@ -418,5 +418,22 @@ intptr_t GCTracer::ScavengeSpeedInBytesPerMillisecond() const {
 
   return static_cast<intptr_t>(bytes / durations);
 }
+
+
+intptr_t GCTracer::MarkCompactSpeedInBytesPerMillisecond() const {
+  intptr_t bytes = 0;
+  double durations = 0.0;
+  EventBuffer::const_iterator iter = mark_compactor_events_.begin();
+  while (iter != mark_compactor_events_.end()) {
+    bytes += iter->start_object_size;
+    durations += iter->end_time - iter->start_time +
+                 iter->pure_incremental_marking_duration;
+    ++iter;
+  }
+
+  if (durations == 0.0) return 0;
+
+  return static_cast<intptr_t>(bytes / durations);
+}
 }
 }  // namespace v8::internal
index fd53d3c..0524f25 100644 (file)
@@ -5,6 +5,8 @@
 #ifndef V8_HEAP_GC_TRACER_H_
 #define V8_HEAP_GC_TRACER_H_
 
+#include "src/base/platform/platform.h"
+
 namespace v8 {
 namespace internal {
 
@@ -81,9 +83,9 @@ class RingBuffer {
 // GCTracer collects and prints ONE line after each garbage collector
 // invocation IFF --trace_gc is used.
 // TODO(ernstm): Unit tests.
-class GCTracer BASE_EMBEDDED {
+class GCTracer {
  public:
-  class Scope BASE_EMBEDDED {
+  class Scope {
    public:
     enum ScopeId {
       EXTERNAL,
@@ -291,6 +293,10 @@ class GCTracer BASE_EMBEDDED {
   // Returns 0 if no events have been recorded.
   intptr_t ScavengeSpeedInBytesPerMillisecond() const;
 
+  // Compute the max mark-sweep speed in bytes/millisecond.
+  // Returns 0 if no events have been recorded.
+  intptr_t MarkCompactSpeedInBytesPerMillisecond() const;
+
  private:
   // Print one detailed trace line in name=value format.
   // TODO(ernstm): Move to Heap.
index 559f761..7604664 100644 (file)
@@ -120,12 +120,7 @@ Heap::Heap()
       store_buffer_(this),
       marking_(this),
       incremental_marking_(this),
-      number_idle_notifications_(0),
-      last_idle_notification_gc_count_(0),
-      last_idle_notification_gc_count_init_(false),
-      mark_sweeps_since_idle_round_started_(0),
       gc_count_at_last_idle_gc_(0),
-      scavenges_since_last_idle_round_(kIdleScavengeThreshold),
       full_codegen_bytes_generated_(0),
       crankshaft_codegen_bytes_generated_(0),
       gcs_since_last_deopt_(0),
@@ -1559,7 +1554,7 @@ void Heap::Scavenge() {
 
   gc_state_ = NOT_IN_GC;
 
-  scavenges_since_last_idle_round_++;
+  gc_idle_time_handler_.NotifyScavenge();
 }
 
 
@@ -4265,12 +4260,7 @@ void Heap::MakeHeapIterable() {
 }
 
 
-void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
-  intptr_t step_size =
-      static_cast<size_t>(GCIdleTimeHandler::EstimateMarkingStepSize(
-          idle_time_in_ms,
-          tracer_.IncrementalMarkingSpeedInBytesPerMillisecond()));
-
+void Heap::AdvanceIdleIncrementalMarking(intptr_t step_size) {
   incremental_marking()->Step(step_size,
                               IncrementalMarking::NO_GC_VIA_STACK_GUARD, true);
 
@@ -4283,7 +4273,7 @@ void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
     }
     CollectAllGarbage(kReduceMemoryFootprintMask,
                       "idle notification: finalize incremental");
-    mark_sweeps_since_idle_round_started_++;
+    gc_idle_time_handler_.NotifyIdleMarkCompact();
     gc_count_at_last_idle_gc_ = gc_count_;
     if (uncommit) {
       new_space_.Shrink();
@@ -4296,83 +4286,49 @@ void Heap::AdvanceIdleIncrementalMarking(int idle_time_in_ms) {
 bool Heap::IdleNotification(int idle_time_in_ms) {
   // If incremental marking is off, we do not perform idle notification.
   if (!FLAG_incremental_marking) return true;
-
-  // Minimal hint that allows to do full GC.
-  const int kMinHintForFullGC = 100;
   isolate()->counters()->gc_idle_time_allotted_in_ms()->AddSample(
       idle_time_in_ms);
   HistogramTimerScope idle_notification_scope(
       isolate_->counters()->gc_idle_notification());
 
-  if (contexts_disposed_ > 0) {
-    contexts_disposed_ = 0;
-    int mark_sweep_time = Min(TimeMarkSweepWouldTakeInMs(), 1000);
-    if (idle_time_in_ms >= mark_sweep_time && !FLAG_expose_gc &&
-        incremental_marking()->IsStopped()) {
+  GCIdleTimeAction action = gc_idle_time_handler_.Compute(
+      idle_time_in_ms, contexts_disposed_, static_cast<size_t>(SizeOfObjects()),
+      incremental_marking()->IsStopped(), tracer());
+  contexts_disposed_ = 0;
+  bool result = false;
+  switch (action.type) {
+    case DO_INCREMENTAL_MARKING:
+      if (incremental_marking()->IsStopped()) {
+        incremental_marking()->Start();
+      }
+      AdvanceIdleIncrementalMarking(action.parameter);
+      break;
+    case DO_FULL_GC: {
       HistogramTimerScope scope(isolate_->counters()->gc_context());
-      CollectAllGarbage(kReduceMemoryFootprintMask,
-                        "idle notification: contexts disposed");
-    } else {
-      AdvanceIdleIncrementalMarking(idle_time_in_ms);
-    }
-
-    // After context disposal there is likely a lot of garbage remaining, reset
-    // the idle notification counters in order to trigger more incremental GCs
-    // on subsequent idle notifications.
-    StartIdleRound();
-    return false;
-  }
-
-  // By doing small chunks of GC work in each IdleNotification,
-  // perform a round of incremental GCs and after that wait until
-  // the mutator creates enough garbage to justify a new round.
-  // An incremental GC progresses as follows:
-  // 1. many incremental marking steps,
-  // 2. one old space mark-sweep-compact,
-  // Use mark-sweep-compact events to count incremental GCs in a round.
-
-  if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
-    if (EnoughGarbageSinceLastIdleRound()) {
-      StartIdleRound();
-    } else {
-      return true;
-    }
-  }
-
-  int remaining_mark_sweeps =
-      kMaxMarkSweepsInIdleRound - mark_sweeps_since_idle_round_started_;
-
-  if (incremental_marking()->IsStopped()) {
-    // 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.
-    // TODO(ulan): Once we enable code compaction for incremental marking,
-    // we can get rid of this special case and always start incremental marking.
-    if (remaining_mark_sweeps <= 2 && idle_time_in_ms >= kMinHintForFullGC) {
-      CollectAllGarbage(kReduceMemoryFootprintMask,
-                        "idle notification: finalize idle round");
-      mark_sweeps_since_idle_round_started_++;
-    } else {
-      incremental_marking()->Start();
+      const char* message = contexts_disposed_
+                                ? "idle notification: contexts disposed"
+                                : "idle notification: finalize idle round";
+      CollectAllGarbage(kReduceMemoryFootprintMask, message);
+      gc_idle_time_handler_.NotifyIdleMarkCompact();
+      break;
     }
+    case DO_SCAVENGE:
+      CollectGarbage(NEW_SPACE, "idle notification: scavenge");
+      break;
+    case DO_NOTHING:
+      result = true;
+      break;
   }
-  if (!incremental_marking()->IsStopped()) {
-    AdvanceIdleIncrementalMarking(idle_time_in_ms);
-  }
-
-  if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
-    FinishIdleRound();
-    return true;
-  }
-
   // If the IdleNotifcation is called with a large hint we will wait for
   // the sweepter threads here.
+  // TODO(ulan): move this in GCIdleTimeHandler.
+  const int kMinHintForFullGC = 100;
   if (idle_time_in_ms >= kMinHintForFullGC &&
       mark_compact_collector()->sweeping_in_progress()) {
     mark_compact_collector()->EnsureSweepingCompleted();
   }
 
-  return false;
+  return result;
 }
 
 
index 5075b05..6886ddd 100644 (file)
@@ -11,6 +11,7 @@
 #include "src/assert-scope.h"
 #include "src/counters.h"
 #include "src/globals.h"
+#include "src/heap/gc-idle-time-handler.h"
 #include "src/heap/gc-tracer.h"
 #include "src/heap/incremental-marking.h"
 #include "src/heap/mark-compact.h"
@@ -1929,30 +1930,7 @@ class Heap {
 
   void SelectScavengingVisitorsTable();
 
-  void StartIdleRound() { mark_sweeps_since_idle_round_started_ = 0; }
-
-  void FinishIdleRound() {
-    mark_sweeps_since_idle_round_started_ = kMaxMarkSweepsInIdleRound;
-    scavenges_since_last_idle_round_ = 0;
-  }
-
-  bool EnoughGarbageSinceLastIdleRound() {
-    return (scavenges_since_last_idle_round_ >= kIdleScavengeThreshold);
-  }
-
-  // Estimates how many milliseconds a Mark-Sweep would take to complete.
-  // In idle notification handler we assume that this function will return:
-  // - a number less than 10 for small heaps, which are less than 8Mb.
-  // - a number greater than 10 for large heaps, which are greater than 32Mb.
-  int TimeMarkSweepWouldTakeInMs() {
-    // Rough estimate of how many megabytes of heap can be processed in 1 ms.
-    static const int kMbPerMs = 2;
-
-    int heap_size_mb = static_cast<int>(SizeOfObjects() / MB);
-    return heap_size_mb / kMbPerMs;
-  }
-
-  void AdvanceIdleIncrementalMarking(int idle_time_in_ms);
+  void AdvanceIdleIncrementalMarking(intptr_t step_size);
 
   void ClearObjectStats(bool clear_last_time_stats = false);
 
@@ -2005,13 +1983,8 @@ class Heap {
 
   IncrementalMarking incremental_marking_;
 
-  int number_idle_notifications_;
-  unsigned int last_idle_notification_gc_count_;
-  bool last_idle_notification_gc_count_init_;
-
-  int mark_sweeps_since_idle_round_started_;
+  GCIdleTimeHandler gc_idle_time_handler_;
   unsigned int gc_count_at_last_idle_gc_;
-  int scavenges_since_last_idle_round_;
 
   // These two counters are monotomically increasing and never reset.
   size_t full_codegen_bytes_generated_;
@@ -2029,7 +2002,7 @@ class Heap {
   static const int kAllocationSiteScratchpadSize = 256;
   int allocation_sites_scratchpad_length_;
 
-  static const int kMaxMarkSweepsInIdleRound = 7;
+  static const int kMaxMarkCompactsInIdleRound = 7;
   static const int kIdleScavengeThreshold = 5;
 
   // Shared state read by the scavenge collector and set by ScavengeObject.
index db331f9..f51908b 100644 (file)
@@ -45,5 +45,29 @@ TEST(EstimateMarkingStepSizeTest, EstimateMarkingStepSizeOverflow2) {
             step_size);
 }
 
+
+TEST(EstimateMarkCompactTimeTest, EstimateMarkCompactTimeInitial) {
+  size_t size = 100 * MB;
+  size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, 0);
+  EXPECT_EQ(size / GCIdleTimeHandler::kInitialConservativeMarkCompactSpeed,
+            time);
+}
+
+
+TEST(EstimateMarkCompactTimeTest, EstimateMarkCompactTimeNonZero) {
+  size_t size = 100 * MB;
+  size_t speed = 10 * KB;
+  size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, speed);
+  EXPECT_EQ(size / speed, time);
+}
+
+
+TEST(EstimateMarkCompactTimeTest, EstimateMarkCompactTimeMax) {
+  size_t size = std::numeric_limits<size_t>::max();
+  size_t speed = 1;
+  size_t time = GCIdleTimeHandler::EstimateMarkCompactTime(size, speed);
+  EXPECT_EQ(GCIdleTimeHandler::kMaxMarkCompactTimeInMs, time);
+}
+
 }  // namespace internal
 }  // namespace v8