Skip to content

Commit 447ebbe

Browse files
committed
perf(array): add dense fast path to Array.prototype.push
Array.prototype.push was going through the generic [[Set]] machinery for every element, which involves property descriptor validation and prototype chain walks. This is unnecessary for dense arrays where we can append directly to the indexed storage. Add a fast path that mirrors the PushValueToArray opcode: check that the array is extensible and has dense storage, then push directly via push_dense() and update the length in-place. Falls through to the existing slow path for sparse arrays, non-extensible arrays, and array-like objects. ~49% improvement on a push-dominated microbenchmark, ~20% on a realistic mixed workload (push objects + filter).
1 parent f5e88de commit 447ebbe

1 file changed

Lines changed: 29 additions & 0 deletions

File tree

  • core/engine/src/builtins/array

core/engine/src/builtins/array/mod.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,35 @@ impl Array {
875875
)
876876
.into());
877877
}
878+
879+
// Fast path: if O is an extensible dense array, push directly to indexed
880+
// storage without going through the generic [[Set]] machinery. push_dense()
881+
// handles type transitions (DenseI32 -> DenseF64 -> DenseElement) and
882+
// only returns false for already-sparse storage, so if the first push
883+
// succeeds all subsequent pushes on the same storage will too.
884+
if o.is_array() && !args.is_empty() {
885+
let mut o_borrowed = o.borrow_mut();
886+
if o_borrowed.extensible
887+
&& let Some(len_i32) = o_borrowed.properties().storage[0].as_i32()
888+
&& o_borrowed
889+
.properties_mut()
890+
.indexed_properties
891+
.push_dense(&args[0])
892+
{
893+
let mut current_len = len_i32 + 1;
894+
for element in &args[1..] {
895+
o_borrowed
896+
.properties_mut()
897+
.indexed_properties
898+
.push_dense(element);
899+
current_len += 1;
900+
}
901+
o_borrowed.properties_mut().storage[0] = JsValue::new(current_len);
902+
len += args.len() as u64;
903+
return Ok(len.into());
904+
}
905+
}
906+
878907
// 5. For each element E of items, do
879908
for element in args.iter().cloned() {
880909
// a. Perform ? Set(O, ! ToString(𝔽(len)), E, true).

0 commit comments

Comments
 (0)