mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-08 05:27:14 +09:00
LibJS: Skip iterator result object allocation in for..of and for..in
Introduce special instruction for `for..of` and `for..in` loop that skips `{ value, done }` result object allocation if iterator is builtin (array, map, set, string). This reduces GC pressure significantly and avoids extracting the `value` and `done` properties. This change makes this micro benchmark 48% faster on my computer: ```js const arr = new Array(10_000_000); let counter = 0; for (let _ of arr) { counter++; } ```
This commit is contained in:
parent
ab52d86a69
commit
81b6a1100e
Notes:
github-actions[bot]
2025-04-30 18:52:38 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: 81b6a1100e
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4533
4 changed files with 94 additions and 23 deletions
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2025, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -611,6 +612,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
|
|||
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(Dump);
|
||||
HANDLE_INSTRUCTION(EnterObjectEnvironment);
|
||||
HANDLE_INSTRUCTION(Exp);
|
||||
HANDLE_INSTRUCTION(ForOfNext);
|
||||
HANDLE_INSTRUCTION(GetById);
|
||||
HANDLE_INSTRUCTION(GetByIdWithThis);
|
||||
HANDLE_INSTRUCTION(GetByValue);
|
||||
|
@ -2980,6 +2982,27 @@ ThrowCompletionOr<void> IteratorNext::execute_impl(Bytecode::Interpreter& interp
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ForOfNext::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto& iterator_record = static_cast<IteratorRecord&>(interpreter.get(m_iterator_record).as_cell());
|
||||
|
||||
Value value;
|
||||
bool done = false;
|
||||
if (auto* builtin_iterator = iterator_record.iterator->as_builtin_iterator()) {
|
||||
TRY(builtin_iterator->next(vm, done, value));
|
||||
} else {
|
||||
auto result = TRY(iterator_next(vm, iterator_record));
|
||||
value = TRY(result->internal_get(vm.names.value, {}));
|
||||
done = TRY(result->internal_get(vm.names.done, {})).to_boolean();
|
||||
}
|
||||
|
||||
interpreter.set(dst_done(), Value(done));
|
||||
interpreter.set(dst_value(), value);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> NewClass::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
Value super_class;
|
||||
|
@ -3754,6 +3777,14 @@ ByteString IteratorNext::to_byte_string_impl(Executable const& executable) const
|
|||
format_operand("iterator_record"sv, m_iterator_record, executable));
|
||||
}
|
||||
|
||||
ByteString ForOfNext::to_byte_string_impl(Executable const& executable) const
|
||||
{
|
||||
return ByteString::formatted("ForOfNext {}, {}, {}",
|
||||
format_operand("dst_value"sv, m_dst_value, executable),
|
||||
format_operand("dst_done"sv, m_dst_done, executable),
|
||||
format_operand("iterator_record"sv, m_iterator_record, executable));
|
||||
}
|
||||
|
||||
ByteString ResolveThisBinding::to_byte_string_impl(Bytecode::Executable const&) const
|
||||
{
|
||||
return "ResolveThisBinding"sv;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue