Use history to estimate what fraction of allocation will be garbage => grow more when less garbage was collected.

Change semantics of heap_growth_rate flag to mean *maximum* number of pages we grow.

Allow more initial growth before first mark-sweep => many small programs will avoid mark-sweep.

R=iposva@google.com

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

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@35410 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
koda@google.com 2014-04-24 22:30:20 +00:00
parent 321cec1c57
commit 12fd7df2bc
2 changed files with 54 additions and 51 deletions

View file

@ -17,8 +17,8 @@ DEFINE_FLAG(int, heap_growth_space_ratio, 20,
"The desired maximum percentage of free space after GC");
DEFINE_FLAG(int, heap_growth_time_ratio, 3,
"The desired maximum percentage of time spent in GC");
DEFINE_FLAG(int, heap_growth_rate, 4,
"The size the heap is grown, in heap pages");
DEFINE_FLAG(int, heap_growth_rate, 256,
"The max number of pages the heap can grow at a time");
DEFINE_FLAG(bool, print_free_list_before_gc, false,
"Print free list statistics before a GC");
DEFINE_FLAG(bool, print_free_list_after_gc, false,
@ -634,13 +634,13 @@ void PageSpace::MarkSweep(bool invoke_api_callbacks) {
PageSpaceController::PageSpaceController(int heap_growth_ratio,
int heap_growth_rate,
int heap_growth_max,
int garbage_collection_time_ratio)
: is_enabled_(false),
grow_heap_(heap_growth_rate),
grow_heap_(heap_growth_max / 2),
heap_growth_ratio_(heap_growth_ratio),
desired_utilization_((100.0 - heap_growth_ratio) / 100.0),
heap_growth_rate_(heap_growth_rate),
heap_growth_max_(heap_growth_max),
garbage_collection_time_ratio_(garbage_collection_time_ratio),
last_code_collection_in_us_(OS::GetCurrentTimeMicros()) {
}
@ -663,54 +663,62 @@ bool PageSpaceController::NeedsGarbageCollection(SpaceUsage after) const {
Utils::RoundUp(capacity_increase_in_words, PageSpace::kPageSizeInWords);
intptr_t capacity_increase_in_pages =
capacity_increase_in_words / PageSpace::kPageSizeInWords;
return capacity_increase_in_pages > grow_heap_;
double multiplier = 1.0;
// To avoid waste, the first GC should be triggered before too long. After
// kInitialTimeoutSeconds, gradually lower the capacity limit.
static const double kInitialTimeoutSeconds = 1.00;
if (history_.IsEmpty()) {
double seconds_since_init = MicrosecondsToSeconds(
OS::GetCurrentTimeMicros() - Isolate::Current()->start_time());
if (seconds_since_init > kInitialTimeoutSeconds) {
multiplier *= seconds_since_init / kInitialTimeoutSeconds;
}
}
return capacity_increase_in_pages * multiplier > grow_heap_;
}
void PageSpaceController::EvaluateGarbageCollection(
SpaceUsage before, SpaceUsage after, int64_t start, int64_t end) {
// TODO(iposva): Reevaluate the growth policies.
intptr_t before_total_in_words =
before.used_in_words + before.external_in_words;
intptr_t after_total_in_words =
after.used_in_words + after.external_in_words;
ASSERT(before_total_in_words >= after_total_in_words);
Heap* heap = Isolate::Current()->heap();
ASSERT(end >= start);
history_.AddGarbageCollectionTime(start, end);
int collected_garbage_ratio = static_cast<int>(
(static_cast<double>(before_total_in_words - after_total_in_words) /
static_cast<double>(before_total_in_words))
* 100.0);
bool enough_free_space =
(collected_garbage_ratio >= heap_growth_ratio_);
int garbage_collection_time_fraction =
history_.GarbageCollectionTimeFraction();
bool enough_free_time =
(garbage_collection_time_fraction <= garbage_collection_time_ratio_);
int gc_time_fraction = history_.GarbageCollectionTimeFraction();
heap->RecordData(PageSpace::kGCTimeFraction, gc_time_fraction);
Heap* heap = Isolate::Current()->heap();
if (enough_free_space && enough_free_time) {
grow_heap_ = 0;
} else {
intptr_t used_target = static_cast<intptr_t>(
after_total_in_words / desired_utilization_);
intptr_t capacity_growth_in_words =
Utils::RoundUp(Utils::Maximum(static_cast<intptr_t>(0),
used_target - after.capacity_in_words),
PageSpace::kPageSizeInWords);
int capacity_growth_in_pages =
capacity_growth_in_words / PageSpace::kPageSizeInWords;
grow_heap_ = Utils::Maximum(capacity_growth_in_pages, heap_growth_rate_);
heap->RecordData(PageSpace::kPageGrowth, capacity_growth_in_pages);
// Assume garbage increases linearly with allocation:
// G = kA, and estimate k from the previous cycle.
intptr_t allocated_since_previous_gc =
before.used_in_words - last_usage_.used_in_words;
intptr_t garbage = before.used_in_words - after.used_in_words;
double k = garbage / static_cast<double>(allocated_since_previous_gc);
heap->RecordData(PageSpace::kGarbageRatio, 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;
}
// Find minimum 'grow_heap_' such that after increasing capacity by
// 'grow_heap_' pages and filling them, we expect a GC to be worthwhile.
for (grow_heap_ = 0; grow_heap_ < heap_growth_max_; ++grow_heap_) {
intptr_t limit =
after.capacity_in_words + (grow_heap_ * PageSpace::kPageSizeInWords);
intptr_t allocated_before_next_gc = limit - after.used_in_words;
double estimated_garbage = k * allocated_before_next_gc;
if (t <= estimated_garbage / limit) {
break;
}
}
heap->RecordData(PageSpace::kPageGrowth, grow_heap_);
// Limit shrinkage: allow growth by at least half the pages freed by GC.
intptr_t freed_pages =
(before.capacity_in_words - after.capacity_in_words) /
PageSpace::kPageSizeInWords;
grow_heap_ = Utils::Maximum(grow_heap_, freed_pages / 2);
heap->RecordData(PageSpace::kGarbageRatio, collected_garbage_ratio);
heap->RecordData(PageSpace::kGCTimeFraction,
garbage_collection_time_fraction);
heap->RecordData(PageSpace::kAllowedGrowth, grow_heap_);
last_usage_ = after;
}

View file

@ -96,6 +96,8 @@ class PageSpaceGarbageCollectionHistory {
int GarbageCollectionTimeFraction();
bool IsEmpty() const { return history_.Size() == 0; }
private:
struct Entry {
int64_t start;
@ -109,14 +111,11 @@ class PageSpaceGarbageCollectionHistory {
};
// If GC is able to reclaim more than heap_growth_ratio (in percent) memory
// and if the relative GC time is below a given threshold,
// then the heap is not grown when the next GC decision is made.
// PageSpaceController controls the heap size.
class PageSpaceController {
public:
PageSpaceController(int heap_growth_ratio,
int heap_growth_rate,
int heap_growth_max,
int garbage_collection_time_ratio);
~PageSpaceController();
@ -126,10 +125,6 @@ class PageSpaceController {
bool NeedsGarbageCollection(SpaceUsage after) const;
// Should be called after each collection to update the controller state.
// A garbage collection is considered as successful if more than
// heap_growth_ratio % of memory got deallocated by the garbage collector.
// In this case garbage collection will be performed next time. Otherwise
// the heap will grow.
void EvaluateGarbageCollection(SpaceUsage before,
SpaceUsage after,
int64_t start, int64_t end);
@ -167,11 +162,11 @@ class PageSpaceController {
// Equivalent to \frac{100-heap_growth_ratio_}{100}.
double desired_utilization_;
// Number of pages we grow.
int heap_growth_rate_;
// Max number of pages we grow.
int heap_growth_max_;
// If the relative GC time stays below garbage_collection_time_ratio_
// garbage collection can be performed.
// If the relative GC time goes above garbage_collection_time_ratio_ %,
// we grow the heap more aggressively.
int garbage_collection_time_ratio_;
// The time in microseconds of the last time we tried to collect unused