Tenure allocation sites only when semi-space is maximum size.
authorhpayer@chromium.org <hpayer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Jun 2014 07:55:38 +0000 (07:55 +0000)
committerhpayer@chromium.org <hpayer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Jun 2014 07:55:38 +0000 (07:55 +0000)
BUG=
R=mvstanton@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21623 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/heap.cc
src/heap.h
src/mark-compact.cc
src/objects-inl.h
src/objects.cc
src/objects.h
src/spaces.h
test/cctest/test-heap.cc

index ff7a9ff..c5ea15a 100644 (file)
@@ -101,6 +101,7 @@ Heap::Heap()
       promotion_rate_(0),
       semi_space_copied_object_size_(0),
       semi_space_copied_rate_(0),
+      maximum_size_scavenges_(0),
       max_gc_pause_(0.0),
       total_gc_time_ms_(0.0),
       max_alive_after_gc_(0),
@@ -438,6 +439,13 @@ void Heap::GarbageCollectionPrologue() {
   if (isolate()->concurrent_osr_enabled()) {
     isolate()->optimizing_compiler_thread()->AgeBufferedOsrJobs();
   }
+
+  if (new_space_.IsAtMaximumCapacity()) {
+    maximum_size_scavenges_++;
+  } else {
+    maximum_size_scavenges_ = 0;
+  }
+  CheckNewSpaceExpansionCriteria();
 }
 
 
