mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-09 17:49:40 +00:00
LibJS: Put vector of regs+consts+locals+args in tail of ExecutionContext
By doing that we avoid doing separate allocation for each such vector, which was really expensive on js heavy websites. For example this change helps to get EC allocation down from ~17% to ~2% on Google Maps. This comes at cost of adding extra complexity to custom execution context allocator, because EC no longer has fixed size and we need to maintain a list of buckets.
This commit is contained in:
parent
c6cd03d7ca
commit
5a92929282
Notes:
github-actions[bot]
2025-04-24 08:32:21 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: 5a92929282
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4434
Reviewed-by: https://github.com/alimpfard
3 changed files with 110 additions and 34 deletions
|
@ -741,11 +741,11 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
|
||||||
|
|
||||||
auto& running_execution_context = vm().running_execution_context();
|
auto& running_execution_context = vm().running_execution_context();
|
||||||
u32 registers_and_constants_and_locals_count = executable.number_of_registers + executable.constants.size() + executable.local_variable_names.size();
|
u32 registers_and_constants_and_locals_count = executable.number_of_registers + executable.constants.size() + executable.local_variable_names.size();
|
||||||
VERIFY(registers_and_constants_and_locals_count <= running_execution_context.registers_and_constants_and_locals_and_arguments.size());
|
VERIFY(registers_and_constants_and_locals_count <= running_execution_context.registers_and_constants_and_locals_and_arguments_span().size());
|
||||||
|
|
||||||
TemporaryChange restore_running_execution_context { m_running_execution_context, &running_execution_context };
|
TemporaryChange restore_running_execution_context { m_running_execution_context, &running_execution_context };
|
||||||
TemporaryChange restore_arguments { m_arguments, running_execution_context.arguments() };
|
TemporaryChange restore_arguments { m_arguments, running_execution_context.arguments() };
|
||||||
TemporaryChange restore_registers_and_constants_and_locals { m_registers_and_constants_and_locals, running_execution_context.registers_and_constants_and_locals_and_arguments.span() };
|
TemporaryChange restore_registers_and_constants_and_locals { m_registers_and_constants_and_locals, running_execution_context.registers_and_constants_and_locals_and_arguments_span() };
|
||||||
|
|
||||||
reg(Register::accumulator()) = initial_accumulator_value;
|
reg(Register::accumulator()) = initial_accumulator_value;
|
||||||
reg(Register::return_value()) = js_special_empty_value();
|
reg(Register::return_value()) = js_special_empty_value();
|
||||||
|
@ -759,8 +759,9 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
|
||||||
|
|
||||||
running_execution_context.executable = &executable;
|
running_execution_context.executable = &executable;
|
||||||
|
|
||||||
|
auto* registers_and_constants_and_locals_and_arguments = running_execution_context.registers_and_constants_and_locals_and_arguments();
|
||||||
for (size_t i = 0; i < executable.constants.size(); ++i) {
|
for (size_t i = 0; i < executable.constants.size(); ++i) {
|
||||||
running_execution_context.registers_and_constants_and_locals_and_arguments[executable.number_of_registers + i] = executable.constants[i];
|
registers_and_constants_and_locals_and_arguments[executable.number_of_registers + i] = executable.constants[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
run_bytecode(entry_point.value_or(0));
|
run_bytecode(entry_point.value_or(0));
|
||||||
|
@ -768,7 +769,6 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
|
||||||
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter did run unit {:p}", &executable);
|
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter did run unit {:p}", &executable);
|
||||||
|
|
||||||
if constexpr (JS_BYTECODE_DEBUG) {
|
if constexpr (JS_BYTECODE_DEBUG) {
|
||||||
auto const& registers_and_constants_and_locals_and_arguments = running_execution_context.registers_and_constants_and_locals_and_arguments;
|
|
||||||
for (size_t i = 0; i < executable.number_of_registers; ++i) {
|
for (size_t i = 0; i < executable.number_of_registers; ++i) {
|
||||||
String value_string;
|
String value_string;
|
||||||
if (registers_and_constants_and_locals_and_arguments[i].is_special_empty_value())
|
if (registers_and_constants_and_locals_and_arguments[i].is_special_empty_value())
|
||||||
|
@ -788,8 +788,8 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
|
||||||
vm().finish_execution_generation();
|
vm().finish_execution_generation();
|
||||||
|
|
||||||
if (!exception.is_special_empty_value())
|
if (!exception.is_special_empty_value())
|
||||||
return { throw_completion(exception), running_execution_context.registers_and_constants_and_locals_and_arguments[0] };
|
return { throw_completion(exception), registers_and_constants_and_locals_and_arguments[0] };
|
||||||
return { return_value, running_execution_context.registers_and_constants_and_locals_and_arguments[0] };
|
return { return_value, registers_and_constants_and_locals_and_arguments[0] };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::enter_unwind_context()
|
void Interpreter::enter_unwind_context()
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
|
* Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
|
||||||
|
* Copyright (c) 2024-2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -15,39 +16,96 @@ namespace JS {
|
||||||
|
|
||||||
class ExecutionContextAllocator {
|
class ExecutionContextAllocator {
|
||||||
public:
|
public:
|
||||||
NonnullOwnPtr<ExecutionContext> allocate()
|
NonnullOwnPtr<ExecutionContext> allocate(u32 registers_and_constants_and_locals_count, u32 arguments_count)
|
||||||
{
|
{
|
||||||
if (m_execution_contexts.is_empty())
|
auto tail_size = registers_and_constants_and_locals_count + arguments_count;
|
||||||
return adopt_own(*new ExecutionContext);
|
|
||||||
void* slot = m_execution_contexts.take_last();
|
void* slot = nullptr;
|
||||||
return adopt_own(*new (slot) ExecutionContext);
|
if (tail_size <= 4 && !m_execution_contexts_with_4_tail.is_empty()) {
|
||||||
|
slot = m_execution_contexts_with_4_tail.take_last();
|
||||||
|
} else if (tail_size <= 16 && !m_execution_contexts_with_16_tail.is_empty()) {
|
||||||
|
slot = m_execution_contexts_with_16_tail.take_last();
|
||||||
|
} else if (tail_size <= 64 && !m_execution_contexts_with_64_tail.is_empty()) {
|
||||||
|
slot = m_execution_contexts_with_64_tail.take_last();
|
||||||
|
} else if (tail_size <= 128 && !m_execution_contexts_with_128_tail.is_empty()) {
|
||||||
|
slot = m_execution_contexts_with_128_tail.take_last();
|
||||||
|
} else if (tail_size <= 256 && !m_execution_contexts_with_256_tail.is_empty()) {
|
||||||
|
slot = m_execution_contexts_with_256_tail.take_last();
|
||||||
|
} else if (tail_size <= 512 && !m_execution_contexts_with_512_tail.is_empty()) {
|
||||||
|
slot = m_execution_contexts_with_512_tail.take_last();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot) {
|
||||||
|
return adopt_own(*new (slot) ExecutionContext(registers_and_constants_and_locals_count, arguments_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tail_allocation_size = [tail_size] -> u32 {
|
||||||
|
if (tail_size <= 4)
|
||||||
|
return 4;
|
||||||
|
if (tail_size <= 16)
|
||||||
|
return 16;
|
||||||
|
if (tail_size <= 64)
|
||||||
|
return 64;
|
||||||
|
if (tail_size <= 128)
|
||||||
|
return 128;
|
||||||
|
if (tail_size <= 256)
|
||||||
|
return 256;
|
||||||
|
if (tail_size <= 512)
|
||||||
|
return 512;
|
||||||
|
return tail_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto* memory = ::operator new(sizeof(ExecutionContext) + tail_allocation_size() * sizeof(Value));
|
||||||
|
return adopt_own(*::new (memory) ExecutionContext(registers_and_constants_and_locals_count, arguments_count));
|
||||||
}
|
}
|
||||||
void deallocate(void* ptr)
|
void deallocate(void* ptr, u32 tail_size)
|
||||||
{
|
{
|
||||||
m_execution_contexts.append(ptr);
|
if (tail_size <= 4) {
|
||||||
|
m_execution_contexts_with_4_tail.append(ptr);
|
||||||
|
} else if (tail_size <= 16) {
|
||||||
|
m_execution_contexts_with_16_tail.append(ptr);
|
||||||
|
} else if (tail_size <= 64) {
|
||||||
|
m_execution_contexts_with_64_tail.append(ptr);
|
||||||
|
} else if (tail_size <= 128) {
|
||||||
|
m_execution_contexts_with_128_tail.append(ptr);
|
||||||
|
} else if (tail_size <= 256) {
|
||||||
|
m_execution_contexts_with_256_tail.append(ptr);
|
||||||
|
} else if (tail_size <= 512) {
|
||||||
|
m_execution_contexts_with_512_tail.append(ptr);
|
||||||
|
} else {
|
||||||
|
::operator delete(ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector<void*> m_execution_contexts;
|
Vector<void*> m_execution_contexts_with_4_tail;
|
||||||
|
Vector<void*> m_execution_contexts_with_16_tail;
|
||||||
|
Vector<void*> m_execution_contexts_with_64_tail;
|
||||||
|
Vector<void*> m_execution_contexts_with_128_tail;
|
||||||
|
Vector<void*> m_execution_contexts_with_256_tail;
|
||||||
|
Vector<void*> m_execution_contexts_with_512_tail;
|
||||||
};
|
};
|
||||||
|
|
||||||
static NeverDestroyed<ExecutionContextAllocator> s_execution_context_allocator;
|
static NeverDestroyed<ExecutionContextAllocator> s_execution_context_allocator;
|
||||||
|
|
||||||
NonnullOwnPtr<ExecutionContext> ExecutionContext::create(u32 registers_and_constants_and_locals_count, u32 arguments_count)
|
NonnullOwnPtr<ExecutionContext> ExecutionContext::create(u32 registers_and_constants_and_locals_count, u32 arguments_count)
|
||||||
{
|
{
|
||||||
auto execution_context = s_execution_context_allocator->allocate();
|
return s_execution_context_allocator->allocate(registers_and_constants_and_locals_count, arguments_count);
|
||||||
execution_context->registers_and_constants_and_locals_and_arguments.resize_with_default_value(registers_and_constants_and_locals_count + arguments_count, js_special_empty_value());
|
|
||||||
execution_context->arguments_offset = registers_and_constants_and_locals_count;
|
|
||||||
return execution_context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExecutionContext::operator delete(void* ptr)
|
void ExecutionContext::operator delete(void* ptr)
|
||||||
{
|
{
|
||||||
s_execution_context_allocator->deallocate(ptr);
|
auto const* execution_context = static_cast<ExecutionContext const*>(ptr);
|
||||||
|
s_execution_context_allocator->deallocate(ptr, execution_context->registers_and_constants_and_locals_and_arguments_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutionContext::ExecutionContext()
|
ExecutionContext::ExecutionContext(u32 registers_and_constants_and_locals_count, u32 arguments_count)
|
||||||
{
|
{
|
||||||
|
registers_and_constants_and_locals_and_arguments_count = registers_and_constants_and_locals_count + arguments_count;
|
||||||
|
arguments_offset = registers_and_constants_and_locals_count;
|
||||||
|
auto* registers_and_constants_and_locals_and_arguments = this->registers_and_constants_and_locals_and_arguments();
|
||||||
|
for (size_t i = 0; i < registers_and_constants_and_locals_count; ++i)
|
||||||
|
registers_and_constants_and_locals_and_arguments[i] = js_special_empty_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutionContext::~ExecutionContext()
|
ExecutionContext::~ExecutionContext()
|
||||||
|
@ -56,7 +114,7 @@ ExecutionContext::~ExecutionContext()
|
||||||
|
|
||||||
NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
|
NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
|
||||||
{
|
{
|
||||||
auto copy = create(registers_and_constants_and_locals_and_arguments.size(), arguments().size());
|
auto copy = create(registers_and_constants_and_locals_and_arguments_count, arguments().size());
|
||||||
copy->function = function;
|
copy->function = function;
|
||||||
copy->realm = realm;
|
copy->realm = realm;
|
||||||
copy->script_or_module = script_or_module;
|
copy->script_or_module = script_or_module;
|
||||||
|
@ -70,10 +128,12 @@ NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
|
||||||
copy->executable = executable;
|
copy->executable = executable;
|
||||||
copy->arguments_offset = arguments_offset;
|
copy->arguments_offset = arguments_offset;
|
||||||
copy->passed_argument_count = passed_argument_count;
|
copy->passed_argument_count = passed_argument_count;
|
||||||
copy->registers_and_constants_and_locals_and_arguments = registers_and_constants_and_locals_and_arguments;
|
|
||||||
copy->unwind_contexts = unwind_contexts;
|
copy->unwind_contexts = unwind_contexts;
|
||||||
copy->saved_lexical_environments = saved_lexical_environments;
|
copy->saved_lexical_environments = saved_lexical_environments;
|
||||||
copy->previously_scheduled_jumps = previously_scheduled_jumps;
|
copy->previously_scheduled_jumps = previously_scheduled_jumps;
|
||||||
|
copy->registers_and_constants_and_locals_and_arguments_count = registers_and_constants_and_locals_and_arguments_count;
|
||||||
|
for (size_t i = 0; i < registers_and_constants_and_locals_and_arguments_count; ++i)
|
||||||
|
copy->registers_and_constants_and_locals_and_arguments()[i] = registers_and_constants_and_locals_and_arguments()[i];
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +149,7 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(*this_value);
|
visitor.visit(*this_value);
|
||||||
visitor.visit(executable);
|
visitor.visit(executable);
|
||||||
visitor.visit(function_name);
|
visitor.visit(function_name);
|
||||||
visitor.visit(registers_and_constants_and_locals_and_arguments);
|
visitor.visit(registers_and_constants_and_locals_and_arguments_span());
|
||||||
for (auto& context : unwind_contexts) {
|
for (auto& context : unwind_contexts) {
|
||||||
visitor.visit(context.lexical_environment);
|
visitor.visit(context.lexical_environment);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
|
* Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
|
||||||
|
* Copyright (c) 2024-2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -42,7 +43,7 @@ struct ExecutionContext {
|
||||||
private:
|
private:
|
||||||
friend class ExecutionContextAllocator;
|
friend class ExecutionContextAllocator;
|
||||||
|
|
||||||
ExecutionContext();
|
ExecutionContext(u32 registers_and_constants_and_locals_count, u32 arguments_count);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void operator delete(void* ptr);
|
void operator delete(void* ptr);
|
||||||
|
@ -70,17 +71,27 @@ public:
|
||||||
// FIXME: Move this out of LibJS (e.g. by using the CustomData concept), as it's used exclusively by LibWeb.
|
// FIXME: Move this out of LibJS (e.g. by using the CustomData concept), as it's used exclusively by LibWeb.
|
||||||
size_t skip_when_determining_incumbent_counter { 0 };
|
size_t skip_when_determining_incumbent_counter { 0 };
|
||||||
|
|
||||||
|
Span<Value> registers_and_constants_and_locals_and_arguments_span()
|
||||||
|
{
|
||||||
|
return { registers_and_constants_and_locals_and_arguments(), registers_and_constants_and_locals_and_arguments_count };
|
||||||
|
}
|
||||||
|
|
||||||
|
Value const* registers_and_constants_and_locals_and_arguments() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<Value*>(reinterpret_cast<uintptr_t>(this) + sizeof(ExecutionContext));
|
||||||
|
}
|
||||||
|
|
||||||
Value argument(size_t index) const
|
Value argument(size_t index) const
|
||||||
{
|
{
|
||||||
auto arguments_size = registers_and_constants_and_locals_and_arguments.size() - arguments_offset;
|
auto arguments_size = registers_and_constants_and_locals_and_arguments_count - arguments_offset;
|
||||||
if (index >= arguments_size) [[unlikely]]
|
if (index >= arguments_size) [[unlikely]]
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
return registers_and_constants_and_locals_and_arguments[arguments_offset + index];
|
return registers_and_constants_and_locals_and_arguments()[arguments_offset + index];
|
||||||
}
|
}
|
||||||
|
|
||||||
Value& local(size_t index)
|
Value& local(size_t index)
|
||||||
{
|
{
|
||||||
return registers_and_constants_and_locals_and_arguments[index];
|
return registers_and_constants_and_locals_and_arguments()[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 arguments_offset { 0 };
|
u32 arguments_offset { 0 };
|
||||||
|
@ -89,22 +100,27 @@ public:
|
||||||
|
|
||||||
Span<Value> arguments()
|
Span<Value> arguments()
|
||||||
{
|
{
|
||||||
return registers_and_constants_and_locals_and_arguments.span().slice(arguments_offset);
|
return { registers_and_constants_and_locals_and_arguments() + arguments_offset, registers_and_constants_and_locals_and_arguments_count - arguments_offset };
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadonlySpan<Value> arguments() const
|
ReadonlySpan<Value> arguments() const
|
||||||
{
|
{
|
||||||
return registers_and_constants_and_locals_and_arguments.span().slice(arguments_offset);
|
return { registers_and_constants_and_locals_and_arguments() + arguments_offset, registers_and_constants_and_locals_and_arguments_count - arguments_offset };
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
friend class Bytecode::Interpreter;
|
|
||||||
Vector<Value> registers_and_constants_and_locals_and_arguments;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Vector<Bytecode::UnwindInfo> unwind_contexts;
|
Vector<Bytecode::UnwindInfo> unwind_contexts;
|
||||||
Vector<Optional<size_t>> previously_scheduled_jumps;
|
Vector<Optional<size_t>> previously_scheduled_jumps;
|
||||||
Vector<GC::Ptr<Environment>> saved_lexical_environments;
|
Vector<GC::Ptr<Environment>> saved_lexical_environments;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Bytecode::Interpreter;
|
||||||
|
|
||||||
|
Value* registers_and_constants_and_locals_and_arguments()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<Value*>(reinterpret_cast<uintptr_t>(this) + sizeof(ExecutionContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 registers_and_constants_and_locals_and_arguments_count { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StackTraceElement {
|
struct StackTraceElement {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue