Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/pages.h" | 5 #include "vm/pages.h" |
| 6 | 6 |
| 7 #include "platform/assert.h" | 7 #include "platform/assert.h" |
| 8 #include "vm/compiler_stats.h" | 8 #include "vm/compiler_stats.h" |
| 9 #include "vm/gc_marker.h" | 9 #include "vm/gc_marker.h" |
| 10 #include "vm/gc_sweeper.h" | 10 #include "vm/gc_sweeper.h" |
| 11 #include "vm/object.h" | 11 #include "vm/object.h" |
| 12 #include "vm/virtual_memory.h" | 12 #include "vm/virtual_memory.h" |
| 13 | 13 |
| 14 namespace dart { | 14 namespace dart { |
| 15 | 15 |
| 16 DEFINE_FLAG(int, heap_growth_space_ratio, 20, | 16 DEFINE_FLAG(int, heap_growth_space_ratio, 20, |
| 17 "The desired maximum percentage of free space after GC"); | 17 "The desired maximum percentage of free space after GC"); |
| 18 DEFINE_FLAG(int, heap_growth_time_ratio, 3, | 18 DEFINE_FLAG(int, heap_growth_time_ratio, 3, |
| 19 "The desired maximum percentage of time spent in GC"); | 19 "The desired maximum percentage of time spent in GC"); |
| 20 DEFINE_FLAG(int, heap_growth_rate, 4, | 20 DEFINE_FLAG(int, heap_growth_rate, 256, |
| 21 "The size the heap is grown, in heap pages"); | 21 "The max number of pages the heap can grow at a time"); |
| 22 DEFINE_FLAG(bool, print_free_list_before_gc, false, | 22 DEFINE_FLAG(bool, print_free_list_before_gc, false, |
| 23 "Print free list statistics before a GC"); | 23 "Print free list statistics before a GC"); |
| 24 DEFINE_FLAG(bool, print_free_list_after_gc, false, | 24 DEFINE_FLAG(bool, print_free_list_after_gc, false, |
| 25 "Print free list statistics after a GC"); | 25 "Print free list statistics after a GC"); |
| 26 DEFINE_FLAG(bool, collect_code, true, | 26 DEFINE_FLAG(bool, collect_code, true, |
| 27 "Attempt to GC infrequently used code."); | 27 "Attempt to GC infrequently used code."); |
| 28 DEFINE_FLAG(int, code_collection_interval_in_us, 30000000, | 28 DEFINE_FLAG(int, code_collection_interval_in_us, 30000000, |
| 29 "Time between attempts to collect unused code."); | 29 "Time between attempts to collect unused code."); |
| 30 DEFINE_FLAG(bool, log_code_drop, false, | 30 DEFINE_FLAG(bool, log_code_drop, false, |
| 31 "Emit a log message when pointers to unused code are dropped."); | 31 "Emit a log message when pointers to unused code are dropped."); |
| (...skipping 595 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 627 OS::PrintErr(" done.\n"); | 627 OS::PrintErr(" done.\n"); |
| 628 } | 628 } |
| 629 | 629 |
| 630 // Done, reset the marker. | 630 // Done, reset the marker. |
| 631 ASSERT(sweeping_); | 631 ASSERT(sweeping_); |
| 632 sweeping_ = false; | 632 sweeping_ = false; |
| 633 } | 633 } |
| 634 | 634 |
| 635 | 635 |
| 636 PageSpaceController::PageSpaceController(int heap_growth_ratio, | 636 PageSpaceController::PageSpaceController(int heap_growth_ratio, |
| 637 int heap_growth_rate, | 637 int heap_growth_max, |
| 638 int garbage_collection_time_ratio) | 638 int garbage_collection_time_ratio) |
| 639 : is_enabled_(false), | 639 : is_enabled_(false), |
| 640 grow_heap_(heap_growth_rate), | 640 grow_heap_(heap_growth_max / 2), |
| 641 heap_growth_ratio_(heap_growth_ratio), | 641 heap_growth_ratio_(heap_growth_ratio), |
| 642 desired_utilization_((100.0 - heap_growth_ratio) / 100.0), | 642 desired_utilization_((100.0 - heap_growth_ratio) / 100.0), |
| 643 heap_growth_rate_(heap_growth_rate), | 643 heap_growth_max_(heap_growth_max), |
| 644 garbage_collection_time_ratio_(garbage_collection_time_ratio), | 644 garbage_collection_time_ratio_(garbage_collection_time_ratio), |
| 645 last_code_collection_in_us_(OS::GetCurrentTimeMicros()) { | 645 last_code_collection_in_us_(OS::GetCurrentTimeMicros()) { |
| 646 } | 646 } |
| 647 | 647 |
| 648 | 648 |
| 649 PageSpaceController::~PageSpaceController() {} | 649 PageSpaceController::~PageSpaceController() {} |
| 650 | 650 |
| 651 | 651 |
| 652 bool PageSpaceController::NeedsGarbageCollection(SpaceUsage after) const { | 652 bool PageSpaceController::NeedsGarbageCollection(SpaceUsage after) const { |
| 653 if (!is_enabled_) { | 653 if (!is_enabled_) { |
| 654 return false; | 654 return false; |
| 655 } | 655 } |
| 656 if (heap_growth_ratio_ == 100) { | 656 if (heap_growth_ratio_ == 100) { |
| 657 return false; | 657 return false; |
| 658 } | 658 } |
| 659 intptr_t capacity_increase_in_words = | 659 intptr_t capacity_increase_in_words = |
| 660 after.capacity_in_words - last_usage_.capacity_in_words; | 660 after.capacity_in_words - last_usage_.capacity_in_words; |
| 661 ASSERT(capacity_increase_in_words >= 0); | 661 ASSERT(capacity_increase_in_words >= 0); |
| 662 capacity_increase_in_words = | 662 capacity_increase_in_words = |
| 663 Utils::RoundUp(capacity_increase_in_words, PageSpace::kPageSizeInWords); | 663 Utils::RoundUp(capacity_increase_in_words, PageSpace::kPageSizeInWords); |
| 664 intptr_t capacity_increase_in_pages = | 664 intptr_t capacity_increase_in_pages = |
| 665 capacity_increase_in_words / PageSpace::kPageSizeInWords; | 665 capacity_increase_in_words / PageSpace::kPageSizeInWords; |
| 666 return capacity_increase_in_pages > grow_heap_; | 666 double multiplier = 1.0; |
| 667 // To avoid waste, the first GC should be triggered before too long. After | |
| 668 // kInitialTimeoutSeconds, gradually lower the capacity limit. | |
| 669 static const double kInitialTimeoutSeconds = 1.00; | |
| 670 if (history_.IsEmpty()) { | |
| 671 double seconds_since_init = MicrosecondsToSeconds( | |
| 672 OS::GetCurrentTimeMicros() - Isolate::Current()->start_time()); | |
| 673 if (seconds_since_init > kInitialTimeoutSeconds) { | |
| 674 multiplier = seconds_since_init / kInitialTimeoutSeconds; | |
| 675 } | |
| 676 } | |
| 677 return capacity_increase_in_pages * multiplier > grow_heap_; | |
| 667 } | 678 } |
| 668 | 679 |
| 669 | 680 |
| 670 void PageSpaceController::EvaluateGarbageCollection( | 681 void PageSpaceController::EvaluateGarbageCollection( |
| 671 SpaceUsage before, SpaceUsage after, int64_t start, int64_t end) { | 682 SpaceUsage before, SpaceUsage after, int64_t start, int64_t end) { |
| 672 // TODO(iposva): Reevaluate the growth policies. | 683 Heap* heap = Isolate::Current()->heap(); |
|
Ivan Posva
2014/04/24 17:44:18
The PSC should remember the heap it is associated
koda
2014/04/24 18:07:45
I'll do this in a follow-up Isolate::Current clean
| |
| 673 intptr_t before_total_in_words = | |
| 674 before.used_in_words + before.external_in_words; | |
| 675 intptr_t after_total_in_words = | |
| 676 after.used_in_words + after.external_in_words; | |
| 677 ASSERT(before_total_in_words >= after_total_in_words); | |
| 678 ASSERT(end >= start); | 684 ASSERT(end >= start); |
| 679 history_.AddGarbageCollectionTime(start, end); | 685 history_.AddGarbageCollectionTime(start, end); |
| 680 int collected_garbage_ratio = static_cast<int>( | 686 int gc_time_fraction = history_.GarbageCollectionTimeFraction(); |
| 681 (static_cast<double>(before_total_in_words - after_total_in_words) / | 687 heap->RecordData(PageSpace::kGCTimeFraction, gc_time_fraction); |
| 682 static_cast<double>(before_total_in_words)) | |
| 683 * 100.0); | |
| 684 bool enough_free_space = | |
| 685 (collected_garbage_ratio >= heap_growth_ratio_); | |
| 686 int garbage_collection_time_fraction = | |
| 687 history_.GarbageCollectionTimeFraction(); | |
| 688 bool enough_free_time = | |
| 689 (garbage_collection_time_fraction <= garbage_collection_time_ratio_); | |
| 690 | 688 |
| 691 Heap* heap = Isolate::Current()->heap(); | 689 // Assume garbage increases linearly with allocation: |
| 692 if (enough_free_space && enough_free_time) { | 690 // G = kA, and estimate k from the previous cycle. |
| 693 grow_heap_ = 0; | 691 intptr_t allocated_since_previous_gc = |
| 694 } else { | 692 before.used_in_words - last_usage_.used_in_words; |
| 695 intptr_t used_target = static_cast<intptr_t>( | 693 intptr_t garbage = before.used_in_words - after.used_in_words; |
| 696 after_total_in_words / desired_utilization_); | 694 double k = garbage / static_cast<double>(allocated_since_previous_gc); |
| 697 intptr_t capacity_growth_in_words = | 695 heap->RecordData(PageSpace::kGarbageRatio, static_cast<int>(k * 100)); |
| 698 Utils::RoundUp(Utils::Maximum(static_cast<intptr_t>(0), | 696 |
| 699 used_target - after.capacity_in_words), | 697 // Define GC to be 'worthwhile' iff at least fraction t of heap is garbage. |
| 700 PageSpace::kPageSizeInWords); | 698 double t = 1.0 - desired_utilization_; |
| 701 int capacity_growth_in_pages = | 699 // If we spend too much time in GC, strive for even more free space. |
| 702 capacity_growth_in_words / PageSpace::kPageSizeInWords; | 700 if (gc_time_fraction > garbage_collection_time_ratio_) { |
| 703 grow_heap_ = Utils::Maximum(capacity_growth_in_pages, heap_growth_rate_); | 701 t += (gc_time_fraction - garbage_collection_time_ratio_) / 100.0; |
| 704 heap->RecordData(PageSpace::kPageGrowth, capacity_growth_in_pages); | |
| 705 } | 702 } |
| 703 | |
| 704 // Find minimum 'grow_heap_' such that after increasing capacity by | |
| 705 // 'grow_heap_' pages and filling them, we expect a GC to be worthwhile. | |
| 706 for (grow_heap_ = 0; grow_heap_ < heap_growth_max_; ++grow_heap_) { | |
| 707 intptr_t limit = | |
| 708 after.capacity_in_words + (grow_heap_ * PageSpace::kPageSizeInWords); | |
| 709 intptr_t allocated_before_next_gc = limit - after.used_in_words; | |
| 710 double estimated_garbage = k * allocated_before_next_gc; | |
| 711 if (t <= estimated_garbage / limit) { | |
| 712 break; | |
| 713 } | |
| 714 } | |
| 715 heap->RecordData(PageSpace::kPageGrowth, grow_heap_); | |
| 716 | |
| 706 // Limit shrinkage: allow growth by at least half the pages freed by GC. | 717 // Limit shrinkage: allow growth by at least half the pages freed by GC. |
| 707 intptr_t freed_pages = | 718 intptr_t freed_pages = |
| 708 (before.capacity_in_words - after.capacity_in_words) / | 719 (before.capacity_in_words - after.capacity_in_words) / |
| 709 PageSpace::kPageSizeInWords; | 720 PageSpace::kPageSizeInWords; |
| 710 grow_heap_ = Utils::Maximum(grow_heap_, freed_pages / 2); | 721 grow_heap_ = Utils::Maximum(grow_heap_, freed_pages / 2); |
| 711 heap->RecordData(PageSpace::kGarbageRatio, collected_garbage_ratio); | |
| 712 heap->RecordData(PageSpace::kGCTimeFraction, | |
| 713 garbage_collection_time_fraction); | |
| 714 heap->RecordData(PageSpace::kAllowedGrowth, grow_heap_); | 722 heap->RecordData(PageSpace::kAllowedGrowth, grow_heap_); |
| 715 last_usage_ = after; | 723 last_usage_ = after; |
| 716 } | 724 } |
| 717 | 725 |
| 718 | 726 |
| 719 PageSpaceGarbageCollectionHistory::PageSpaceGarbageCollectionHistory() | 727 PageSpaceGarbageCollectionHistory::PageSpaceGarbageCollectionHistory() |
| 720 : index_(0) { | 728 : index_(0) { |
| 721 for (intptr_t i = 0; i < kHistoryLength; i++) { | 729 for (intptr_t i = 0; i < kHistoryLength; i++) { |
| 722 start_[i] = 0; | 730 start_[i] = 0; |
| 723 end_[i] = 0; | 731 end_[i] = 0; |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 753 return 0; | 761 return 0; |
| 754 } else { | 762 } else { |
| 755 ASSERT(total_time >= gc_time); | 763 ASSERT(total_time >= gc_time); |
| 756 int result= static_cast<int>((static_cast<double>(gc_time) / | 764 int result= static_cast<int>((static_cast<double>(gc_time) / |
| 757 static_cast<double>(total_time)) * 100); | 765 static_cast<double>(total_time)) * 100); |
| 758 return result; | 766 return result; |
| 759 } | 767 } |
| 760 } | 768 } |
| 761 | 769 |
| 762 } // namespace dart | 770 } // namespace dart |
| OLD | NEW |