@@ -485,12 +493,19 @@ void Heap::ProcessPretenuringFeedback() {
 
     // If the scratchpad overflowed, we have to iterate over the allocation
     // sites list.
+    // TODO(hpayer): We iterate over the whole list of allocation sites when
+    // we grew to the maximum semi-space size to deopt maybe tenured
+    // allocation sites. We could hold the maybe tenured allocation sites
+    // in a seperate data structure if this is a performance problem.
     bool use_scratchpad =
-        allocation_sites_scratchpad_length_ < kAllocationSiteScratchpadSize;
+        allocation_sites_scratchpad_length_ < kAllocationSiteScratchpadSize &&
+        new_space_.IsAtMaximumCapacity() &&
+        maximum_size_scavenges_ == 0;
 
     int i = 0;
     Object* list_element = allocation_sites_list();
     bool trigger_deoptimization = false;
+    bool maximum_size_scavenge = MaximumSizeScavenge();
     while (use_scratchpad ?
               i < allocation_sites_scratchpad_length_ :
               list_element->IsAllocationSite()) {
@@ -500,14 +515,17 @@ void Heap::ProcessPretenuringFeedback() {
       allocation_mementos_found += site->memento_found_count();
       if (site->memento_found_count() > 0) {
         active_allocation_sites++;
+        if (site->DigestPretenuringFeedback(maximum_size_scavenge)) {
+          trigger_deoptimization = true;
+        }
+        if (site->GetPretenureMode() == TENURED) {
+          tenure_decisions++;
+        } else {
+          dont_tenure_decisions++;
+        }
+        allocation_sites++;
       }
-      if (site->DigestPretenuringFeedback()) trigger_deoptimization = true;
-      if (site->GetPretenureMode() == TENURED) {
-        tenure_decisions++;
-      } else {
-        dont_tenure_decisions++;
-      }
-      allocation_sites++;
+
       if (use_scratchpad) {
         i++;
       } else {
@@ -1433,8 +1451,6 @@ void Heap::Scavenge() {
   // Used for updating survived_since_last_expansion_ at function end.
   intptr_t survived_watermark = PromotedSpaceSizeOfObjects();
 
-  CheckNewSpaceExpansionCriteria();
-
   SelectScavengingVisitorsTable();
 
   incremental_marking()->PrepareForScavenge();
index 8956e3c..b533cbb 100644 (file)
@@ -1359,6 +1359,10 @@ class Heap {
 
   void DeoptMarkedAllocationSites();
 
+  bool MaximumSizeScavenge() {
+    return maximum_size_scavenges_ > 0;
+  }
+
   // ObjectStats are kept in two arrays, counts and sizes. Related stats are
   // stored in a contiguous linear buffer. Stats groups are stored one after
   // another.
@@ -2019,6 +2023,12 @@ class Heap {
   intptr_t semi_space_copied_object_size_;
   double semi_space_copied_rate_;
 
+  // This is the pretenuring trigger for allocation sites that are in maybe
+  // tenure state. When we switched to the maximum new space size we deoptimize
+  // the code that belongs to the allocation site and derive the lifetime
+  // of the allocation site.
+  unsigned int maximum_size_scavenges_;
+
   // TODO(hpayer): Allocation site pretenuring may make this method obsolete.
   // Re-visit incremental marking heuristics.
   bool IsHighSurvivalRate() {
index fcaad0d..005c386 100644 (file)
@@ -3030,7 +3030,6 @@ void MarkCompactCollector::EvacuateNewSpace() {
   // sweep collection by failing allocations.  But since we are already in
   // a mark-sweep allocation, there is no sense in trying to trigger one.
   AlwaysAllocateScope scope(isolate());
-  heap()->CheckNewSpaceExpansionCriteria();
 
   NewSpace* new_space = heap()->new_space();
 
index 42c46e0..568b9c4 100644 (file)
@@ -1577,42 +1577,60 @@ inline void AllocationSite::IncrementMementoCreateCount() {
 }
 
 
-inline bool AllocationSite::DigestPretenuringFeedback() {
-  bool decision_changed = false;
+inline bool AllocationSite::MakePretenureDecision(
+    PretenureDecision current_decision,
+    double ratio,
+    bool maximum_size_scavenge) {
+  // Here we just allow state transitions from undecided or maybe tenure
+  // to don't tenure, maybe tenure, or tenure.
+  if ((current_decision == kUndecided || current_decision == kMaybeTenure)) {
+    if (ratio >= kPretenureRatio) {
+      // We just transition into tenure state when the semi-space was at
+      // maximum capacity.
+      if (maximum_size_scavenge) {
+        set_deopt_dependent_code(true);
+        set_pretenure_decision(kTenure);
+        // Currently we just need to deopt when we make a state transition to
+        // tenure.
+        return true;
+      }
+      set_pretenure_decision(kMaybeTenure);
+    } else {
+      set_pretenure_decision(kDontTenure);
+    }
+  }
+  return false;
+}
+
+
+inline bool AllocationSite::DigestPretenuringFeedback(
+    bool maximum_size_scavenge) {
+  bool deopt = false;
   int create_count = memento_create_count();
   int found_count = memento_found_count();
   bool minimum_mementos_created = create_count >= kPretenureMinimumCreated;
   double ratio =
       minimum_mementos_created || FLAG_trace_pretenuring_statistics ?
           static_cast<double>(found_count) / create_count : 0.0;
-  PretenureFlag current_mode = GetPretenureMode();
-
-  // TODO(hpayer): Add an intermediate state MAYBE_TENURE which collects
-  // more lifetime feedback for tenuring candidates. In the meantime, we
-  // just allow transitions from undecided to tenured or not tenured.
-  if (minimum_mementos_created && pretenure_decision() == kUndecided) {
-    PretenureDecision result = ratio >= kPretenureRatio
-        ? kTenure
-        : kDontTenure;
-    set_pretenure_decision(result);
-    if (current_mode != GetPretenureMode()) {
-      decision_changed = true;
-      set_deopt_dependent_code(true);
-    }
+  PretenureDecision current_decision = pretenure_decision();
+
+  if (minimum_mementos_created) {
+    deopt = MakePretenureDecision(
+        current_decision, ratio, maximum_size_scavenge);
   }
 
   if (FLAG_trace_pretenuring_statistics) {
     PrintF(
         "AllocationSite(%p): (created, found, ratio) (%d, %d, %f) %s => %s\n",
          static_cast<void*>(this), create_count, found_count, ratio,
-         current_mode == TENURED ? "tenured" : "not tenured",
-         GetPretenureMode() == TENURED ? "tenured" : "not tenured");
+         PretenureDecisionName(current_decision),
+         PretenureDecisionName(pretenure_decision()));
   }
 
   // Clear feedback calculation fields until the next gc.
   set_memento_found_count(0);
   set_memento_create_count(0);
-  return decision_changed;
+  return deopt;
 }
 
 
index b7fe861..613a954 100644 (file)
@@ -13303,6 +13303,19 @@ void AllocationSite::AddDependentCompilationInfo(Handle<AllocationSite> site,
 }
 
 
+const char* AllocationSite::PretenureDecisionName(PretenureDecision decision) {
+  switch (decision) {
+    case kUndecided: return "undecided";
+    case kDontTenure: return "don't tenure";
+    case kMaybeTenure: return "maybe tenure";
+    case kTenure: return "tenure";
+    case kZombie: return "zombie";
+    default: UNREACHABLE();
+  }
+  return NULL;
+}
+
+
 void JSObject::UpdateAllocationSite(Handle<JSObject> object,
                                     ElementsKind to_kind) {
   if (!object->IsJSArray()) return;
index 9f63657..73a1c5a 100644 (file)
@@ -8412,11 +8412,14 @@ class AllocationSite: public Struct {
   enum PretenureDecision {
     kUndecided = 0,
     kDontTenure = 1,
-    kTenure = 2,
-    kZombie = 3,
+    kMaybeTenure = 2,
+    kTenure = 3,
+    kZombie = 4,
     kLastPretenureDecisionValue = kZombie
   };
 
+  const char* PretenureDecisionName(PretenureDecision decision);
+
   DECL_ACCESSORS(transition_info, Object)
   // nested_site threads a list of sites that represent nested literals
   // walked in a particular order. So [[1, 2], 1, 2] will have one
@@ -8438,8 +8441,8 @@ class AllocationSite: public Struct {
   class DoNotInlineBit:         public BitField<bool,         29,  1> {};
 
   // Bitfields for pretenure_data
-  class MementoFoundCountBits:  public BitField<int,               0, 27> {};
-  class PretenureDecisionBits:  public BitField<PretenureDecision, 27, 2> {};
+  class MementoFoundCountBits:  public BitField<int,               0, 26> {};
+  class PretenureDecisionBits:  public BitField<PretenureDecision, 26, 3> {};
   class DeoptDependentCodeBit:  public BitField<bool,              29, 1> {};
   STATIC_ASSERT(PretenureDecisionBits::kMax >= kLastPretenureDecisionValue);
 
@@ -8502,7 +8505,11 @@ class AllocationSite: public Struct {
 
   inline void MarkZombie();
 
-  inline bool DigestPretenuringFeedback();
+  inline bool MakePretenureDecision(PretenureDecision current_decision,
+                                    double ratio,
+                                    bool maximum_size_scavenge);
+
+  inline bool DigestPretenuringFeedback(bool maximum_size_scavenge);
 
   ElementsKind GetElementsKind() {
     ASSERT(!SitePointsToLiteral());
index 93e2478..06ae55e 100644 (file)
@@ -2499,6 +2499,10 @@ class NewSpace : public Space {
     return to_space_.MaximumCapacity();
   }
 
+  bool IsAtMaximumCapacity() {
+    return Capacity() == MaximumCapacity();
+  }
+
   // Returns the initial capacity of a semispace.
   int InitialCapacity() {
     ASSERT(to_space_.InitialCapacity() == from_space_.InitialCapacity());
index 6d4da76..bdad930 100644 (file)
@@ -2200,6 +2200,11 @@ TEST(OptimizedPretenuringAllocationFolding) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
   i::ScopedVector<char> source(1024);
   i::OS::SNPrintF(
       source,
@@ -2244,6 +2249,11 @@ TEST(OptimizedPretenuringObjectArrayLiterals) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
   i::ScopedVector<char> source(1024);
   i::OS::SNPrintF(
       source,
@@ -2279,6 +2289,12 @@ TEST(OptimizedPretenuringMixedInObjectProperties) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
+
   i::ScopedVector<char> source(1024);
   i::OS::SNPrintF(
       source,
@@ -2320,6 +2336,11 @@ TEST(OptimizedPretenuringDoubleArrayProperties) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
   i::ScopedVector<char> source(1024);
   i::OS::SNPrintF(
       source,
@@ -2355,6 +2376,11 @@ TEST(OptimizedPretenuringdoubleArrayLiterals) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
   i::ScopedVector<char> source(1024);
   i::OS::SNPrintF(
       source,
@@ -2390,6 +2416,11 @@ TEST(OptimizedPretenuringNestedMixedArrayLiterals) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
   i::ScopedVector<char> source(1024);
   i::OS::SNPrintF(
       source,
@@ -2434,6 +2465,11 @@ TEST(OptimizedPretenuringNestedObjectLiterals) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
   i::ScopedVector<char> source(1024);
   i::OS::SNPrintF(
       source,
@@ -2478,6 +2514,11 @@ TEST(OptimizedPretenuringNestedDoubleLiterals) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
   i::ScopedVector<char> source(1024);
   i::OS::SNPrintF(
       source,
@@ -2530,6 +2571,11 @@ TEST(OptimizedPretenuringConstructorCalls) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
   i::ScopedVector<char> source(1024);
   // Call new is doing slack tracking for the first
   // JSFunction::kGenerousAllocationCount allocations, and we can't find
@@ -2576,6 +2622,11 @@ TEST(OptimizedPretenuringCallNew) {
   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
   v8::HandleScope scope(CcTest::isolate());
 
+  // Grow new space unitl maximum capacity reached.
+  while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
+    CcTest::heap()->new_space()->Grow();
+  }
+
   i::ScopedVector<char> source(1024);
   // Call new is doing slack tracking for the first
   // JSFunction::kGenerousAllocationCount allocations, and we can't find