Index: src/heap/heap.cc |
diff --git a/src/heap/heap.cc b/src/heap/heap.cc |
index f2783cf48297e0c26e35cb2169153e008e02e301..538a3f91f2ab833e606ab738dc1ba3dd5e110349 100644 |
--- a/src/heap/heap.cc |
+++ b/src/heap/heap.cc |
@@ -1219,7 +1219,9 @@ bool Heap::PerformGarbageCollection( |
old_gen_exhausted_ = false; |
old_generation_size_configured_ = true; |
// This should be updated before PostGarbageCollectionProcessing, which can |
- // cause another GC. |
+ // cause another GC. Take into account the objects promoted during GC. |
+ old_generation_allocation_counter_ += |
+ static_cast<size_t>(promoted_objects_size_); |
old_generation_size_at_last_gc_ = PromotedSpaceSizeOfObjects(); |
} else { |
Scavenge(); |
@@ -1261,9 +1263,12 @@ bool Heap::PerformGarbageCollection( |
// Register the amount of external allocated memory. |
amount_of_external_allocated_memory_at_last_global_gc_ = |
amount_of_external_allocated_memory_; |
- SetOldGenerationAllocationLimit( |
- PromotedSpaceSizeOfObjects(), |
- tracer()->CurrentAllocationThroughputInBytesPerMillisecond()); |
+ double gc_speed = tracer()->CombinedMarkCompactSpeedInBytesPerMillisecond(); |
+ double mutator_speed = static_cast<double>( |
+ tracer() |
+ ->CurrentOldGenerationAllocationThroughputInBytesPerMillisecond()); |
+ intptr_t old_gen_size = PromotedSpaceSizeOfObjects(); |
+ SetOldGenerationAllocationLimit(old_gen_size, gc_speed, mutator_speed); |
} |
{ |
@@ -5286,6 +5291,70 @@ int64_t Heap::PromotedExternalMemorySize() { |
} |
+const double Heap::kMinHeapGrowingFactor = 1.1; |
+const double Heap::kMaxHeapGrowingFactor = 4.0; |
+const double Heap::kMaxHeapGrowingFactorMemoryConstrained = 2.0; |
+const double Heap::kMaxHeapGrowingFactorIdle = 1.5; |
+const double Heap::kTargetMutatorUtilization = 0.97; |
+ |
+ |
+// Given GC speed in bytes per ms, the allocation throughput in bytes per ms |
+// (mutator speed), this function returns the heap growing factor that will |
+// achieve the kTargetMutatorUtilisation if the GC speed and the mutator speed |
+// remain the same until the next GC. |
+// |
+// For a fixed time-frame T = TM + TG, the mutator utilization is the ratio |
+// TM / (TM + TG), where TM is the time spent in the mutator and TG is the |
+// time spent in the garbage collector. |
+// |
+// Let MU be kTargetMutatorUtilisation, the desired mutator utilization for the |
+// time-frame from the end of the current GC to the end of the next GC. Based |
+// on the MU we can compute the heap growing factor F as |
+// |
+// F = R * (1 - MU) / (R * (1 - MU) - MU), where R = gc_speed / mutator_speed. |
+// |
+// This formula can be derived as follows. |
+// |
+// F = Limit / Live by definition, where the Limit is the allocation limit, |
+// and the Live is size of live objects. |
+// Let’s assume that we already know the Limit. Then: |
+// TG = Limit / gc_speed |
+// TM = (TM + TG) * MU, by definition of MU. |
+// TM = TG * MU / (1 - MU) |
+// TM = Limit * MU / (gc_speed * (1 - MU)) |
+// On the other hand, if the allocation throughput remains constant: |
+// Limit = Live + TM * allocation_throughput = Live + TM * mutator_speed |
+// Solving it for TM, we get |
+// TM = (Limit - Live) / mutator_speed |
+// Combining the two equation for TM: |
+// (Limit - Live) / mutator_speed = Limit * MU / (gc_speed * (1 - MU)) |
+// (Limit - Live) = Limit * MU * mutator_speed / (gc_speed * (1 - MU)) |
+// substitute R = gc_speed / mutator_speed |
+// (Limit - Live) = Limit * MU / (R * (1 - MU)) |
+// substitute F = Limit / Live |
+// F - 1 = F * MU / (R * (1 - MU)) |
+// F - F * MU / (R * (1 - MU)) = 1 |
+// F * (1 - MU / (R * (1 - MU))) = 1 |
+// F * (R * (1 - MU) - MU) / (R * (1 - MU)) = 1 |
+// F = R * (1 - MU) / (R * (1 - MU) - MU) |
+double Heap::HeapGrowingFactor(double gc_speed, double mutator_speed) { |
+ if (gc_speed == 0 || mutator_speed == 0) return kMaxHeapGrowingFactor; |
+ |
+ const double speed_ratio = gc_speed / mutator_speed; |
+ const double mu = kTargetMutatorUtilization; |
+ |
+ const double a = speed_ratio * (1 - mu); |
+ const double b = speed_ratio * (1 - mu) - mu; |
+ |
+ // The factor is a / b, but we need to check for small b first. |
+ double factor = |
+ (a < b * kMaxHeapGrowingFactor) ? a / b : kMaxHeapGrowingFactor; |
+ factor = Min(factor, kMaxHeapGrowingFactor); |
+ factor = Max(factor, kMinHeapGrowingFactor); |
+ return factor; |
+} |
+ |
+ |
intptr_t Heap::CalculateOldGenerationAllocationLimit(double factor, |
intptr_t old_gen_size) { |
CHECK(factor > 1.0); |
@@ -5298,52 +5367,34 @@ intptr_t Heap::CalculateOldGenerationAllocationLimit(double factor, |
} |
-void Heap::SetOldGenerationAllocationLimit( |
- intptr_t old_gen_size, size_t current_allocation_throughput) { |
-// Allocation throughput on Android devices is typically lower than on |
-// non-mobile devices. |
-#if V8_OS_ANDROID |
- const size_t kHighThroughput = 2500; |
- const size_t kLowThroughput = 250; |
-#else |
- const size_t kHighThroughput = 10000; |
- const size_t kLowThroughput = 1000; |
-#endif |
- const double min_scaling_factor = 1.1; |
- const double max_scaling_factor = 1.5; |
- double max_factor = 4; |
- const double idle_max_factor = 1.5; |
+void Heap::SetOldGenerationAllocationLimit(intptr_t old_gen_size, |
+ double gc_speed, |
+ double mutator_speed) { |
+ double factor = HeapGrowingFactor(gc_speed, mutator_speed); |
+ |
+ if (FLAG_trace_gc_verbose) { |
+ PrintIsolate(isolate_, |
+ "Heap growing factor %.1f based on mu=%.3f, speed_ratio=%.f " |
+ "(gc=%.f, mutator=%.f)\n", |
+ factor, kTargetMutatorUtilization, gc_speed / mutator_speed, |
+ gc_speed, mutator_speed); |
+ } |
+ |
// We set the old generation growing factor to 2 to grow the heap slower on |
// memory-constrained devices. |
if (max_old_generation_size_ <= kMaxOldSpaceSizeMediumMemoryDevice) { |
- max_factor = 2; |
- } |
- |
- double factor; |
- double idle_factor; |
- if (current_allocation_throughput == 0 || |
- current_allocation_throughput >= kHighThroughput) { |
- factor = max_factor; |
- } else if (current_allocation_throughput <= kLowThroughput) { |
- factor = min_scaling_factor; |
- } else { |
- // Compute factor using linear interpolation between points |
- // (kHighThroughput, max_scaling_factor) and (kLowThroughput, min_factor). |
- factor = min_scaling_factor + |
- (current_allocation_throughput - kLowThroughput) * |
- (max_scaling_factor - min_scaling_factor) / |
- (kHighThroughput - kLowThroughput); |
+ factor = Min(factor, kMaxHeapGrowingFactorMemoryConstrained); |
} |
if (FLAG_stress_compaction || |
mark_compact_collector()->reduce_memory_footprint_) { |
- factor = min_scaling_factor; |
+ factor = kMinHeapGrowingFactor; |
} |
// TODO(hpayer): Investigate if idle_old_generation_allocation_limit_ is still |
// needed after taking the allocation rate for the old generation limit into |
// account. |
- idle_factor = Min(factor, idle_max_factor); |
+ double idle_factor = Min(factor, kMaxHeapGrowingFactorIdle); |
old_generation_allocation_limit_ = |
CalculateOldGenerationAllocationLimit(factor, old_gen_size); |