Add microbenchmarks for latest JSC changes (#31562)
This PR adds microbenchmarks for latest JSC changes
---
Comparison of Bun 1.3.14 (WebKit `5488984d`) and Bun main (WebKit
`0d85951a`), on Apple Silicon macOS.
Numbers are mitata average time per iteration of the snippet, using the
benchmarks in `bench/snippets/` (`BENCHMARK_RUNNER=1 <bun>
bench/snippets/<file>.mjs`).
> [!NOTE]
> The code snippets shown in each section are simplified excerpts for
illustration only. The actual measurements were taken with the benchmark
files added in this PR.
## `Array#indexOf` / `Array#includes` (string arrays)
```js
const arr = Array.from({ length: 64 }, (_, i) => String.fromCharCode(65 + (i % 26)) + "x".repeat(63));
const key = "@" + "x".repeat(63);
let result = 0;
for (let i = 0; i < 1e4; i++) result += arr.indexOf(key);
```
| version | time |
| ------- | -------- |
| 1.3.14 | 13.12 ms |
| main | 2.00 ms |
6.6 times faster (`includes`: 11.96 ms → 2.03 ms, 5.9 times faster)
upstream commits: https://commits.webkit.org/313389@main,
https://commits.webkit.org/312963@main by sosukesuzuki
## `Object.defineProperty`
```js
const o = {};
Object.defineProperty(o, "hey", { value: 42, writable: true, enumerable: true, configurable: true });
```
| version | time |
| ------- | ------- |
| 1.3.14 | 38.9 ns |
| main | 4.5 ns |
8.6 times faster (accessor descriptor: 1.6x, getter only: 1.4x, 4
properties in a row: 3.1x)
upstream commit: https://commits.webkit.org/313124@main by Constellation
## `Promise.all` with non-thenable values
```js
const values = Array.from({ length: 1000 }, (_, i) => i);
await Promise.all(values);
```
| version | time |
| ------- | -------- |
| 1.3.14 | 24.46 µs |
| main | 15.29 µs |
1.6 times faster (`Promise.race([p1, p2])`: 1.35x)
upstream commits: https://commits.webkit.org/313576@main,
https://commits.webkit.org/313361@main by sosukesuzuki
## `Promise#then` chain
```js
let p = Promise.resolve(0);
for (let i = 0; i < 10; i++) p = p.then(v => v + 1);
await p;
```
| version | time |
| ------- | -------- |
| 1.3.14 | 296.1 ns |
| main | 228.1 ns |
1.3 times faster
upstream commits: https://commits.webkit.org/313220@main,
https://commits.webkit.org/313277@main by Constellation,
https://commits.webkit.org/313294@main by sosukesuzuki
## Memory per pending `Promise` with one `.then` handler
```js
const p = new Promise(() => {});
p.then(v => v);
// keep p alive, measure heap size after GC
```
| version | heap per promise |
| ------- | ---------------- |
| 1.3.14 | 104 bytes |
| main | 72 bytes |
31% less memory (the promise reaction cell is no longer allocated for
the first `.then`)
upstream commit: https://commits.webkit.org/313220@main by Constellation
## `String#startsWith` / `String#endsWith` with a constant search string
```js
// urls: 64 different strings
let count = 0;
for (let i = 0; i < urls.length; i++) count += urls[i].startsWith("https://");
```
| version | time |
| ------- | -------- |
| 1.3.14 | 314.5 ns |
| main | 68.4 ns |
4.6 times faster (`endsWith`: 316.4 ns → 70.2 ns, 4.5 times faster)
upstream commit: https://commits.webkit.org/313394@main by sosukesuzuki
## `String#split`
```js
// sentences: 64 different ~90-char strings
sentences[i].split(" ");
```
| version | time |
| ------- | -------- |
| 1.3.14 | 63.98 µs |
| main | 20.85 µs |
3.1 times faster (`split(",")` on short CSV lines: 1.9 times faster)
upstream commit: https://commits.webkit.org/312843@main by sosukesuzuki
## `String#match`
```js
// sentences: 64 different ~90-char strings
sentences[i].match(/[a-z]+/g);
```
| version | time |
| ------- | -------- |
| 1.3.14 | 50.54 µs |
| main | 15.84 µs |
3.2 times faster (non-global regexp with a capture group: 2.7 times
faster)
upstream commit: https://commits.webkit.org/313363@main by sosukesuzuki
## `String#substring`
```js
// urls: 64 different strings
urls[i].substring(8, 24);
```
| version | time |
| ------- | -------- |
| 1.3.14 | 789.1 ns |
| main | 354.2 ns |
2.2 times faster
upstream commit: https://commits.webkit.org/313402@main by Constellation
## `TypedArray#sort`
```js
const floats = Float64Array.from({ length: 1024 }, () => Math.random() * 1000);
floats.slice().sort();
```
| version | time |
| ------- | -------- |
| 1.3.14 | 16.84 µs |
| main | 7.25 µs |
2.3 times faster (Int32Array: 1.9x, with a comparator: 2.0x)
upstream commit: https://commits.webkit.org/312803@main by syg
## `Array#sort` (small arrays)
```js
// 64 unsorted numbers
numbers.sort((a, b) => a - b);
```
| version | time |
| ------- | -------- |
| 1.3.14 | 1.02 µs |
| main | 774.4 ns |
1.3 times faster
upstream commit: https://commits.webkit.org/312983@main by Constellation
## `Array#concat`
```js
const a = Array.from({ length: 128 }, (_, i) => i);
const b = Array.from({ length: 128 }, (_, i) => i + 128);
a.concat(b);
```
| version | time |
| ------- | ------- |
| 1.3.14 | 67.6 ns |
| main | 55.5 ns |
1.2 times faster (small int arrays: 1.3x, string arrays: 1.2x)
upstream commit: https://commits.webkit.org/312543@main by Constellation
## String equality (16-bit / non-Latin1 strings)
```js
// two distinct 256-char strings with identical 16-bit content
function eq(x, y) {
return x === y;
}
let n = 0;
for (let i = 0; i < 1e6; ++i) {
if (eq(a, b)) n++;
if (eq(a, c)) n++;
}
```
| version | time |
| ------- | -------- |
| 1.3.14 | 11.46 ms |
| main | 8.78 ms |
1.3 times faster (65-char strings: 5.62 ms → 4.20 ms, 1.3 times faster)
upstream commit: https://commits.webkit.org/312931@main by sosukesuzuki
## `Date.now()`
```js
let sum = 0;
for (let i = 0; i < 1e6; ++i) sum += Date.now();
```
| version | time |
| ------- | -------- |
| 1.3.14 | 22.27 ms |
| main | 19.18 ms |
1.2 times faster (elapsed-time pattern `Date.now() - start`: 25.02 ms →
19.80 ms, 1.3 times faster)
upstream commit: https://commits.webkit.org/313578@main by sosukesuzuki
## `Array#unshift`
```js
const arr = [];
for (let i = 0; i < 16; i++) arr.unshift(i);
```
| version | time |
| ------- | -------- |
| 1.3.14 | 268.4 ns |
| main | 215.2 ns |
1.2 times faster
upstream commits: https://commits.webkit.org/313475@main,
https://commits.webkit.org/312915@main by Constellation
## Async module graphs (top-level await)
```sh
# 50,000 synchronous ES modules importing one module with top-level await
cd bench/module-loader
CASCADE_CHAINS=500 CASCADE_DEPTH=100 bun create-tla-graph.js
bun ./tla-cascade-entry.mjs
```
| version | time |
| ------- | ------- |
| 1.3.14 | ~1.9 s |
| main | ~1.25 s |
~1.5 times faster (GatherAvailableAncestors is now O(N) instead of
O(N²))
upstream commit: https://commits.webkit.org/312498@main by sosukesuzuki S
SUZUKI Sosuke committed
2629789a22bd9fcd5137dc42d8a1865870aa992f
Parent: 61bd997
Committed by GitHub <noreply@github.com>
on 5/31/2026, 6:07:34 PM