From 20bc2f1374f4316584534e05bcc09f5526cdd036 Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Mon, 13 Jul 2026 10:06:30 +0300 Subject: [PATCH] Add benchmark lanes for for-in over arrays, holey-array traversal and large-chunk string concat Coverage lanes ahead of a QuickJS-learnings performance campaign: * ForInGuardBenchmark: array receivers for for-in - dense (1,000 ints), holey (every 4th index) and an array carrying extra named own props. for-in over an array currently materializes the full key list (List + JsString per index + numeric sort) on every enumeration; ~1.6-2.5 MB per op on these lanes. * ArrayHoleTraversalBenchmark: index-loop sum, join, indexOf and the in operator over a 75%-holey dense-backed array vs its packed twin. Each hole read leaves the dense fast path and probes the prototype chain; the holey sum lane allocates ~7 MB per op vs 1.4 KB packed. * StringConcatLargeBenchmark: += with 4 KB chunks, 64 KB + 64 KB concatenation and build-then-charAt-scan, versus the small-chunk baseline the current ConcatenatedString already handles well. No engine changes. Co-Authored-By: Claude Fable 5 --- Jint.Benchmark/ArrayHoleTraversalBenchmark.cs | 141 ++++++++++++++++++ Jint.Benchmark/ForInGuardBenchmark.cs | 64 +++++++- Jint.Benchmark/StringConcatLargeBenchmark.cs | 106 +++++++++++++ 3 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 Jint.Benchmark/ArrayHoleTraversalBenchmark.cs create mode 100644 Jint.Benchmark/StringConcatLargeBenchmark.cs diff --git a/Jint.Benchmark/ArrayHoleTraversalBenchmark.cs b/Jint.Benchmark/ArrayHoleTraversalBenchmark.cs new file mode 100644 index 0000000000..b7d390379e --- /dev/null +++ b/Jint.Benchmark/ArrayHoleTraversalBenchmark.cs @@ -0,0 +1,141 @@ +using BenchmarkDotNet.Attributes; +using Jint.Native; + +namespace Jint.Benchmark; + +/// +/// Traversal cost of holey (but still dense-backed) arrays versus packed ones. Reading a hole +/// falls off the dense fast path and probes the prototype chain per element (the spec requires a +/// HasProperty/Get walk), so index loops, join and indexOf pay a per-hole penalty that a packed +/// array never sees. The dense/holey lane pairs measure that gap; `in` exercises the raw +/// HasProperty probe. +/// +[MemoryDiagnoser] +[HideColumns("Error", "Gen0", "Gen1", "Gen2")] +public class ArrayHoleTraversalBenchmark +{ + private Engine _engine = null!; + private Prepared