Commit b8c3ee40 authored by hpayer@chromium.org's avatar hpayer@chromium.org

Tenure allocation sites only when semi-space is maximum size.

BUG=
R=mvstanton@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21623 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent adeaedf5
...@@ -101,6 +101,7 @@ Heap::Heap() ...@@ -101,6 +101,7 @@ Heap::Heap()
promotion_rate_(0), promotion_rate_(0),
semi_space_copied_object_size_(0), semi_space_copied_object_size_(0),
semi_space_copied_rate_(0), semi_space_copied_rate_(0),
maximum_size_scavenges_(0),
max_gc_pause_(0.0), max_gc_pause_(0.0),
total_gc_time_ms_(0.0), total_gc_time_ms_(0.0),
max_alive_after_gc_(0), max_alive_after_gc_(0),
...@@ -438,6 +439,13 @@ void Heap::GarbageCollectionPrologue() { ...@@ -438,6 +439,13 @@ void Heap::GarbageCollectionPrologue() {
if (isolate()->concurrent_osr_enabled()) { if (isolate()->concurrent_osr_enabled()) {
isolate()->optimizing_compiler_thread()->AgeBufferedOsrJobs(); isolate()->optimizing_compiler_thread()->AgeBufferedOsrJobs();
} }
if (new_space_.IsAtMaximumCapacity()) {
maximum_size_scavenges_++;
} else {
maximum_size_scavenges_ = 0;
}
CheckNewSpaceExpansionCriteria();
} }
...@@ -485,12 +493,19 @@ void Heap::ProcessPretenuringFeedback() { ...@@ -485,12 +493,19 @@ void Heap::ProcessPretenuringFeedback() {
// If the scratchpad overflowed, we have to iterate over the allocation // If the scratchpad overflowed, we have to iterate over the allocation
// sites list. // sites list.
// TODO(hpayer): We iterate over the whole list of allocation sites when
// we grew to the maximum semi-space size to deopt maybe tenured
// allocation sites. We could hold the maybe tenured allocation sites
// in a seperate data structure if this is a performance problem.
bool use_scratchpad = bool use_scratchpad =
allocation_sites_scratchpad_length_ < kAllocationSiteScratchpadSize; allocation_sites_scratchpad_length_ < kAllocationSiteScratchpadSize &&
new_space_.IsAtMaximumCapacity() &&
maximum_size_scavenges_ == 0;
int i = 0; int i = 0;
Object* list_element = allocation_sites_list(); Object* list_element = allocation_sites_list();
bool trigger_deoptimization = false; bool trigger_deoptimization = false;
bool maximum_size_scavenge = MaximumSizeScavenge();
while (use_scratchpad ? while (use_scratchpad ?
i < allocation_sites_scratchpad_length_ : i < allocation_sites_scratchpad_length_ :
list_element->IsAllocationSite()) { list_element->IsAllocationSite()) {
...@@ -500,14 +515,17 @@ void Heap::ProcessPretenuringFeedback() { ...@@ -500,14 +515,17 @@ void Heap::ProcessPretenuringFeedback() {
allocation_mementos_found += site->memento_found_count(); allocation_mementos_found += site->memento_found_count();
if (site->memento_found_count() > 0) { if (site->memento_found_count() > 0) {
active_allocation_sites++; active_allocation_sites++;
if (site->DigestPretenuringFeedback(maximum_size_scavenge)) {
trigger_deoptimization = true;
}
if (site->GetPretenureMode() == TENURED) {
tenure_decisions++;
} else {
dont_tenure_decisions++;
}
allocation_sites++;
} }
if (site->DigestPretenuringFeedback()) trigger_deoptimization = true;
if (site->GetPretenureMode() == TENURED) {
tenure_decisions++;
} else {
dont_tenure_decisions++;
}
allocation_sites++;
if (use_scratchpad) { if (use_scratchpad) {
i++; i++;
} else { } else {
...@@ -1433,8 +1451,6 @@ void Heap::Scavenge() { ...@@ -1433,8 +1451,6 @@ void Heap::Scavenge() {
// Used for updating survived_since_last_expansion_ at function end. // Used for updating survived_since_last_expansion_ at function end.
intptr_t survived_watermark = PromotedSpaceSizeOfObjects(); intptr_t survived_watermark = PromotedSpaceSizeOfObjects();
CheckNewSpaceExpansionCriteria();
SelectScavengingVisitorsTable(); SelectScavengingVisitorsTable();
incremental_marking()->PrepareForScavenge(); incremental_marking()->PrepareForScavenge();
......
...@@ -1359,6 +1359,10 @@ class Heap { ...@@ -1359,6 +1359,10 @@ class Heap {
void DeoptMarkedAllocationSites(); void DeoptMarkedAllocationSites();
bool MaximumSizeScavenge() {
return maximum_size_scavenges_ > 0;
}
// ObjectStats are kept in two arrays, counts and sizes. Related stats are // ObjectStats are kept in two arrays, counts and sizes. Related stats are
// stored in a contiguous linear buffer. Stats groups are stored one after // stored in a contiguous linear buffer. Stats groups are stored one after
// another. // another.
...@@ -2019,6 +2023,12 @@ class Heap { ...@@ -2019,6 +2023,12 @@ class Heap {
intptr_t semi_space_copied_object_size_; intptr_t semi_space_copied_object_size_;
double semi_space_copied_rate_; double semi_space_copied_rate_;
// This is the pretenuring trigger for allocation sites that are in maybe
// tenure state. When we switched to the maximum new space size we deoptimize
// the code that belongs to the allocation site and derive the lifetime
// of the allocation site.
unsigned int maximum_size_scavenges_;
// TODO(hpayer): Allocation site pretenuring may make this method obsolete. // TODO(hpayer): Allocation site pretenuring may make this method obsolete.
// Re-visit incremental marking heuristics. // Re-visit incremental marking heuristics.
bool IsHighSurvivalRate() { bool IsHighSurvivalRate() {
......
...@@ -3030,7 +3030,6 @@ void MarkCompactCollector::EvacuateNewSpace() { ...@@ -3030,7 +3030,6 @@ void MarkCompactCollector::EvacuateNewSpace() {
// sweep collection by failing allocations. But since we are already in // sweep collection by failing allocations. But since we are already in
// a mark-sweep allocation, there is no sense in trying to trigger one. // a mark-sweep allocation, there is no sense in trying to trigger one.
AlwaysAllocateScope scope(isolate()); AlwaysAllocateScope scope(isolate());
heap()->CheckNewSpaceExpansionCriteria();
NewSpace* new_space = heap()->new_space(); NewSpace* new_space = heap()->new_space();
......
...@@ -1577,42 +1577,60 @@ inline void AllocationSite::IncrementMementoCreateCount() { ...@@ -1577,42 +1577,60 @@ inline void AllocationSite::IncrementMementoCreateCount() {
} }
inline bool AllocationSite::DigestPretenuringFeedback() { inline bool AllocationSite::MakePretenureDecision(
bool decision_changed = false; PretenureDecision current_decision,
double ratio,
bool maximum_size_scavenge) {
// Here we just allow state transitions from undecided or maybe tenure
// to don't tenure, maybe tenure, or tenure.
if ((current_decision == kUndecided || current_decision == kMaybeTenure)) {
if (ratio >= kPretenureRatio) {
// We just transition into tenure state when the semi-space was at
// maximum capacity.
if (maximum_size_scavenge) {
set_deopt_dependent_code(true);
set_pretenure_decision(kTenure);
// Currently we just need to deopt when we make a state transition to
// tenure.
return true;
}
set_pretenure_decision(kMaybeTenure);
} else {
set_pretenure_decision(kDontTenure);
}
}
return false;
}
inline bool AllocationSite::DigestPretenuringFeedback(
bool maximum_size_scavenge) {
bool deopt = false;
int create_count = memento_create_count(); int create_count = memento_create_count();
int found_count = memento_found_count(); int found_count = memento_found_count();
bool minimum_mementos_created = create_count >= kPretenureMinimumCreated; bool minimum_mementos_created = create_count >= kPretenureMinimumCreated;
double ratio = double ratio =
minimum_mementos_created || FLAG_trace_pretenuring_statistics ? minimum_mementos_created || FLAG_trace_pretenuring_statistics ?
static_cast<double>(found_count) / create_count : 0.0; static_cast<double>(found_count) / create_count : 0.0;
PretenureFlag current_mode = GetPretenureMode(); PretenureDecision current_decision = pretenure_decision();
// TODO(hpayer): Add an intermediate state MAYBE_TENURE which collects if (minimum_mementos_created) {
// more lifetime feedback for tenuring candidates. In the meantime, we deopt = MakePretenureDecision(
// just allow transitions from undecided to tenured or not tenured. current_decision, ratio, maximum_size_scavenge);
if (minimum_mementos_created && pretenure_decision() == kUndecided) {
PretenureDecision result = ratio >= kPretenureRatio
? kTenure
: kDontTenure;
set_pretenure_decision(result);
if (current_mode != GetPretenureMode()) {
decision_changed = true;
set_deopt_dependent_code(true);
}
} }
if (FLAG_trace_pretenuring_statistics) { if (FLAG_trace_pretenuring_statistics) {
PrintF( PrintF(
"AllocationSite(%p): (created, found, ratio) (%d, %d, %f) %s => %s\n", "AllocationSite(%p): (created, found, ratio) (%d, %d, %f) %s => %s\n",
static_cast<void*>(this), create_count, found_count, ratio, static_cast<void*>(this), create_count, found_count, ratio,
current_mode == TENURED ? "tenured" : "not tenured", PretenureDecisionName(current_decision),
GetPretenureMode() == TENURED ? "tenured" : "not tenured"); PretenureDecisionName(pretenure_decision()));
} }
// Clear feedback calculation fields until the next gc. // Clear feedback calculation fields until the next gc.
set_memento_found_count(0); set_memento_found_count(0);
set_memento_create_count(0); set_memento_create_count(0);
return decision_changed; return deopt;
} }
......
...@@ -13303,6 +13303,19 @@ void AllocationSite::AddDependentCompilationInfo(Handle<AllocationSite> site, ...@@ -13303,6 +13303,19 @@ void AllocationSite::AddDependentCompilationInfo(Handle<AllocationSite> site,
} }
const char* AllocationSite::PretenureDecisionName(PretenureDecision decision) {
switch (decision) {
case kUndecided: return "undecided";
case kDontTenure: return "don't tenure";
case kMaybeTenure: return "maybe tenure";
case kTenure: return "tenure";
case kZombie: return "zombie";
default: UNREACHABLE();
}
return NULL;
}
void JSObject::UpdateAllocationSite(Handle<JSObject> object, void JSObject::UpdateAllocationSite(Handle<JSObject> object,
ElementsKind to_kind) { ElementsKind to_kind) {
if (!object->IsJSArray()) return; if (!object->IsJSArray()) return;
......
...@@ -8412,11 +8412,14 @@ class AllocationSite: public Struct { ...@@ -8412,11 +8412,14 @@ class AllocationSite: public Struct {
enum PretenureDecision { enum PretenureDecision {
kUndecided = 0, kUndecided = 0,
kDontTenure = 1, kDontTenure = 1,
kTenure = 2, kMaybeTenure = 2,
kZombie = 3, kTenure = 3,
kZombie = 4,
kLastPretenureDecisionValue = kZombie kLastPretenureDecisionValue = kZombie
}; };
const char* PretenureDecisionName(PretenureDecision decision);
DECL_ACCESSORS(transition_info, Object) DECL_ACCESSORS(transition_info, Object)
// nested_site threads a list of sites that represent nested literals // nested_site threads a list of sites that represent nested literals
// walked in a particular order. So [[1, 2], 1, 2] will have one // walked in a particular order. So [[1, 2], 1, 2] will have one
...@@ -8438,8 +8441,8 @@ class AllocationSite: public Struct { ...@@ -8438,8 +8441,8 @@ class AllocationSite: public Struct {
class DoNotInlineBit: public BitField<bool, 29, 1> {}; class DoNotInlineBit: public BitField<bool, 29, 1> {};
// Bitfields for pretenure_data // Bitfields for pretenure_data
class MementoFoundCountBits: public BitField<int, 0, 27> {}; class MementoFoundCountBits: public BitField<int, 0, 26> {};
class PretenureDecisionBits: public BitField<PretenureDecision, 27, 2> {}; class PretenureDecisionBits: public BitField<PretenureDecision, 26, 3> {};
class DeoptDependentCodeBit: public BitField<bool, 29, 1> {}; class DeoptDependentCodeBit: public BitField<bool, 29, 1> {};
STATIC_ASSERT(PretenureDecisionBits::kMax >= kLastPretenureDecisionValue); STATIC_ASSERT(PretenureDecisionBits::kMax >= kLastPretenureDecisionValue);
...@@ -8502,7 +8505,11 @@ class AllocationSite: public Struct { ...@@ -8502,7 +8505,11 @@ class AllocationSite: public Struct {
inline void MarkZombie(); inline void MarkZombie();
inline bool DigestPretenuringFeedback(); inline bool MakePretenureDecision(PretenureDecision current_decision,
double ratio,
bool maximum_size_scavenge);
inline bool DigestPretenuringFeedback(bool maximum_size_scavenge);
ElementsKind GetElementsKind() { ElementsKind GetElementsKind() {
ASSERT(!SitePointsToLiteral()); ASSERT(!SitePointsToLiteral());
......
...@@ -2499,6 +2499,10 @@ class NewSpace : public Space { ...@@ -2499,6 +2499,10 @@ class NewSpace : public Space {
return to_space_.MaximumCapacity(); return to_space_.MaximumCapacity();
} }
bool IsAtMaximumCapacity() {
return Capacity() == MaximumCapacity();
}
// Returns the initial capacity of a semispace. // Returns the initial capacity of a semispace.
int InitialCapacity() { int InitialCapacity() {
ASSERT(to_space_.InitialCapacity() == from_space_.InitialCapacity()); ASSERT(to_space_.InitialCapacity() == from_space_.InitialCapacity());
......
...@@ -2200,6 +2200,11 @@ TEST(OptimizedPretenuringAllocationFolding) { ...@@ -2200,6 +2200,11 @@ TEST(OptimizedPretenuringAllocationFolding) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
i::OS::SNPrintF( i::OS::SNPrintF(
source, source,
...@@ -2244,6 +2249,11 @@ TEST(OptimizedPretenuringObjectArrayLiterals) { ...@@ -2244,6 +2249,11 @@ TEST(OptimizedPretenuringObjectArrayLiterals) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
i::OS::SNPrintF( i::OS::SNPrintF(
source, source,
...@@ -2279,6 +2289,12 @@ TEST(OptimizedPretenuringMixedInObjectProperties) { ...@@ -2279,6 +2289,12 @@ TEST(OptimizedPretenuringMixedInObjectProperties) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
i::OS::SNPrintF( i::OS::SNPrintF(
source, source,
...@@ -2320,6 +2336,11 @@ TEST(OptimizedPretenuringDoubleArrayProperties) { ...@@ -2320,6 +2336,11 @@ TEST(OptimizedPretenuringDoubleArrayProperties) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
i::OS::SNPrintF( i::OS::SNPrintF(
source, source,
...@@ -2355,6 +2376,11 @@ TEST(OptimizedPretenuringdoubleArrayLiterals) { ...@@ -2355,6 +2376,11 @@ TEST(OptimizedPretenuringdoubleArrayLiterals) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
i::OS::SNPrintF( i::OS::SNPrintF(
source, source,
...@@ -2390,6 +2416,11 @@ TEST(OptimizedPretenuringNestedMixedArrayLiterals) { ...@@ -2390,6 +2416,11 @@ TEST(OptimizedPretenuringNestedMixedArrayLiterals) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
i::OS::SNPrintF( i::OS::SNPrintF(
source, source,
...@@ -2434,6 +2465,11 @@ TEST(OptimizedPretenuringNestedObjectLiterals) { ...@@ -2434,6 +2465,11 @@ TEST(OptimizedPretenuringNestedObjectLiterals) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
i::OS::SNPrintF( i::OS::SNPrintF(
source, source,
...@@ -2478,6 +2514,11 @@ TEST(OptimizedPretenuringNestedDoubleLiterals) { ...@@ -2478,6 +2514,11 @@ TEST(OptimizedPretenuringNestedDoubleLiterals) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
i::OS::SNPrintF( i::OS::SNPrintF(
source, source,
...@@ -2530,6 +2571,11 @@ TEST(OptimizedPretenuringConstructorCalls) { ...@@ -2530,6 +2571,11 @@ TEST(OptimizedPretenuringConstructorCalls) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
// Call new is doing slack tracking for the first // Call new is doing slack tracking for the first
// JSFunction::kGenerousAllocationCount allocations, and we can't find // JSFunction::kGenerousAllocationCount allocations, and we can't find
...@@ -2576,6 +2622,11 @@ TEST(OptimizedPretenuringCallNew) { ...@@ -2576,6 +2622,11 @@ TEST(OptimizedPretenuringCallNew) {
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(CcTest::isolate());
// Grow new space unitl maximum capacity reached.
while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
CcTest::heap()->new_space()->Grow();
}
i::ScopedVector<char> source(1024); i::ScopedVector<char> source(1024);
// Call new is doing slack tracking for the first // Call new is doing slack tracking for the first
// JSFunction::kGenerousAllocationCount allocations, and we can't find // JSFunction::kGenerousAllocationCount allocations, and we can't find
......
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