blob: d59f8abe9f5ccd0c74b7218f7d41067deef5b061 [file] [log] [blame]
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/heap/heap-controller.h"
#include "src/execution/isolate-inl.h"
#include "src/heap/spaces.h"
namespace v8 {
namespace internal {
template <typename Trait>
double MemoryController<Trait>::GrowingFactor(Heap* heap, size_t max_heap_size,
double gc_speed,
double mutator_speed) {
const double max_factor = MaxGrowingFactor(max_heap_size);
const double factor =
DynamicGrowingFactor(gc_speed, mutator_speed, max_factor);
if (FLAG_trace_gc_verbose) {
Isolate::FromHeap(heap)->PrintWithTimestamp(
"[%s] factor %.1f based on mu=%.3f, speed_ratio=%.f "
"(gc=%.f, mutator=%.f)\n",
Trait::kName, factor, Trait::kTargetMutatorUtilization,
gc_speed / mutator_speed, gc_speed, mutator_speed);
}
return factor;
}
template <typename Trait>
double MemoryController<Trait>::MaxGrowingFactor(size_t max_heap_size) {
constexpr double kMinSmallFactor = 1.3;
constexpr double kMaxSmallFactor = 2.0;
constexpr double kHighFactor = 4.0;
size_t max_size = max_heap_size;
max_size = Max(max_size, Trait::kMinSize);
// If we are on a device with lots of memory, we allow a high heap
// growing factor.
if (max_size >= Trait::kMaxSize) {
return kHighFactor;
}
DCHECK_GE(max_size, Trait::kMinSize);
DCHECK_LT(max_size, Trait::kMaxSize);
// On smaller devices we linearly scale the factor: (X-A)/(B-A)*(D-C)+C
double factor = (max_size - Trait::kMinSize) *
(kMaxSmallFactor - kMinSmallFactor) /
(Trait::kMaxSize - Trait::kMinSize) +
kMinSmallFactor;
return factor;
}
// 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 target_mutator_utilization_ 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 target_mutator_utilization_, 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)
template <typename Trait>
double MemoryController<Trait>::DynamicGrowingFactor(double gc_speed,
double mutator_speed,
double max_factor) {
DCHECK_LE(Trait::kMinGrowingFactor, max_factor);
DCHECK_GE(Trait::kMaxGrowingFactor, max_factor);
if (gc_speed == 0 || mutator_speed == 0) return max_factor;
const double speed_ratio = gc_speed / mutator_speed;
const double a = speed_ratio * (1 - Trait::kTargetMutatorUtilization);
const double b = speed_ratio * (1 - Trait::kTargetMutatorUtilization) -
Trait::kTargetMutatorUtilization;
// The factor is a / b, but we need to check for small b first.
double factor = (a < b * max_factor) ? a / b : max_factor;
factor = Min(factor, max_factor);
factor = Max(factor, Trait::kMinGrowingFactor);
return factor;
}
template <typename Trait>
size_t MemoryController<Trait>::MinimumAllocationLimitGrowingStep(
Heap::HeapGrowingMode growing_mode) {
const size_t kRegularAllocationLimitGrowingStep = 8;
const size_t kLowMemoryAllocationLimitGrowingStep = 2;
size_t limit = (Page::kPageSize > MB ? Page::kPageSize : MB);
return limit * (growing_mode == Heap::HeapGrowingMode::kConservative
? kLowMemoryAllocationLimitGrowingStep
: kRegularAllocationLimitGrowingStep);
}
template <typename Trait>
size_t MemoryController<Trait>::CalculateAllocationLimit(
Heap* heap, size_t current_size, size_t min_size, size_t max_size,
size_t new_space_capacity, double factor,
Heap::HeapGrowingMode growing_mode) {
switch (growing_mode) {
case Heap::HeapGrowingMode::kConservative:
case Heap::HeapGrowingMode::kSlow:
factor = Min(factor, Trait::kConservativeGrowingFactor);
break;
case Heap::HeapGrowingMode::kMinimal:
factor = Trait::kMinGrowingFactor;
break;
case Heap::HeapGrowingMode::kDefault:
break;
}
if (FLAG_heap_growing_percent > 0) {
factor = 1.0 + FLAG_heap_growing_percent / 100.0;
}
if (FLAG_heap_growing_percent > 0) {
factor = 1.0 + FLAG_heap_growing_percent / 100.0;
}
CHECK_LT(1.0, factor);
CHECK_LT(0, current_size);
const uint64_t limit =
Max(static_cast<uint64_t>(current_size * factor),
static_cast<uint64_t>(current_size) +
MinimumAllocationLimitGrowingStep(growing_mode)) +
new_space_capacity;
const uint64_t limit_above_min_size = Max<uint64_t>(limit, min_size);
const uint64_t halfway_to_the_max =
(static_cast<uint64_t>(current_size) + max_size) / 2;
const size_t result =
static_cast<size_t>(Min(limit_above_min_size, halfway_to_the_max));
if (FLAG_trace_gc_verbose) {
Isolate::FromHeap(heap)->PrintWithTimestamp(
"[%s] Limit: old size: %zu KB, new limit: %zu KB (%.1f)\n",
Trait::kName, current_size / KB, result / KB, factor);
}
return result;
}
template class V8_EXPORT_PRIVATE MemoryController<V8HeapTrait>;
template class V8_EXPORT_PRIVATE MemoryController<GlobalMemoryTrait>;
const char* V8HeapTrait::kName = "HeapController";
const char* GlobalMemoryTrait::kName = "GlobalMemoryController";
} // namespace internal
} // namespace v8