%PDF- %PDF-
| Direktori : /home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/ |
| Current File : //home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/pretenuring-handler.cc |
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/heap/pretenuring-handler.h"
#include "src/common/globals.h"
#include "src/execution/isolate.h"
#include "src/flags/flags.h"
#include "src/handles/global-handles-inl.h"
#include "src/heap/gc-tracer-inl.h"
#include "src/heap/new-spaces.h"
#include "src/objects/allocation-site-inl.h"
namespace v8 {
namespace internal {
PretenuringHandler::PretenuringHandler(Heap* heap)
: heap_(heap), global_pretenuring_feedback_(kInitialFeedbackCapacity) {}
PretenuringHandler::~PretenuringHandler() = default;
namespace {
static constexpr int kMinMementoCount = 100;
double GetPretenuringRatioThreshold(size_t new_space_capacity) {
static constexpr double kScavengerPretenureRatio = 0.85;
// MinorMS allows for a much larger new space, thus we require a lower
// survival rate for pretenuring.
static constexpr double kMinorMSPretenureMaxRatio = 0.8;
static constexpr double kMinorMSMinCapacity = 16 * MB;
if (!v8_flags.minor_ms) return kScavengerPretenureRatio;
if (new_space_capacity <= kMinorMSMinCapacity)
return kMinorMSPretenureMaxRatio;
// When capacity is 64MB, the pretenuring ratio would be 0.2.
return kMinorMSPretenureMaxRatio * kMinorMSMinCapacity / new_space_capacity;
}
inline bool MakePretenureDecision(
Tagged<AllocationSite> site,
AllocationSite::PretenureDecision current_decision, double ratio,
bool new_space_capacity_was_above_pretenuring_threshold,
size_t new_space_capacity) {
// Here we just allow state transitions from undecided or maybe tenure
// to don't tenure, maybe tenure, or tenure.
if ((current_decision == AllocationSite::kUndecided ||
current_decision == AllocationSite::kMaybeTenure)) {
if (ratio >= GetPretenuringRatioThreshold(new_space_capacity)) {
// We just transition into tenure state when the semi-space was at
// maximum capacity.
if (new_space_capacity_was_above_pretenuring_threshold) {
site->set_deopt_dependent_code(true);
site->set_pretenure_decision(AllocationSite::kTenure);
// Currently we just need to deopt when we make a state transition to
// tenure.
return true;
}
site->set_pretenure_decision(AllocationSite::kMaybeTenure);
} else {
site->set_pretenure_decision(AllocationSite::kDontTenure);
}
}
return false;
}
// Clear feedback calculation fields until the next gc.
inline void ResetPretenuringFeedback(Tagged<AllocationSite> site) {
site->set_memento_found_count(0);
site->set_memento_create_count(0);
}
inline bool DigestPretenuringFeedback(
Isolate* isolate, Tagged<AllocationSite> site,
bool new_space_capacity_was_above_pretenuring_threshold,
size_t new_space_capacity) {
bool deopt = false;
int create_count = site->memento_create_count();
int found_count = site->memento_found_count();
bool minimum_mementos_created = create_count >= kMinMementoCount;
double ratio =
minimum_mementos_created || v8_flags.trace_pretenuring_statistics
? static_cast<double>(found_count) / create_count
: 0.0;
AllocationSite::PretenureDecision current_decision =
site->pretenure_decision();
if (minimum_mementos_created) {
deopt = MakePretenureDecision(
site, current_decision, ratio,
new_space_capacity_was_above_pretenuring_threshold, new_space_capacity);
}
if (v8_flags.trace_pretenuring_statistics) {
PrintIsolate(isolate,
"pretenuring: AllocationSite(%p): (created, found, ratio) "
"(%d, %d, %f) %s => %s\n",
reinterpret_cast<void*>(site.ptr()), create_count, found_count,
ratio, site->PretenureDecisionName(current_decision),
site->PretenureDecisionName(site->pretenure_decision()));
}
ResetPretenuringFeedback(site);
return deopt;
}
bool PretenureAllocationSiteManually(Isolate* isolate,
Tagged<AllocationSite> site) {
AllocationSite::PretenureDecision current_decision =
site->pretenure_decision();
bool deopt = true;
if (current_decision == AllocationSite::kUndecided ||
current_decision == AllocationSite::kMaybeTenure) {
site->set_deopt_dependent_code(true);
site->set_pretenure_decision(AllocationSite::kTenure);
} else {
deopt = false;
}
if (v8_flags.trace_pretenuring_statistics) {
PrintIsolate(isolate,
"pretenuring manually requested: AllocationSite(%p): "
"%s => %s\n",
reinterpret_cast<void*>(site.ptr()),
site->PretenureDecisionName(current_decision),
site->PretenureDecisionName(site->pretenure_decision()));
}
ResetPretenuringFeedback(site);
return deopt;
}
} // namespace
// static
int PretenuringHandler::GetMinMementoCountForTesting() {
return kMinMementoCount;
}
void PretenuringHandler::MergeAllocationSitePretenuringFeedback(
const PretenuringFeedbackMap& local_pretenuring_feedback) {
PtrComprCageBase cage_base(heap_->isolate());
Tagged<AllocationSite> site;
for (auto& site_and_count : local_pretenuring_feedback) {
site = site_and_count.first;
MapWord map_word = site->map_word(cage_base, kRelaxedLoad);
if (map_word.IsForwardingAddress()) {
site = AllocationSite::cast(map_word.ToForwardingAddress(site));
}
// We have not validated the allocation site yet, since we have not
// dereferenced the site during collecting information.
// This is an inlined check of AllocationMemento::IsValid.
if (!IsAllocationSite(site) || site->IsZombie()) continue;
const int value = static_cast<int>(site_and_count.second);
DCHECK_LT(0, value);
if (site->IncrementMementoFoundCount(value) >= kMinMementoCount) {
// For sites in the global map the count is accessed through the site.
global_pretenuring_feedback_.insert(std::make_pair(site, 0));
}
}
}
void PretenuringHandler::RemoveAllocationSitePretenuringFeedback(
Tagged<AllocationSite> site) {
global_pretenuring_feedback_.erase(site);
}
void PretenuringHandler::ProcessPretenuringFeedback(
size_t new_space_capacity_before_gc) {
// The minimum new space capacity from which allocation sites can be
// pretenured. A too small capacity means frequent GCs. Objects thus don't get
// a chance to die before being promoted, which may lead to wrong pretenuring
// decisions.
static constexpr size_t kDefaultMinNewSpaceCapacityForPretenuring =
8192 * KB * Heap::kPointerMultiplier;
DCHECK(heap_->tracer()->IsInAtomicPause());
if (!v8_flags.allocation_site_pretenuring) return;
const size_t min_new_space_capacity_for_pretenuring =
std::min(heap_->new_space()->MaximumCapacity(),
kDefaultMinNewSpaceCapacityForPretenuring);
bool trigger_deoptimization = false;
int tenure_decisions = 0;
int dont_tenure_decisions = 0;
int allocation_mementos_found = 0;
int allocation_sites = 0;
int active_allocation_sites = 0;
Tagged<AllocationSite> site;
// Step 1: Digest feedback for recorded allocation sites.
// This is the pretenuring trigger for allocation sites that are in maybe
// tenure state. When we switched to a large enough new space size we
// deoptimize the code that belongs to the allocation site and derive the
// lifetime of the allocation site.
bool new_space_was_above_pretenuring_threshold =
new_space_capacity_before_gc >= min_new_space_capacity_for_pretenuring;
for (auto& site_and_count : global_pretenuring_feedback_) {
allocation_sites++;
site = site_and_count.first;
// Count is always access through the site.
DCHECK_EQ(0, site_and_count.second);
int found_count = site->memento_found_count();
// An entry in the storage does not imply that the count is > 0 because
// allocation sites might have been reset due to too many objects dying
// in old space.
if (found_count > 0) {
DCHECK(IsAllocationSite(site));
active_allocation_sites++;
allocation_mementos_found += found_count;
if (DigestPretenuringFeedback(heap_->isolate(), site,
new_space_was_above_pretenuring_threshold,
new_space_capacity_before_gc)) {
trigger_deoptimization = true;
}
if (site->GetAllocationType() == AllocationType::kOld) {
tenure_decisions++;
} else {
dont_tenure_decisions++;
}
}
}
// Step 2: Pretenure allocation sites for manual requests.
if (allocation_sites_to_pretenure_) {
while (!allocation_sites_to_pretenure_->empty()) {
auto pretenure_site = allocation_sites_to_pretenure_->Pop();
if (PretenureAllocationSiteManually(heap_->isolate(), pretenure_site)) {
trigger_deoptimization = true;
}
}
allocation_sites_to_pretenure_.reset();
}
// Step 3: Deopt maybe tenured allocation sites if necessary.
// New space capacity was too low for pretenuring but is now above the
// threshold. Maybe tenured allocation sites may be pretenured on the next GC.
bool deopt_maybe_tenured = (heap_->NewSpaceTargetCapacity() >=
min_new_space_capacity_for_pretenuring) &&
!new_space_was_above_pretenuring_threshold;
if (deopt_maybe_tenured) {
heap_->ForeachAllocationSite(heap_->allocation_sites_list(),
[&allocation_sites, &trigger_deoptimization](
Tagged<AllocationSite> site) {
DCHECK(IsAllocationSite(site));
allocation_sites++;
if (site->IsMaybeTenure()) {
site->set_deopt_dependent_code(true);
trigger_deoptimization = true;
}
});
}
if (trigger_deoptimization) {
heap_->isolate()->stack_guard()->RequestDeoptMarkedAllocationSites();
}
if (v8_flags.trace_pretenuring_statistics &&
(allocation_mementos_found > 0 || tenure_decisions > 0 ||
dont_tenure_decisions > 0)) {
PrintIsolate(
heap_->isolate(),
"pretenuring: threshold=%.2f deopt_maybe_tenured=%d visited_sites=%d "
"active_sites=%d "
"mementos=%d tenured=%d not_tenured=%d\n",
GetPretenuringRatioThreshold(new_space_capacity_before_gc),
deopt_maybe_tenured ? 1 : 0, allocation_sites, active_allocation_sites,
allocation_mementos_found, tenure_decisions, dont_tenure_decisions);
}
global_pretenuring_feedback_.clear();
global_pretenuring_feedback_.reserve(kInitialFeedbackCapacity);
}
void PretenuringHandler::PretenureAllocationSiteOnNextCollection(
Tagged<AllocationSite> site) {
if (!allocation_sites_to_pretenure_) {
allocation_sites_to_pretenure_.reset(
new GlobalHandleVector<AllocationSite>(heap_));
}
allocation_sites_to_pretenure_->Push(site);
}
void PretenuringHandler::reset() { allocation_sites_to_pretenure_.reset(); }
} // namespace internal
} // namespace v8