promotion_rate_(0),
semi_space_copied_object_size_(0),
semi_space_copied_rate_(0),
+ maximum_size_scavenges_(0),
max_gc_pause_(0.0),
total_gc_time_ms_(0.0),
max_alive_after_gc_(0),
if (isolate()->concurrent_osr_enabled()) {
isolate()->optimizing_compiler_thread()->AgeBufferedOsrJobs();
}
+
+ if (new_space_.IsAtMaximumCapacity()) {
+ maximum_size_scavenges_++;
+ } else {
+ maximum_size_scavenges_ = 0;
+ }
+ CheckNewSpaceExpansionCriteria();
}
// If the scratchpad overflowed, we have to iterate over the allocation
// 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 =
- allocation_sites_scratchpad_length_ < kAllocationSiteScratchpadSize;
+ allocation_sites_scratchpad_length_ < kAllocationSiteScratchpadSize &&
+ new_space_.IsAtMaximumCapacity() &&
+ maximum_size_scavenges_ == 0;
int i = 0;
Object* list_element = allocation_sites_list();
bool trigger_deoptimization = false;
+ bool maximum_size_scavenge = MaximumSizeScavenge();
while (use_scratchpad ?
i < allocation_sites_scratchpad_length_ :
list_element->IsAllocationSite()) {
allocation_mementos_found += site->memento_found_count();
if (site->memento_found_count() > 0) {
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) {
i++;
} else {
// Used for updating survived_since_last_expansion_ at function end.
intptr_t survived_watermark = PromotedSpaceSizeOfObjects();
- CheckNewSpaceExpansionCriteria();
-
SelectScavengingVisitorsTable();
incremental_marking()->PrepareForScavenge();
void DeoptMarkedAllocationSites();
+ bool MaximumSizeScavenge() {
+ return maximum_size_scavenges_ > 0;
+ }
+
// ObjectStats are kept in two arrays, counts and sizes. Related stats are
// stored in a contiguous linear buffer. Stats groups are stored one after
// another.
intptr_t semi_space_copied_object_size_;
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.
// Re-visit incremental marking heuristics.
bool IsHighSurvivalRate() {
// sweep collection by failing allocations. But since we are already in
// a mark-sweep allocation, there is no sense in trying to trigger one.
AlwaysAllocateScope scope(isolate());
- heap()->CheckNewSpaceExpansionCriteria();
NewSpace* new_space = heap()->new_space();
}
-inline bool AllocationSite::DigestPretenuringFeedback() {
- bool decision_changed = false;
+inline bool AllocationSite::MakePretenureDecision(
+ 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 found_count = memento_found_count();
bool minimum_mementos_created = create_count >= kPretenureMinimumCreated;
double ratio =
minimum_mementos_created || FLAG_trace_pretenuring_statistics ?
static_cast<double>(found_count) / create_count : 0.0;
- PretenureFlag current_mode = GetPretenureMode();
-
- // TODO(hpayer): Add an intermediate state MAYBE_TENURE which collects
- // more lifetime feedback for tenuring candidates. In the meantime, we
- // just allow transitions from undecided to tenured or not tenured.
- 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);
- }
+ PretenureDecision current_decision = pretenure_decision();
+
+ if (minimum_mementos_created) {
+ deopt = MakePretenureDecision(
+ current_decision, ratio, maximum_size_scavenge);
}
if (FLAG_trace_pretenuring_statistics) {
PrintF(
"AllocationSite(%p): (created, found, ratio) (%d, %d, %f) %s => %s\n",
static_cast<void*>(this), create_count, found_count, ratio,
- current_mode == TENURED ? "tenured" : "not tenured",
- GetPretenureMode() == TENURED ? "tenured" : "not tenured");
+ PretenureDecisionName(current_decision),
+ PretenureDecisionName(pretenure_decision()));
}
// Clear feedback calculation fields until the next gc.
set_memento_found_count(0);
set_memento_create_count(0);
- return decision_changed;
+ return deopt;
}
}
+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,
ElementsKind to_kind) {
if (!object->IsJSArray()) return;
enum PretenureDecision {
kUndecided = 0,
kDontTenure = 1,
- kTenure = 2,
- kZombie = 3,
+ kMaybeTenure = 2,
+ kTenure = 3,
+ kZombie = 4,
kLastPretenureDecisionValue = kZombie
};
+ const char* PretenureDecisionName(PretenureDecision decision);
+
DECL_ACCESSORS(transition_info, Object)
// nested_site threads a list of sites that represent nested literals
// walked in a particular order. So [[1, 2], 1, 2] will have one
class DoNotInlineBit: public BitField<bool, 29, 1> {};
// Bitfields for pretenure_data
- class MementoFoundCountBits: public BitField<int, 0, 27> {};
- class PretenureDecisionBits: public BitField<PretenureDecision, 27, 2> {};
+ class MementoFoundCountBits: public BitField<int, 0, 26> {};
+ class PretenureDecisionBits: public BitField<PretenureDecision, 26, 3> {};
class DeoptDependentCodeBit: public BitField<bool, 29, 1> {};
STATIC_ASSERT(PretenureDecisionBits::kMax >= kLastPretenureDecisionValue);
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() {
ASSERT(!SitePointsToLiteral());
return to_space_.MaximumCapacity();
}
+ bool IsAtMaximumCapacity() {
+ return Capacity() == MaximumCapacity();
+ }
+
// Returns the initial capacity of a semispace.
int InitialCapacity() {
ASSERT(to_space_.InitialCapacity() == from_space_.InitialCapacity());
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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::OS::SNPrintF(
source,
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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::OS::SNPrintF(
source,
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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::OS::SNPrintF(
source,
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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::OS::SNPrintF(
source,
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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::OS::SNPrintF(
source,
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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::OS::SNPrintF(
source,
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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::OS::SNPrintF(
source,
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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::OS::SNPrintF(
source,
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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);
// Call new is doing slack tracking for the first
// JSFunction::kGenerousAllocationCount allocations, and we can't find
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
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);
// Call new is doing slack tracking for the first
// JSFunction::kGenerousAllocationCount allocations, and we can't find