[vm, gc] Adjust growth policy.
- Don't treat heap_growth_max as a lower bound when using the ratio heuristic. For small heaps this can cause us to exceed the working set size by more than 10x.
- Stick with the ratio heuristic when we're staying under the desired time fraction.
- Avoid tiny initial thresholds by growing at least by one new-space.
TEST=golem
Bug: https://github.com/dart-lang/sdk/issues/27414
Change-Id: I64b2994ea9a32ce8a8bbec15cd89d8b46a01b1bb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/397460
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 5fc7c34..c2cd33b 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -1660,7 +1660,7 @@
// G = kA, and estimate k from the previous cycle.
const intptr_t allocated_since_previous_gc =
before.CombinedUsedInWords() - last_usage_.CombinedUsedInWords();
- intptr_t grow_heap;
+ intptr_t growth_in_pages;
if (allocated_since_previous_gc > 0) {
intptr_t garbage =
before.CombinedUsedInWords() - after.CombinedUsedInWords();
@@ -1673,69 +1673,74 @@
const int garbage_ratio = static_cast<int>(k * 100);
- // Define GC to be 'worthwhile' iff at least fraction t of heap is garbage.
- double t = 1.0 - desired_utilization_;
- // If we spend too much time in GC, strive for even more free space.
- if (gc_time_fraction > garbage_collection_time_ratio_) {
- t += (gc_time_fraction - garbage_collection_time_ratio_) / 100.0;
- }
-
// Number of pages we can allocate and still be within the desired growth
// ratio.
- const intptr_t grow_pages =
+ const intptr_t growth_ratio_heuristic =
(static_cast<intptr_t>(after.CombinedUsedInWords() /
desired_utilization_) -
(after.CombinedUsedInWords())) /
kPageSizeInWords;
if (garbage_ratio == 0) {
// No garbage in the previous cycle so it would be hard to compute a
- // grow_heap size based on estimated garbage so we use growth ratio
+ // growth_in_pages size based on estimated garbage so we use growth ratio
// heuristics instead.
- grow_heap =
- Utils::Maximum(static_cast<intptr_t>(heap_growth_max_), grow_pages);
+ growth_in_pages = growth_ratio_heuristic;
} else if (garbage_collection_time_ratio_ == 0) {
// Exclude time from the growth policy decision for --deterministic.
- grow_heap =
- Utils::Maximum(static_cast<intptr_t>(heap_growth_max_), grow_pages);
+ growth_in_pages = growth_ratio_heuristic;
+ } else if (gc_time_fraction <= garbage_collection_time_ratio_) {
+ // Stick with the ratio hueristic when we're staying under the desired
+ // time fraction.
+ growth_in_pages = growth_ratio_heuristic;
} else {
- // Find minimum 'grow_heap' such that after increasing capacity by
- // 'grow_heap' pages and filling them, we expect a GC to be worthwhile.
+ // Define GC to be 'worthwhile' iff at least fraction t of heap is
+ // garbage.
+ double t = 1.0 - desired_utilization_;
+ // If we spend too much time in GC, strive for even more free space.
+ if (gc_time_fraction > garbage_collection_time_ratio_) {
+ t += (gc_time_fraction - garbage_collection_time_ratio_) / 100.0;
+ }
+
+ // Find minimum 'growth_in_pages' such that after increasing capacity by
+ // 'growth_in_pages' pages and filling them, we expect a GC to be
+ // worthwhile.
intptr_t max = heap_growth_max_;
intptr_t min = 0;
- intptr_t local_grow_heap = 0;
+ intptr_t local_growth_in_pages = 0;
while (min < max) {
- local_grow_heap = (max + min) / 2;
- const intptr_t limit =
- after.CombinedUsedInWords() + (local_grow_heap * kPageSizeInWords);
+ local_growth_in_pages = (max + min) / 2;
+ const intptr_t limit = after.CombinedUsedInWords() +
+ (local_growth_in_pages * kPageSizeInWords);
const intptr_t allocated_before_next_gc =
limit - (after.CombinedUsedInWords());
const double estimated_garbage = k * allocated_before_next_gc;
if (t <= estimated_garbage / limit) {
- max = local_grow_heap - 1;
+ max = local_growth_in_pages - 1;
} else {
- min = local_grow_heap + 1;
+ min = local_growth_in_pages + 1;
}
}
- local_grow_heap = (max + min) / 2;
- grow_heap = local_grow_heap;
- ASSERT(grow_heap >= 0);
+ local_growth_in_pages = (max + min) / 2;
+ growth_in_pages = local_growth_in_pages;
+ ASSERT(growth_in_pages >= 0);
// If we are going to grow by heap_grow_max_ then ensure that we
// will be growing the heap at least by the growth ratio heuristics.
- if (grow_heap >= heap_growth_max_) {
- grow_heap = Utils::Maximum(grow_pages, grow_heap);
+ if (growth_in_pages >= heap_growth_max_) {
+ growth_in_pages =
+ Utils::Maximum(growth_in_pages, growth_ratio_heuristic);
}
}
} else {
- grow_heap = 0;
+ growth_in_pages = 0;
}
last_usage_ = after;
intptr_t max_capacity_in_words = heap_->old_space()->max_capacity_in_words_;
if (max_capacity_in_words != 0) {
- ASSERT(grow_heap >= 0);
+ ASSERT(growth_in_pages >= 0);
// Fraction of asymptote used.
double f = static_cast<double>(after.CombinedUsedInWords() +
- (kPageSizeInWords * grow_heap)) /
+ (kPageSizeInWords * growth_in_pages)) /
static_cast<double>(max_capacity_in_words);
ASSERT(f >= 0.0);
// Increase weight at the high end.
@@ -1744,13 +1749,13 @@
f = 1.0 - f;
ASSERT(f <= 1.0);
// Discount growth more the closer we get to the desired asymptote.
- grow_heap = static_cast<intptr_t>(grow_heap * f);
+ growth_in_pages = static_cast<intptr_t>(growth_in_pages * f);
// Minimum growth step after reaching the asymptote.
intptr_t min_step = (2 * MB) / kPageSize;
- grow_heap = Utils::Maximum(min_step, grow_heap);
+ growth_in_pages = Utils::Maximum(min_step, growth_in_pages);
}
- RecordUpdate(before, after, grow_heap, "gc");
+ RecordUpdate(before, after, growth_in_pages, "gc");
}
void PageSpaceController::EvaluateAfterLoading(SpaceUsage after) {
@@ -1767,6 +1772,9 @@
}
// Apply growth cap.
+ intptr_t heap_growth_min = FLAG_new_gen_semi_max_size * MB / kPageSize;
+ growth_in_pages =
+ Utils::Maximum(static_cast<intptr_t>(heap_growth_min), growth_in_pages);
growth_in_pages =
Utils::Minimum(static_cast<intptr_t>(heap_growth_max_), growth_in_pages);