Commit 5b783479 authored by Mythri A's avatar Mythri A Committed by Commit Bot

[turboprop] Reduce BytecodeBudgetInterrupt overhead from Turboprop

Earlier we used the same interrupt budget always and waited for higher
number of ticks when tiering up from Turboprop to TurboFan. On some of
the real world pages this adds a reasonable overhead for processing
these interrupts. This cl sets the interrupt budget to a higher value so
there are fewer interrupts. This cl:
1. Sets the interrupt budget on feedback cell to
FLAG_interrupt_budget * scale factor when we install optimized code.
2. Resets the budget to FLAG_interrupt_budget when there is a
deoptimization.
3. Updates the runtime profiler to remove the scaling of number of ticks
needed for optimization when tiering up from TP to TF.

On sheets benchmark, we spend 40-50ms when servicing interrupts from
Turboprop code. This change brings it down to ~7ms. We also see
improvements on other pages.


Bug: v8:9684
Change-Id: Ia3e5e998d1fff44f2e08a240a8769b7ebe794da2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2696661
Commit-Queue: Mythri Alle <mythria@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72906}
parent 96bfcfb8
......@@ -943,7 +943,8 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeCache(
if (osr_offset.IsNone() && function->has_feedback_vector()) {
FeedbackVector feedback_vector = function->feedback_vector();
feedback_vector.EvictOptimizedCodeMarkedForDeoptimization(
function->shared(), "GetCodeFromOptimizedCodeCache");
function->raw_feedback_cell(), function->shared(),
"GetCodeFromOptimizedCodeCache");
code = feedback_vector.optimized_code();
} else if (!osr_offset.IsNone()) {
code = function->context()
......@@ -995,7 +996,8 @@ void InsertCodeIntoOptimizedCodeCache(
if (compilation_info->osr_offset().IsNone()) {
Handle<FeedbackVector> vector =
handle(function->feedback_vector(), function->GetIsolate());
FeedbackVector::SetOptimizedCode(vector, code);
FeedbackVector::SetOptimizedCode(vector, code,
function->raw_feedback_cell());
} else {
DCHECK(CodeKindCanOSR(kind));
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
......@@ -3266,7 +3268,8 @@ void Compiler::PostInstantiation(Handle<JSFunction> function) {
// deoptimized the code on the feedback vector. So check for any
// deoptimized code just before installing it on the funciton.
function->feedback_vector().EvictOptimizedCodeMarkedForDeoptimization(
*shared, "new function from shared function info");
function->raw_feedback_cell(), *shared,
"new function from shared function info");
Code code = function->feedback_vector().optimized_code();
if (!code.is_null()) {
// Caching of optimized code enabled and optimized code found.
......
......@@ -437,7 +437,8 @@ void Deoptimizer::DeoptimizeFunction(JSFunction function, Code code) {
// The code in the function's optimized code feedback vector slot might
// be different from the code on the function - evict it if necessary.
function.feedback_vector().EvictOptimizedCodeMarkedForDeoptimization(
function.shared(), "unlinking code marked for deopt");
function.raw_feedback_cell(), function.shared(),
"unlinking code marked for deopt");
if (!code.deopt_already_counted()) {
code.set_deopt_already_counted(true);
}
......
......@@ -234,7 +234,7 @@ bool RuntimeProfiler::MaybeOSR(JSFunction function, UnoptimizedFrame* frame) {
// OSR should happen roughly at the same with or without FLAG_turboprop.
// Turboprop has much lower interrupt budget so scale the ticks accordingly.
int scale_factor =
FLAG_turboprop ? FLAG_ticks_scale_factor_for_top_tier : 1;
FLAG_turboprop ? FLAG_interrupt_budget_scale_factor_for_top_tier : 1;
int64_t scaled_ticks = static_cast<int64_t>(ticks) / scale_factor;
int64_t allowance = kOSRBytecodeSizeAllowanceBase +
scaled_ticks * kOSRBytecodeSizeAllowancePerTick;
......@@ -269,8 +269,6 @@ OptimizationReason RuntimeProfiler::ShouldOptimize(JSFunction function,
}
int ticks = function.feedback_vector().profiler_ticks();
bool active_tier_is_turboprop = function.ActiveTierIsMidtierTurboprop();
int scale_factor =
active_tier_is_turboprop ? FLAG_ticks_scale_factor_for_top_tier : 1;
int ticks_for_optimization =
kProfilerTicksBeforeOptimization +
(bytecode.length() / kBytecodeSizeAllowancePerTick);
......@@ -285,7 +283,6 @@ OptimizationReason RuntimeProfiler::ShouldOptimize(JSFunction function,
std::min(global_ticks_diff / kMidTierGlobalTicksScaleFactor,
kMaxAdditionalMidTierGlobalTicks);
}
ticks_for_optimization *= scale_factor;
if (ticks >= ticks_for_optimization) {
return OptimizationReason::kHotAndStable;
} else if (ShouldOptimizeAsSmallFunction(bytecode.length(), ticks,
......
......@@ -585,10 +585,10 @@ DEFINE_VALUE_IMPLICATION(turboprop, interrupt_budget, 14 * KB)
DEFINE_VALUE_IMPLICATION(turboprop, reuse_opt_code_count, 2)
DEFINE_UINT_READONLY(max_minimorphic_map_checks, 4,
"max number of map checks to perform in minimorphic state")
// Since Turboprop uses much lower value for interrupt budget, we need to wait
// for a higher number of ticks to tierup to Turbofan roughly match the default.
// The default of 10 is approximately the ration of TP to TF interrupt budget.
DEFINE_INT(ticks_scale_factor_for_top_tier, 10,
// The scale factor determines the interrupt budget when tiering up from
// Turboprop to TurboFan. The default of 10 is approximately the ratio of
// Turboprop to the TurboFan interrupt budget.
DEFINE_INT(interrupt_budget_scale_factor_for_top_tier, 10,
"scale factor for profiler ticks when tiering up from midtier")
// Flags for Sparkplug
......
......@@ -55,9 +55,6 @@ void FeedbackCell::SetInitialInterruptBudget() {
}
}
void FeedbackCell::SetInterruptBudget() {
set_interrupt_budget(FLAG_interrupt_budget);
}
void FeedbackCell::IncrementClosureCount(Isolate* isolate) {
ReadOnlyRoots r(isolate);
......
......@@ -39,7 +39,6 @@ class FeedbackCell : public TorqueGeneratedFeedbackCell<FeedbackCell, Struct> {
HeapObject target)>>
gc_notify_updated_slot = base::nullopt);
inline void SetInitialInterruptBudget();
inline void SetInterruptBudget();
// The closure count is encoded in the cell's map, which distinguishes
// between zero, one, or many closures. This function records a new closure
......
......@@ -381,7 +381,8 @@ void FeedbackVector::SaturatingIncrementProfilerTicks() {
// static
void FeedbackVector::SetOptimizedCode(Handle<FeedbackVector> vector,
Handle<Code> code) {
Handle<Code> code,
FeedbackCell feedback_cell) {
DCHECK(CodeKindIsOptimizedJSFunction(code->kind()));
// We should only set optimized code only when there is no valid optimized
// code or we are tiering up.
......@@ -400,14 +401,40 @@ void FeedbackVector::SetOptimizedCode(Handle<FeedbackVector> vector,
state = OptimizationTierBits::update(state, GetTierForCodeKind(code->kind()));
state = OptimizationMarkerBits::update(state, OptimizationMarker::kNone);
vector->set_flags(state);
// With FLAG_turboprop, we would have an interrupt budget necessary for
// tiering up to Turboprop code. Once we install turboprop code, set it to a
// higher value as required for tiering up from Turboprop to TurboFan.
if (FLAG_turboprop) {
FeedbackVector::SetInterruptBudget(feedback_cell);
}
}
void FeedbackVector::ClearOptimizedCode() {
// static
void FeedbackVector::SetInterruptBudget(FeedbackCell feedback_cell) {
DCHECK(feedback_cell.value().IsFeedbackVector());
FeedbackVector vector = FeedbackVector::cast(feedback_cell.value());
// Set the interrupt budget as required for tiering up to next level. Without
// Turboprop, this is used only to tier up to TurboFan and hence always set to
// FLAG_interrupt_budget. With Turboprop, we use this budget to both tier up
// to Turboprop and TurboFan. When there is no optimized code, set it to
// FLAG_interrupt_budget required for tiering up to Turboprop. When there is
// optimized code, set it to a higher value required for tiering up from
// Turboprop to TurboFan.
if (FLAG_turboprop && vector.has_optimized_code()) {
feedback_cell.set_interrupt_budget(
FLAG_interrupt_budget *
FLAG_interrupt_budget_scale_factor_for_top_tier);
} else {
feedback_cell.set_interrupt_budget(FLAG_interrupt_budget);
}
}
void FeedbackVector::ClearOptimizedCode(FeedbackCell feedback_cell) {
DCHECK(has_optimized_code());
DCHECK_NE(optimization_tier(), OptimizationTier::kNone);
set_maybe_optimized_code(HeapObjectReference::ClearedValue(GetIsolate()),
kReleaseStore);
ClearOptimizationTier();
ClearOptimizationTier(feedback_cell);
}
void FeedbackVector::ClearOptimizationMarker() {
......@@ -420,10 +447,15 @@ void FeedbackVector::SetOptimizationMarker(OptimizationMarker marker) {
set_flags(state);
}
void FeedbackVector::ClearOptimizationTier() {
void FeedbackVector::ClearOptimizationTier(FeedbackCell feedback_cell) {
int32_t state = flags();
state = OptimizationTierBits::update(state, OptimizationTier::kNone);
set_flags(state);
// We are discarding the optimized code, adjust the interrupt budget
// so we have the correct budget required for the tier up.
if (FLAG_turboprop) {
FeedbackVector::SetInterruptBudget(feedback_cell);
}
}
void FeedbackVector::InitializeOptimizationState() {
......@@ -436,10 +468,10 @@ void FeedbackVector::InitializeOptimizationState() {
}
void FeedbackVector::EvictOptimizedCodeMarkedForDeoptimization(
SharedFunctionInfo shared, const char* reason) {
FeedbackCell feedback_cell, SharedFunctionInfo shared, const char* reason) {
MaybeObject slot = maybe_optimized_code(kAcquireLoad);
if (slot->IsCleared()) {
ClearOptimizationTier();
ClearOptimizationTier(feedback_cell);
return;
}
......@@ -449,7 +481,7 @@ void FeedbackVector::EvictOptimizedCodeMarkedForDeoptimization(
if (!code.deopt_already_counted()) {
code.set_deopt_already_counted(true);
}
ClearOptimizedCode();
ClearOptimizedCode(feedback_cell);
}
}
......
......@@ -228,18 +228,24 @@ class FeedbackVector
inline OptimizationTier optimization_tier() const;
inline int global_ticks_at_last_runtime_profiler_interrupt() const;
inline void set_global_ticks_at_last_runtime_profiler_interrupt(int ticks);
void ClearOptimizedCode();
void EvictOptimizedCodeMarkedForDeoptimization(SharedFunctionInfo shared,
void ClearOptimizedCode(FeedbackCell feedback_cell);
void EvictOptimizedCodeMarkedForDeoptimization(FeedbackCell feedback_cell,
SharedFunctionInfo shared,
const char* reason);
static void SetOptimizedCode(Handle<FeedbackVector> vector,
Handle<Code> code);
static void SetOptimizedCode(Handle<FeedbackVector> vector, Handle<Code> code,
FeedbackCell feedback_cell);
void SetOptimizationMarker(OptimizationMarker marker);
void ClearOptimizationTier();
void ClearOptimizationTier(FeedbackCell feedback_cell);
void InitializeOptimizationState();
// Clears the optimization marker in the feedback vector.
void ClearOptimizationMarker();
// Sets the interrupt budget based on the optimized code available on the
// feedback vector. This function expects that the feedback cell contains a
// feedback vector.
static void SetInterruptBudget(FeedbackCell feedback_cell);
// Conversion from a slot to an integer index to the underlying array.
static int GetIndex(FeedbackSlot slot) { return slot.ToInt(); }
......
......@@ -66,6 +66,20 @@ bool JSFunction::IsMarkedForConcurrentOptimization() {
OptimizationMarker::kCompileOptimizedConcurrent;
}
void JSFunction::SetInterruptBudget() {
if (!has_feedback_vector()) {
DCHECK(shared().is_compiled());
int budget = FLAG_budget_for_feedback_vector_allocation;
if (FLAG_feedback_allocation_on_bytecode_size) {
budget = shared().GetBytecodeArray(GetIsolate()).length() *
FLAG_scale_factor_for_feedback_allocation;
}
raw_feedback_cell().set_interrupt_budget(budget);
return;
}
FeedbackVector::SetInterruptBudget(raw_feedback_cell());
}
void JSFunction::MarkForOptimization(ConcurrencyMode mode) {
Isolate* isolate = GetIsolate();
if (!isolate->concurrent_recompilation_enabled() ||
......
......@@ -303,9 +303,7 @@ void JSFunction::EnsureClosureFeedbackCellArray(
if (V8_UNLIKELY(FLAG_feedback_allocation_on_bytecode_size) &&
(reset_budget_for_feedback_allocation ||
!has_closure_feedback_cell_array)) {
int budget = function->shared().GetBytecodeArray(isolate).length() *
FLAG_scale_factor_for_feedback_allocation;
function->raw_feedback_cell().set_interrupt_budget(budget);
function->SetInterruptBudget();
}
if (has_closure_feedback_cell_array) {
......@@ -323,9 +321,8 @@ void JSFunction::EnsureClosureFeedbackCellArray(
if (function->raw_feedback_cell() == isolate->heap()->many_closures_cell()) {
Handle<FeedbackCell> feedback_cell =
isolate->factory()->NewOneClosureCell(feedback_cell_array);
feedback_cell->set_interrupt_budget(
function->raw_feedback_cell().interrupt_budget());
function->set_raw_feedback_cell(*feedback_cell, kReleaseStore);
function->SetInterruptBudget();
} else {
function->raw_feedback_cell().set_value(*feedback_cell_array,
kReleaseStore);
......@@ -355,7 +352,7 @@ void JSFunction::EnsureFeedbackVector(Handle<JSFunction> function,
DCHECK(function->raw_feedback_cell() !=
isolate->heap()->many_closures_cell());
function->raw_feedback_cell().set_value(*feedback_vector, kReleaseStore);
function->raw_feedback_cell().SetInterruptBudget();
function->SetInterruptBudget();
}
// static
......
......@@ -157,6 +157,10 @@ class JSFunction : public JSFunctionOrBoundFunction {
// Clears the optimization marker in the function's feedback vector.
inline void ClearOptimizationMarker();
// Sets the interrupt budget based on whether the function has a feedback
// vector and any optimized code.
inline void SetInterruptBudget();
// If slack tracking is active, it computes instance size of the initial map
// with minimum permissible object slack. If it is not active, it simply
// returns the initial map's instance size.
......
......@@ -182,7 +182,8 @@ RUNTIME_FUNCTION(Runtime_HealOptimizedCodeSlot) {
DCHECK(function->shared().is_compiled());
function->feedback_vector().EvictOptimizedCodeMarkedForDeoptimization(
function->shared(), "Runtime_HealOptimizedCodeSlot");
function->raw_feedback_cell(), function->shared(),
"Runtime_HealOptimizedCodeSlot");
return function->code();
}
......
......@@ -331,7 +331,7 @@ RUNTIME_FUNCTION(Runtime_BytecodeBudgetInterruptFromBytecode) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
function->raw_feedback_cell().set_interrupt_budget(FLAG_interrupt_budget);
function->SetInterruptBudget();
if (!function->has_feedback_vector()) {
IsCompiledScope is_compiled_scope(
function->shared().is_compiled_scope(isolate));
......@@ -362,7 +362,7 @@ RUNTIME_FUNCTION(Runtime_BytecodeBudgetInterruptFromCode) {
DCHECK(feedback_cell->value().IsFeedbackVector());
feedback_cell->set_interrupt_budget(FLAG_interrupt_budget);
FeedbackVector::SetInterruptBudget(*feedback_cell);
SealHandleScope shs(isolate);
isolate->counters()->runtime_profiler_ticks()->Increment();
......
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