Commit ecf51e34 authored by Rodrigo Bruno's avatar Rodrigo Bruno Committed by Commit Bot

[heap] refactoring HeapController.

The HeapController is now refactored in a way that new controllers only
need to specify the constants that define how a space grows and shrinks.

Bug: chromium:845409
Change-Id: I804eed440a791d6fbd232b7540a1cbe66b16a5f1
Reviewed-on: https://chromium-review.googlesource.com/1165347
Commit-Queue: Rodrigo Bruno <rfbpb@google.com>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55006}
parent ac66ba62
......@@ -8,11 +8,6 @@
namespace v8 {
namespace internal {
const double HeapController::kMinHeapGrowingFactor = 1.1;
const double HeapController::kMaxHeapGrowingFactor = 4.0;
const double HeapController::kConservativeHeapGrowingFactor = 1.3;
const double HeapController::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
......@@ -52,78 +47,71 @@ const double HeapController::kTargetMutatorUtilization = 0.97;
// 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 MemoryController::GrowingFactor(double min_growing_factor,
double max_growing_factor,
double target_mutator_utilization,
double gc_speed, double mutator_speed,
double MemoryController::GrowingFactor(double gc_speed, double mutator_speed,
double max_factor) {
DCHECK_LE(min_growing_factor, max_factor);
DCHECK_GE(max_growing_factor, max_factor);
DCHECK_LE(kMinGrowingFactor, max_factor);
DCHECK_GE(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 - target_mutator_utilization);
const double b = speed_ratio * (1 - target_mutator_utilization) -
target_mutator_utilization;
const double a = speed_ratio * (1 - kTargetMutatorUtilization);
const double b =
speed_ratio * (1 - kTargetMutatorUtilization) - 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, min_growing_factor);
factor = Max(factor, kMinGrowingFactor);
return factor;
}
double MemoryController::MaxGrowingFactor(size_t curr_max_size, size_t min_size,
size_t max_size) {
double MemoryController::MaxGrowingFactor(size_t curr_max_size) {
const double min_small_factor = 1.3;
const double max_small_factor = 2.0;
const double high_factor = 4.0;
size_t max_size_in_mb = curr_max_size / MB;
max_size_in_mb = Max(max_size_in_mb, static_cast<size_t>(min_size));
max_size_in_mb = Max(max_size_in_mb, kMinSize);
// If we are on a device with lots of memory, we allow a high heap
// growing factor.
if (max_size_in_mb >= max_size) {
if (max_size_in_mb >= kMaxSize) {
return high_factor;
}
DCHECK_GE(max_size_in_mb, min_size);
DCHECK_LT(max_size_in_mb, max_size);
DCHECK_GE(max_size_in_mb, kMinSize);
DCHECK_LT(max_size_in_mb, kMaxSize);
// On smaller devices we linearly scale the factor: (X-A)/(B-A)*(D-C)+C
double factor = (max_size_in_mb - min_size) *
double factor = (max_size_in_mb - kMinSize) *
(max_small_factor - min_small_factor) /
(max_size - min_size) +
(kMaxSize - kMinSize) +
min_small_factor;
return factor;
}
size_t HeapController::CalculateOldGenerationAllocationLimit(
size_t old_gen_size, size_t max_old_generation_size, double gc_speed,
double mutator_speed, size_t new_space_capacity,
Heap::HeapGrowingMode growing_mode) {
double max_factor = MaxGrowingFactor(
max_old_generation_size, kMinOldGenerationSize, kMaxOldGenerationSize);
double factor = GrowingFactor(kMinHeapGrowingFactor, kMaxHeapGrowingFactor,
kTargetMutatorUtilization, gc_speed,
mutator_speed, max_factor);
size_t MemoryController::CalculateAllocationLimit(
size_t curr_size, size_t max_size, double gc_speed, double mutator_speed,
size_t new_space_capacity, Heap::HeapGrowingMode growing_mode) {
double max_factor = MaxGrowingFactor(max_size);
double factor = GrowingFactor(gc_speed, mutator_speed, max_factor);
if (FLAG_trace_gc_verbose) {
heap_->isolate()->PrintWithTimestamp(
"Heap growing factor %.1f based on mu=%.3f, speed_ratio=%.f "
"%s factor %.1f based on mu=%.3f, speed_ratio=%.f "
"(gc=%.f, mutator=%.f)\n",
factor, kTargetMutatorUtilization, gc_speed / mutator_speed, gc_speed,
mutator_speed);
ControllerName(), factor, kTargetMutatorUtilization,
gc_speed / mutator_speed, gc_speed, mutator_speed);
}
if (growing_mode == Heap::HeapGrowingMode::kConservative ||
growing_mode == Heap::HeapGrowingMode::kSlow) {
factor = Min(factor, kConservativeHeapGrowingFactor);
factor = Min(factor, kConservativeGrowingFactor);
}
if (growing_mode == Heap::HeapGrowingMode::kMinimal) {
factor = kMinHeapGrowingFactor;
factor = kMinGrowingFactor;
}
if (FLAG_heap_growing_percent > 0) {
......@@ -131,26 +119,25 @@ size_t HeapController::CalculateOldGenerationAllocationLimit(
}
CHECK_LT(1.0, factor);
CHECK_LT(0, old_gen_size);
uint64_t limit = static_cast<uint64_t>(old_gen_size * factor);
limit = Max(limit, static_cast<uint64_t>(old_gen_size) +
CHECK_LT(0, curr_size);
uint64_t limit = static_cast<uint64_t>(curr_size * factor);
limit = Max(limit, static_cast<uint64_t>(curr_size) +
MinimumAllocationLimitGrowingStep(growing_mode));
limit += new_space_capacity;
uint64_t halfway_to_the_max =
(static_cast<uint64_t>(old_gen_size) + max_old_generation_size) / 2;
(static_cast<uint64_t>(curr_size) + max_size) / 2;
size_t result = static_cast<size_t>(Min(limit, halfway_to_the_max));
if (FLAG_trace_gc_verbose) {
heap_->isolate()->PrintWithTimestamp(
"Heap Controller Limit: old size: %" PRIuS " KB, new limit: %" PRIuS
" KB (%.1f)\n",
old_gen_size / KB, result / KB, factor);
"%s Limit: old size: %" PRIuS " KB, new limit: %" PRIuS " KB (%.1f)\n",
ControllerName(), curr_size / KB, result / KB, factor);
}
return result;
}
size_t HeapController::MinimumAllocationLimitGrowingStep(
size_t MemoryController::MinimumAllocationLimitGrowingStep(
Heap::HeapGrowingMode growing_mode) {
const size_t kRegularAllocationLimitGrowingStep = 8;
const size_t kLowMemoryAllocationLimitGrowingStep = 2;
......
......@@ -13,49 +13,65 @@
namespace v8 {
namespace internal {
class MemoryController {
protected:
V8_EXPORT_PRIVATE static double GrowingFactor(
double min_growing_factor, double max_growing_factor,
double target_mutator_utilization, double gc_speed, double mutator_speed,
double max_factor);
V8_EXPORT_PRIVATE static double MaxGrowingFactor(size_t curr_max_size,
size_t min_size,
size_t max_size);
FRIEND_TEST(HeapController, HeapGrowingFactor);
FRIEND_TEST(HeapController, MaxHeapGrowingFactor);
FRIEND_TEST(HeapControllerTest, OldGenerationAllocationLimit);
};
class HeapController : public MemoryController {
class V8_EXPORT_PRIVATE MemoryController {
public:
explicit HeapController(Heap* heap) : heap_(heap) {}
MemoryController(Heap* heap, double min_growing_factor,
double max_growing_factor,
double conservative_growing_factor,
double target_mutator_utilization, size_t min_size,
size_t max_size)
: heap_(heap),
kMinGrowingFactor(min_growing_factor),
kMaxGrowingFactor(max_growing_factor),
kConservativeGrowingFactor(conservative_growing_factor),
kTargetMutatorUtilization(target_mutator_utilization),
kMinSize(min_size),
kMaxSize(max_size) {}
virtual ~MemoryController() {}
// Computes the allocation limit to trigger the next full garbage collection.
V8_EXPORT_PRIVATE size_t CalculateOldGenerationAllocationLimit(
size_t old_gen_size, size_t max_old_generation_size, double gc_speed,
double mutator_speed, size_t new_space_capacity,
Heap::HeapGrowingMode growing_mode);
// Computes the allocation limit to trigger the next garbage collection.
size_t CalculateAllocationLimit(size_t curr_size, size_t max_size,
double gc_speed, double mutator_speed,
size_t new_space_capacity,
Heap::HeapGrowingMode growing_mode);
// Computes the growing step when the limit increases.
size_t MinimumAllocationLimitGrowingStep(Heap::HeapGrowingMode growing_mode);
// The old space size has to be a multiple of Page::kPageSize.
protected:
double GrowingFactor(double gc_speed, double mutator_speed,
double max_factor);
double MaxGrowingFactor(size_t curr_max_size);
virtual const char* ControllerName() = 0;
Heap* const heap_;
const double kMinGrowingFactor;
const double kMaxGrowingFactor;
const double kConservativeGrowingFactor;
const double kTargetMutatorUtilization;
// Sizes are in MB.
static const size_t kMinOldGenerationSize = 128 * Heap::kPointerMultiplier;
static const size_t kMaxOldGenerationSize = 1024 * Heap::kPointerMultiplier;
const size_t kMinSize;
const size_t kMaxSize;
private:
FRIEND_TEST(HeapController, HeapGrowingFactor);
FRIEND_TEST(HeapController, MaxHeapGrowingFactor);
FRIEND_TEST(HeapControllerTest, HeapGrowingFactor);
FRIEND_TEST(HeapControllerTest, MaxHeapGrowingFactor);
FRIEND_TEST(HeapControllerTest, MaxOldGenerationSize);
FRIEND_TEST(HeapControllerTest, OldGenerationAllocationLimit);
};
V8_EXPORT_PRIVATE static const double kMinHeapGrowingFactor;
V8_EXPORT_PRIVATE static const double kMaxHeapGrowingFactor;
V8_EXPORT_PRIVATE static const double kConservativeHeapGrowingFactor;
V8_EXPORT_PRIVATE static const double kTargetMutatorUtilization;
class HeapController : public MemoryController {
public:
explicit HeapController(Heap* heap)
: MemoryController(heap, 1.1, 4.0, 1.3, 0.97, kMinHeapSize,
kMaxHeapSize) {}
Heap* heap_;
// Sizes are in MB.
static const size_t kMinHeapSize = 128 * Heap::kPointerMultiplier;
static const size_t kMaxHeapSize = 1024 * Heap::kPointerMultiplier;
protected:
const char* ControllerName() { return "HeapController"; }
};
} // namespace internal
......
......@@ -259,8 +259,8 @@ size_t Heap::ComputeMaxOldGenerationSize(uint64_t physical_memory) {
size_t computed_size = static_cast<size_t>(physical_memory / i::MB /
old_space_physical_memory_factor *
kPointerMultiplier);
return Max(Min(computed_size, HeapController::kMaxOldGenerationSize),
HeapController::kMinOldGenerationSize);
return Max(Min(computed_size, HeapController::kMaxHeapSize),
HeapController::kMinHeapSize);
}
size_t Heap::Capacity() {
......@@ -1786,7 +1786,7 @@ bool Heap::PerformGarbageCollection(
external_memory_at_last_mark_compact_ = external_memory_;
external_memory_limit_ = external_memory_ + kExternalAllocationSoftLimit;
size_t new_limit = heap_controller()->CalculateOldGenerationAllocationLimit(
size_t new_limit = heap_controller()->CalculateAllocationLimit(
old_gen_size, max_old_generation_size_, gc_speed, mutator_speed,
new_space()->Capacity(), CurrentHeapGrowingMode());
old_generation_allocation_limit_ = new_limit;
......@@ -1795,7 +1795,7 @@ bool Heap::PerformGarbageCollection(
old_gen_size, tracer()->AverageMarkCompactMutatorUtilization());
} else if (HasLowYoungGenerationAllocationRate() &&
old_generation_size_configured_) {
size_t new_limit = heap_controller()->CalculateOldGenerationAllocationLimit(
size_t new_limit = heap_controller()->CalculateAllocationLimit(
old_gen_size, max_old_generation_size_, gc_speed, mutator_speed,
new_space()->Capacity(), CurrentHeapGrowingMode());
if (new_limit < old_generation_allocation_limit_) {
......
......@@ -2262,7 +2262,7 @@ class Heap {
friend class EphemeronHashTableMarkingTask;
friend class GCCallbacksScope;
friend class GCTracer;
friend class HeapController;
friend class MemoryController;
friend class HeapIterator;
friend class IdleScavengeObserver;
friend class IncrementalMarking;
......
......@@ -32,104 +32,82 @@ void CheckEqualRounded(double expected, double actual) {
EXPECT_DOUBLE_EQ(expected, actual);
}
TEST(HeapController, HeapGrowingFactor) {
double min_factor = HeapController::kMinHeapGrowingFactor;
double max_factor = HeapController::kMaxHeapGrowingFactor;
double target_mu = HeapController::kTargetMutatorUtilization;
CheckEqualRounded(max_factor,
MemoryController::GrowingFactor(min_factor, max_factor,
target_mu, 34, 1, 4.0));
CheckEqualRounded(3.553, MemoryController::GrowingFactor(
min_factor, max_factor, target_mu, 45, 1, 4.0));
CheckEqualRounded(2.830, MemoryController::GrowingFactor(
min_factor, max_factor, target_mu, 50, 1, 4.0));
CheckEqualRounded(1.478, MemoryController::GrowingFactor(
min_factor, max_factor, target_mu, 100, 1, 4.0));
CheckEqualRounded(1.193, MemoryController::GrowingFactor(
min_factor, max_factor, target_mu, 200, 1, 4.0));
CheckEqualRounded(1.121, MemoryController::GrowingFactor(
min_factor, max_factor, target_mu, 300, 1, 4.0));
CheckEqualRounded(MemoryController::GrowingFactor(min_factor, max_factor,
target_mu, 300, 1, 4.0),
MemoryController::GrowingFactor(min_factor, max_factor,
target_mu, 600, 2, 4.0));
CheckEqualRounded(min_factor,
MemoryController::GrowingFactor(min_factor, max_factor,
target_mu, 400, 1, 4.0));
TEST_F(HeapControllerTest, HeapGrowingFactor) {
HeapController heap_controller(i_isolate()->heap());
double min_factor = heap_controller.kMinGrowingFactor;
double max_factor = heap_controller.kMaxGrowingFactor;
CheckEqualRounded(max_factor, heap_controller.GrowingFactor(34, 1, 4.0));
CheckEqualRounded(3.553, heap_controller.GrowingFactor(45, 1, 4.0));
CheckEqualRounded(2.830, heap_controller.GrowingFactor(50, 1, 4.0));
CheckEqualRounded(1.478, heap_controller.GrowingFactor(100, 1, 4.0));
CheckEqualRounded(1.193, heap_controller.GrowingFactor(200, 1, 4.0));
CheckEqualRounded(1.121, heap_controller.GrowingFactor(300, 1, 4.0));
CheckEqualRounded(heap_controller.GrowingFactor(300, 1, 4.0),
heap_controller.GrowingFactor(600, 2, 4.0));
CheckEqualRounded(min_factor, heap_controller.GrowingFactor(400, 1, 4.0));
}
TEST(HeapController, MaxHeapGrowingFactor) {
CheckEqualRounded(1.3, MemoryController::MaxGrowingFactor(
HeapController::kMinOldGenerationSize * MB,
HeapController::kMinOldGenerationSize,
HeapController::kMaxOldGenerationSize));
CheckEqualRounded(1.600, MemoryController::MaxGrowingFactor(
HeapController::kMaxOldGenerationSize / 2 * MB,
HeapController::kMinOldGenerationSize,
HeapController::kMaxOldGenerationSize));
CheckEqualRounded(1.999, MemoryController::MaxGrowingFactor(
(HeapController::kMaxOldGenerationSize -
Heap::kPointerMultiplier) *
MB,
HeapController::kMinOldGenerationSize,
HeapController::kMaxOldGenerationSize));
TEST_F(HeapControllerTest, MaxHeapGrowingFactor) {
HeapController heap_controller(i_isolate()->heap());
CheckEqualRounded(
4.0, MemoryController::MaxGrowingFactor(
static_cast<size_t>(HeapController::kMaxOldGenerationSize) * MB,
HeapController::kMinOldGenerationSize,
HeapController::kMaxOldGenerationSize));
1.3, heap_controller.MaxGrowingFactor(heap_controller.kMinSize * MB));
CheckEqualRounded(1.600, heap_controller.MaxGrowingFactor(
heap_controller.kMaxSize / 2 * MB));
CheckEqualRounded(
1.999, heap_controller.MaxGrowingFactor(
(heap_controller.kMaxSize - Heap::kPointerMultiplier) * MB));
CheckEqualRounded(4.0,
heap_controller.MaxGrowingFactor(
static_cast<size_t>(heap_controller.kMaxSize) * MB));
}
TEST_F(HeapControllerTest, OldGenerationAllocationLimit) {
Heap* heap = i_isolate()->heap();
HeapController heap_controller(heap);
size_t old_gen_size = 128 * MB;
size_t max_old_generation_size = 512 * MB;
double gc_speed = 100;
double mutator_speed = 1;
size_t new_space_capacity = 16 * MB;
double max_factor = MemoryController::MaxGrowingFactor(
max_old_generation_size, HeapController::kMinOldGenerationSize,
HeapController::kMaxOldGenerationSize);
double max_factor = heap_controller.MaxGrowingFactor(max_old_generation_size);
double factor =
MemoryController::GrowingFactor(HeapController::kMinHeapGrowingFactor,
HeapController::kMaxHeapGrowingFactor,
HeapController::kTargetMutatorUtilization,
gc_speed, mutator_speed, max_factor);
heap_controller.GrowingFactor(gc_speed, mutator_speed, max_factor);
EXPECT_EQ(static_cast<size_t>(old_gen_size * factor + new_space_capacity),
heap->heap_controller()->CalculateOldGenerationAllocationLimit(
heap->heap_controller()->CalculateAllocationLimit(
old_gen_size, max_old_generation_size, gc_speed, mutator_speed,
new_space_capacity, Heap::HeapGrowingMode::kDefault));
factor = Min(factor, HeapController::kConservativeHeapGrowingFactor);
factor = Min(factor, heap_controller.kConservativeGrowingFactor);
EXPECT_EQ(static_cast<size_t>(old_gen_size * factor + new_space_capacity),
heap->heap_controller()->CalculateOldGenerationAllocationLimit(
heap->heap_controller()->CalculateAllocationLimit(
old_gen_size, max_old_generation_size, gc_speed, mutator_speed,
new_space_capacity, Heap::HeapGrowingMode::kSlow));
factor = Min(factor, HeapController::kConservativeHeapGrowingFactor);
factor = Min(factor, heap_controller.kConservativeGrowingFactor);
EXPECT_EQ(static_cast<size_t>(old_gen_size * factor + new_space_capacity),
heap->heap_controller()->CalculateOldGenerationAllocationLimit(
heap->heap_controller()->CalculateAllocationLimit(
old_gen_size, max_old_generation_size, gc_speed, mutator_speed,
new_space_capacity, Heap::HeapGrowingMode::kConservative));
factor = HeapController::kMinHeapGrowingFactor;
factor = heap_controller.kMinGrowingFactor;
EXPECT_EQ(static_cast<size_t>(old_gen_size * factor + new_space_capacity),
heap->heap_controller()->CalculateOldGenerationAllocationLimit(
heap->heap_controller()->CalculateAllocationLimit(
old_gen_size, max_old_generation_size, gc_speed, mutator_speed,
new_space_capacity, Heap::HeapGrowingMode::kMinimal));
}
TEST(HeapController, MaxOldGenerationSize) {
TEST_F(HeapControllerTest, MaxOldGenerationSize) {
HeapController heap_controller(i_isolate()->heap());
uint64_t configurations[][2] = {
{0, HeapController::kMinOldGenerationSize},
{512, HeapController::kMinOldGenerationSize},
{0, heap_controller.kMinSize},
{512, heap_controller.kMinSize},
{1 * GB, 256 * Heap::kPointerMultiplier},
{2 * static_cast<uint64_t>(GB), 512 * Heap::kPointerMultiplier},
{4 * static_cast<uint64_t>(GB), HeapController::kMaxOldGenerationSize},
{8 * static_cast<uint64_t>(GB), HeapController::kMaxOldGenerationSize}};
{4 * static_cast<uint64_t>(GB), heap_controller.kMaxSize},
{8 * static_cast<uint64_t>(GB), heap_controller.kMaxSize}};
for (auto configuration : configurations) {
ASSERT_EQ(configuration[1],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment