diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml index cedad2d937..d5d9aa3645 100644 --- a/.github/workflows/release-packages.yml +++ b/.github/workflows/release-packages.yml @@ -704,6 +704,10 @@ jobs: cp "target-abort/${{ matrix.target }}/dist/libperry_runtime.a" \ "target/${{ matrix.target }}/dist/libperry_runtime_abort.a" + - name: Build core runtime profile (Unix) + if: runner.os != 'Windows' && matrix.old_glibc_image == '' && matrix.musl_image == '' + run: bash scripts/build_core_runtime.sh '${{ matrix.target }}' + - name: Build native ext libraries (Unix) # #2532 — the perry-ext-* wrapper crates ship the host functions for # node:http (server), ws, net, zlib, fastify, the db drivers, etc. @@ -772,7 +776,7 @@ jobs: src="target-glibc231/$TARGET/dist" dst="target/$TARGET/dist" mkdir -p "$dst" - for artifact in perry libperry_runtime.a libperry_runtime_abort.a libperry_stdlib.a; do + for artifact in perry libperry_runtime.a libperry_runtime_abort.a libperry_runtime_core.a libperry_stdlib.a; do test -s "$src/$artifact" cp "$src/$artifact" "$dst/$artifact" done @@ -818,7 +822,7 @@ jobs: src="target-musl/$TARGET/dist" dst="target/$TARGET/dist" mkdir -p "$dst" - for artifact in perry libperry_runtime.a libperry_runtime_abort.a libperry_stdlib.a; do + for artifact in perry libperry_runtime.a libperry_runtime_abort.a libperry_runtime_core.a libperry_stdlib.a; do test -s "$src/$artifact" cp "$src/$artifact" "$dst/$artifact" done @@ -955,7 +959,7 @@ jobs: mkdir -p staging cp target/${{ matrix.target }}/dist/perry staging/ # Include static libraries for linking (runtime, stdlib, UI) - for lib in libperry_runtime.a libperry_runtime_abort.a libperry_stdlib.a libperry_ui_macos.a libperry_ui_gtk4.a; do + for lib in libperry_runtime.a libperry_runtime_abort.a libperry_runtime_core.a libperry_stdlib.a libperry_ui_macos.a libperry_ui_gtk4.a; do if [ -f "target/${{ matrix.target }}/dist/$lib" ]; then cp "target/${{ matrix.target }}/dist/$lib" staging/ fi diff --git a/CLAUDE.md b/CLAUDE.md index e0f56b89e8..fd07c171ed 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Perry is a native TypeScript compiler written in Rust that compiles TypeScript source code directly to native executables. It uses SWC for TypeScript parsing and LLVM for code generation. -**Current Version:** 0.5.1564 +**Current Version:** 0.5.1565 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index 1a50360303..36b9765959 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5690,7 +5690,7 @@ checksum = "1542e48011813fbdf3c075da4a4ed53ee93c816eef62e36eb5064a6fd2be10a5" [[package]] name = "perry" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "base64 0.22.1", @@ -5754,7 +5754,7 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-dispatch", "serde", @@ -5762,7 +5762,7 @@ dependencies = [ [[package]] name = "perry-audio-miniaudio" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "cc", "libc", @@ -5771,7 +5771,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "aho-corasick", "anyhow", @@ -5789,7 +5789,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "perry-hir", @@ -5797,7 +5797,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "perry-hir", @@ -5805,7 +5805,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "perry-dispatch", @@ -5814,7 +5814,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "perry-hir", @@ -5822,7 +5822,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "base64 0.22.1", @@ -5834,7 +5834,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "perry-hir", @@ -5842,7 +5842,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "async-trait", @@ -5870,14 +5870,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "serde", "serde_json", @@ -5885,7 +5885,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1564" +version = "0.5.1565" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5896,7 +5896,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "clap", @@ -5911,7 +5911,7 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "block2", "objc2", @@ -5921,7 +5921,7 @@ dependencies = [ [[package]] name = "perry-ext-argon2" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "argon2", "perry-ffi", @@ -5930,7 +5930,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "reqwest", @@ -5939,7 +5939,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "bcrypt", "perry-ffi", @@ -5947,7 +5947,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "rusqlite", @@ -5955,7 +5955,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "scraper", @@ -5963,7 +5963,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "perry-runtime", @@ -5971,7 +5971,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "chrono", "cron", @@ -5981,7 +5981,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "chrono", "perry-ffi", @@ -5989,7 +5989,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "rust_decimal", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "serde_json", @@ -6005,7 +6005,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "rand 0.10.2", @@ -6013,7 +6013,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "perry-runtime", @@ -6021,14 +6021,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "bytes", "http-body-util", @@ -6046,7 +6046,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "bytes", "lazy_static", @@ -6059,7 +6059,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "bytes", @@ -6091,7 +6091,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "lazy_static", "perry-ffi", @@ -6101,7 +6101,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "jsonwebtoken", @@ -6112,7 +6112,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "lru", "perry-ffi", @@ -6121,7 +6121,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "chrono", "perry-ffi", @@ -6129,7 +6129,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "bson", "futures-util", @@ -6141,7 +6141,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "chrono", "perry-ffi", @@ -6153,7 +6153,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "nanoid", "perry-ffi", @@ -6162,7 +6162,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "bytes", "perry-ffi", @@ -6177,7 +6177,7 @@ dependencies = [ [[package]] name = "perry-ext-node-forge" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "const-oid 0.10.2", "der 0.8.1", @@ -6196,7 +6196,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "lettre", "perry-ffi", @@ -6206,7 +6206,7 @@ dependencies = [ [[package]] name = "perry-ext-parcel-watcher" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "notify", "perry-ffi", @@ -6218,7 +6218,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "printpdf", @@ -6226,7 +6226,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "sqlx", @@ -6235,7 +6235,7 @@ dependencies = [ [[package]] name = "perry-ext-qs" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "perry-runtime", @@ -6244,7 +6244,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "governor", "perry-ffi", @@ -6252,7 +6252,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "fast_image_resize", "image", @@ -6263,7 +6263,7 @@ dependencies = [ [[package]] name = "perry-ext-streams" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "lazy_static", "perry-ffi", @@ -6272,7 +6272,7 @@ dependencies = [ [[package]] name = "perry-ext-typescript" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "perry-ffi", @@ -6292,7 +6292,7 @@ dependencies = [ [[package]] name = "perry-ext-undici" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "perry-runtime", @@ -6301,7 +6301,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "uuid", @@ -6309,7 +6309,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-ffi", "perry-validation", @@ -6318,7 +6318,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "futures-util", "lazy_static", @@ -6331,7 +6331,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "brotli", "flate2", @@ -6341,7 +6341,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "dashmap 6.2.1", "once_cell", @@ -6351,7 +6351,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "perry-api-manifest", @@ -6372,11 +6372,11 @@ dependencies = [ [[package]] name = "perry-native-registration" -version = "0.5.1564" +version = "0.5.1565" [[package]] name = "perry-parser" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "perry-diagnostics", @@ -6390,7 +6390,7 @@ dependencies = [ [[package]] name = "perry-perex" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perex", "regex", @@ -6398,7 +6398,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "ahash", "anyhow", @@ -6458,14 +6458,14 @@ dependencies = [ [[package]] name = "perry-runtime-static" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-runtime", ] [[package]] name = "perry-stdlib" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "aes 0.8.4", "aes 0.9.1", @@ -6560,14 +6560,14 @@ dependencies = [ [[package]] name = "perry-stdlib-static" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "perry-stdlib", ] [[package]] name = "perry-transform" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "perry-hir", @@ -6576,15 +6576,17 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ + "dirs", "perry-ffi", "perry-ui-model", + "tempfile", ] [[package]] name = "perry-ui-android" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "itoa", @@ -6602,7 +6604,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "rand 0.10.2", "serde", @@ -6612,7 +6614,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "cairo-rs 0.22.9", @@ -6635,7 +6637,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "block2", @@ -6652,7 +6654,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "block2", @@ -6669,7 +6671,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1564" +version = "0.5.1565" [[package]] name = "perry-ui-test" @@ -6680,11 +6682,11 @@ dependencies = [ [[package]] name = "perry-ui-testkit" -version = "0.5.1564" +version = "0.5.1565" [[package]] name = "perry-ui-tvos" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "block2", @@ -6701,7 +6703,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "block2", @@ -6718,7 +6720,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "block2", "libc", @@ -6732,7 +6734,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "libc", @@ -6751,7 +6753,7 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "base64 0.22.1", "libc", @@ -6764,7 +6766,7 @@ dependencies = [ [[package]] name = "perry-updater" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "anyhow", "base64 0.22.1", @@ -6780,7 +6782,7 @@ dependencies = [ [[package]] name = "perry-validation" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "idna", "regex", @@ -6790,7 +6792,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1564" +version = "0.5.1565" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index e62facd0be..d15ea1e7f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -338,7 +338,7 @@ codegen-units = 1 codegen-units = 1 [workspace.package] -version = "0.5.1564" +version = "0.5.1565" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/benchmarks/regexp-construction/README.md b/benchmarks/regexp-construction/README.md new file mode 100644 index 0000000000..bf9b26c9da --- /dev/null +++ b/benchmarks/regexp-construction/README.md @@ -0,0 +1,70 @@ +# Repeated RegExp construction (#10179) + +Run all commands on a build host, with the compiler and runtime/stdlib archives +from the same build. In the issue's Mac lane, wrap every command below in +`GIT_DIR=/tmp/perry-regexp-lane.git ./remote.sh ''`. + +`prepare.py` resolves the actual OpenCode v1.18.30 packages under +`$OPENCODE_SRC/node_modules/.bun`: emoji-regex 10.6.0, string-width 7.2.0, +strip-ansi 7.1.2, and get-east-asian-width 1.6.0. Nothing substitutes a shortened +pattern. The current compiler reports 10 modules for this probe. + +```sh +export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$PWD/target}" +export RUSTFLAGS="-C force-unwind-tables=yes -C force-frame-pointers=yes" +cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static +export PERRY_RUNTIME_DIR="$CARGO_TARGET_DIR/release" +python3 benchmarks/regexp-construction/prepare.py /tmp/regexp-probe.ts +"$CARGO_TARGET_DIR/release/perry" compile /tmp/regexp-probe.ts \ + -o /tmp/regexp-probe --no-auto-optimize --debug-symbols +/tmp/regexp-probe all 1 +/root/claude-opencode/bun/bin/bun /tmp/regexp-probe.ts all 1 +PERRY_REGEX_DIAG=1 /tmp/regexp-probe all 1 +perf record -q -F 499 -g --call-graph dwarf -o /tmp/regexp.perf \ + /tmp/regexp-probe construct 100 +perf report --stdio --no-children -g none -i /tmp/regexp.perf \ + --percent-limit 1 -F overhead,symbol +``` + +The first positional argument selects an operation (`construct`, `test-ascii`, +`test-emoji`, `stripAnsi`, `stripAnsi-match`, `ignorable`, `segment`, +`eastAsianWidth`, `stringWidth`, or `all`). The second multiplies iterations. +The emoji test intentionally preserves global `lastIndex` between calls, as +in the issue probe: successful and unsuccessful searches alternate. Compare +identical iteration counts and include the printed checksum. Timing is wall +microseconds per iteration and includes the first operation; larger scales +amortize initial compilation/validation and Bun's warmup. + +`measure.py` checks checksums and retains samples, medians, and execution +orders. Native builds run from one fixed pathname/inode; copying happens +outside the timing window. Rotation plus reversal covers all six orders of +before/after/Bun. Use a multiple of six rounds for balanced positions, and +repeat `--case MODE:SCALE` to select individual cases. + +```sh +taskset -c 15 python3 benchmarks/regexp-construction/measure.py \ + --before /tmp/regexp-probe-before --after /tmp/regexp-probe \ + --source /tmp/regexp-probe.ts --bun /root/claude-opencode/bun/bin/bun \ + --output /tmp/regexp-probe-results.json --runs 12 +``` + +For existing benchmark regressions, `compare.py` compiles all 12 existing +`benchmarks/app-patterns/kernels/*.ts` with each compiler/archive bundle, +checks outputs against the repository's pinned Node oracle, and alternates +before/after execution order. Each build is copied to the same executable path +outside the timed window. It reports child user CPU as well as wall time; +use identical `--before`/`--after` bundles for an A/A noise control. Both +harnesses also accept `--paired-controls`: two identical-copy labels per build +run in each balanced four-run block (the probe omits Bun in this mode). + +```sh +export PATH=/tmp/regexp-oracle/node-v26.5.1-linux-x64/bin:$PATH +python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-baseline-libs --after "$CARGO_TARGET_DIR/release" \ + --output /tmp/regexp-app-comparison --runs 12 +``` + +See [current-results.md](current-results.md) for the latest integration comparison, +[final-results.md](final-results.md) for the prior Perex 0.1.4 measurements, +[results.md](results.md) for the original attribution, and +[merged-results.md](merged-results.md) for the earlier integration measurements. diff --git a/benchmarks/regexp-construction/app-samples.json b/benchmarks/regexp-construction/app-samples.json new file mode 100644 index 0000000000..534f766204 --- /dev/null +++ b/benchmarks/regexp-construction/app-samples.json @@ -0,0 +1,14 @@ +[ + {"name":"batch","node_exit":0,"before":[{"cpu_ms":53.50199999999994,"wall_ms":88.28007197007537},{"cpu_ms":47.62299999999997,"wall_ms":61.03243213146925},{"cpu_ms":49.36499999999988,"wall_ms":59.80988801456988},{"cpu_ms":49.33599999999983,"wall_ms":69.91573004052043},{"cpu_ms":49.75899999999989,"wall_ms":61.158268013969064},{"cpu_ms":47.49399999999993,"wall_ms":54.10855705849826},{"cpu_ms":52.15100000000028,"wall_ms":122.86090198904276},{"cpu_ms":49.03099999999982,"wall_ms":105.35239987075329},{"cpu_ms":54.21100000000001,"wall_ms":117.9290481377393},{"cpu_ms":55.11500000000025,"wall_ms":120.3060089610517},{"cpu_ms":50.61099999999996,"wall_ms":74.89613490179181},{"cpu_ms":49.75200000000024,"wall_ms":73.13893199898303},{"cpu_ms":48.73799999999973,"wall_ms":73.10119201429188},{"cpu_ms":51.152000000000086,"wall_ms":65.68812113255262},{"cpu_ms":54.952999999999804,"wall_ms":82.9087570309639}],"before_median_cpu_ms":49.75899999999989,"after":[{"cpu_ms":49.08000000000001,"wall_ms":64.6241339854896},{"cpu_ms":49.63700000000016,"wall_ms":71.94150518625975},{"cpu_ms":49.68400000000006,"wall_ms":58.337328024208546},{"cpu_ms":49.36300000000004,"wall_ms":69.83772502280772},{"cpu_ms":51.2450000000002,"wall_ms":71.63529307581484},{"cpu_ms":48.353000000000094,"wall_ms":53.87409799732268},{"cpu_ms":49.91799999999991,"wall_ms":91.30243910476565},{"cpu_ms":53.46200000000012,"wall_ms":114.07939507625997},{"cpu_ms":51.82299999999973,"wall_ms":113.96184493787587},{"cpu_ms":53.32299999999979,"wall_ms":118.93719201907516},{"cpu_ms":53.85499999999999,"wall_ms":86.71758184209466},{"cpu_ms":50.027000000000044,"wall_ms":72.39967095665634},{"cpu_ms":51.18299999999998,"wall_ms":76.27159496769309},{"cpu_ms":52.497999999999934,"wall_ms":76.04552316479385},{"cpu_ms":52.850000000000286,"wall_ms":74.87114891409874}],"after_median_cpu_ms":51.18299999999998}, + {"name":"buffer_transcode","node_exit":0,"before":[{"cpu_ms":75.20499999999953,"wall_ms":247.69417010247707},{"cpu_ms":74.9260000000005,"wall_ms":162.18660795129836},{"cpu_ms":75.38700000000009,"wall_ms":342.0196168590337},{"cpu_ms":77.04100000000037,"wall_ms":163.29416795633733},{"cpu_ms":79.99299999999998,"wall_ms":200.47719008289278},{"cpu_ms":76.91900000000018,"wall_ms":276.49802691303194},{"cpu_ms":68.53700000000006,"wall_ms":137.96956092119217},{"cpu_ms":79.23300000000033,"wall_ms":311.30894809029996},{"cpu_ms":86.4729999999998,"wall_ms":215.4078260064125},{"cpu_ms":77.07100000000011,"wall_ms":294.6943249553442},{"cpu_ms":81.766,"wall_ms":311.20751798152924},{"cpu_ms":74.44399999999973,"wall_ms":262.43828888982534},{"cpu_ms":72.18999999999997,"wall_ms":260.5005369987339},{"cpu_ms":78.61799999999963,"wall_ms":333.93404982052743},{"cpu_ms":74.98200000000033,"wall_ms":434.18309604749084}],"before_median_cpu_ms":76.91900000000018,"after":[{"cpu_ms":68.85600000000025,"wall_ms":168.95423317328095},{"cpu_ms":69.0369999999998,"wall_ms":237.46347497217357},{"cpu_ms":72.17999999999947,"wall_ms":159.09391012974083},{"cpu_ms":75.30500000000018,"wall_ms":458.8369990233332},{"cpu_ms":76.59099999999964,"wall_ms":384.97643708251417},{"cpu_ms":76.17799999999963,"wall_ms":171.12970794551075},{"cpu_ms":75.66200000000035,"wall_ms":382.9843858256936},{"cpu_ms":71.65799999999933,"wall_ms":659.9673291202635},{"cpu_ms":77.24400000000031,"wall_ms":363.42049203813076},{"cpu_ms":76.12299999999993,"wall_ms":185.8349828980863},{"cpu_ms":73.45700000000033,"wall_ms":248.71361488476396},{"cpu_ms":70.03100000000018,"wall_ms":278.3614790532738},{"cpu_ms":74.91100000000017,"wall_ms":178.90263092704117},{"cpu_ms":82.20200000000055,"wall_ms":291.53323895297945},{"cpu_ms":79.69499999999918,"wall_ms":288.325565867126}],"after_median_cpu_ms":75.30500000000018}, + {"name":"date_format_parse","node_exit":0,"before":[{"cpu_ms":39.58800000000018,"wall_ms":59.96600002981722},{"cpu_ms":42.88799999999959,"wall_ms":58.32497403025627},{"cpu_ms":47.67400000000066,"wall_ms":159.00021488778293},{"cpu_ms":45.01699999999964,"wall_ms":74.87217104062438},{"cpu_ms":43.5579999999991,"wall_ms":52.00656899251044},{"cpu_ms":44.12899999999986,"wall_ms":55.48637080937624},{"cpu_ms":42.8090000000001,"wall_ms":64.25793003290892},{"cpu_ms":40.9209999999991,"wall_ms":65.59373484924436},{"cpu_ms":48.88599999999954,"wall_ms":193.21048306301236},{"cpu_ms":47.4610000000002,"wall_ms":81.92804385907948},{"cpu_ms":47.25100000000104,"wall_ms":77.52007804811001},{"cpu_ms":48.72499999999924,"wall_ms":76.58684416674078},{"cpu_ms":46.637999999999735,"wall_ms":78.89163005165756},{"cpu_ms":46.19899999999966,"wall_ms":76.31898391991854},{"cpu_ms":49.1439999999983,"wall_ms":70.65349817276001}],"before_median_cpu_ms":46.19899999999966,"after":[{"cpu_ms":42.462000000000444,"wall_ms":66.57733302563429},{"cpu_ms":48.021999999999565,"wall_ms":121.17168703116477},{"cpu_ms":47.883000000000564,"wall_ms":113.28023206442595},{"cpu_ms":46.36299999999949,"wall_ms":65.79931895248592},{"cpu_ms":45.78400000000116,"wall_ms":66.14992395043373},{"cpu_ms":45.01699999999964,"wall_ms":53.39573393575847},{"cpu_ms":43.32300000000089,"wall_ms":63.7915781699121},{"cpu_ms":43.29000000000072,"wall_ms":61.56557914800942},{"cpu_ms":41.74800000000012,"wall_ms":59.9835030734539},{"cpu_ms":47.30200000000018,"wall_ms":75.93077886849642},{"cpu_ms":45.16999999999882,"wall_ms":71.88473897986114},{"cpu_ms":50.89000000000077,"wall_ms":79.35869321227074},{"cpu_ms":47.098000000000084,"wall_ms":77.6592381298542},{"cpu_ms":50.74300000000065,"wall_ms":74.96260921470821},{"cpu_ms":48.8540000000004,"wall_ms":67.47289607301354}],"after_median_cpu_ms":46.36299999999949}, + {"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":106.05400000000031,"wall_ms":332.28826499544084},{"cpu_ms":98.35799999999928,"wall_ms":370.5330619122833},{"cpu_ms":100.40899999999908,"wall_ms":151.14167192950845},{"cpu_ms":93.42400000000062,"wall_ms":141.91185706295073},{"cpu_ms":97.97799999999945,"wall_ms":114.43778383545578},{"cpu_ms":99.88600000000147,"wall_ms":143.32567900419235},{"cpu_ms":103.58800000000024,"wall_ms":144.90972901694477},{"cpu_ms":101.27800000000065,"wall_ms":123.76677501015365},{"cpu_ms":104.1470000000011,"wall_ms":144.5750528946519},{"cpu_ms":100.60000000000002,"wall_ms":144.495113985613},{"cpu_ms":104.55400000000026,"wall_ms":145.49416094087064},{"cpu_ms":102.12899999999969,"wall_ms":143.07505008764565},{"cpu_ms":97.77999999999842,"wall_ms":115.26004201732576},{"cpu_ms":99.6800000000011,"wall_ms":193.3565668296069},{"cpu_ms":103.06699999999935,"wall_ms":144.39926482737064}],"before_median_cpu_ms":100.60000000000002,"after":[{"cpu_ms":101.13199999999978,"wall_ms":382.45927495881915},{"cpu_ms":99.34100000000079,"wall_ms":152.5857700034976},{"cpu_ms":99.3119999999994,"wall_ms":152.2292650770396},{"cpu_ms":93.7859999999997,"wall_ms":136.27275195904076},{"cpu_ms":96.08300000000014,"wall_ms":137.40162085741758},{"cpu_ms":96.6989999999992,"wall_ms":136.1968598794192},{"cpu_ms":100.1379999999994,"wall_ms":138.60473805107176},{"cpu_ms":92.5659999999997,"wall_ms":123.91980108805001},{"cpu_ms":96.2259999999997,"wall_ms":140.47773787751794},{"cpu_ms":99.44299999999906,"wall_ms":138.82935792207718},{"cpu_ms":97.7440000000005,"wall_ms":141.8009998742491},{"cpu_ms":98.79399999999983,"wall_ms":155.89084499515593},{"cpu_ms":98.84500000000074,"wall_ms":108.3019410725683},{"cpu_ms":100.69599999999923,"wall_ms":146.34105283766985},{"cpu_ms":100.0740000000011,"wall_ms":141.6026500519365}],"after_median_cpu_ms":98.84500000000074}, + {"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":130.5390000000024,"wall_ms":175.02262187190354},{"cpu_ms":127.94499999999687,"wall_ms":152.5856510270387},{"cpu_ms":125.78600000000151,"wall_ms":137.03888305462897},{"cpu_ms":137.28799999999808,"wall_ms":160.32336791977286},{"cpu_ms":139.5149999999994,"wall_ms":196.6064979787916},{"cpu_ms":135.35699999999906,"wall_ms":149.24896601587534},{"cpu_ms":138.2930000000009,"wall_ms":181.91860592924058},{"cpu_ms":135.4849999999992,"wall_ms":193.27181903645396},{"cpu_ms":120.44500000000014,"wall_ms":172.09382890723646},{"cpu_ms":144.83399999999946,"wall_ms":246.8819129280746},{"cpu_ms":136.80599999999998,"wall_ms":144.21537099406123},{"cpu_ms":134.92899999999963,"wall_ms":144.47707100771368},{"cpu_ms":137.57099999999767,"wall_ms":143.40252289548516},{"cpu_ms":138.3320000000019,"wall_ms":144.6442420128733},{"cpu_ms":139.67799999999997,"wall_ms":143.68484006263316}],"before_median_cpu_ms":136.80599999999998,"after":[{"cpu_ms":138.67699999999772,"wall_ms":167.8737250622362},{"cpu_ms":133.0910000000003,"wall_ms":163.09094917960465},{"cpu_ms":125.50200000000089,"wall_ms":173.79008792340755},{"cpu_ms":141.38600000000068,"wall_ms":153.33373798057437},{"cpu_ms":138.3250000000018,"wall_ms":173.88305300846696},{"cpu_ms":139.18900000000178,"wall_ms":159.68503500334918},{"cpu_ms":137.67799999999752,"wall_ms":166.7701518163085},{"cpu_ms":134.95400000000046,"wall_ms":161.45028290338814},{"cpu_ms":139.49200000000062,"wall_ms":170.92170915566385},{"cpu_ms":137.80500000000018,"wall_ms":161.9660770520568},{"cpu_ms":131.6360000000003,"wall_ms":144.35988198965788},{"cpu_ms":139.32700000000153,"wall_ms":147.20600889995694},{"cpu_ms":136.54999999999973,"wall_ms":147.47505192644894},{"cpu_ms":139.58299999999824,"wall_ms":179.39168494194746},{"cpu_ms":139.7490000000019,"wall_ms":147.77994202449918}],"after_median_cpu_ms":138.3250000000018}, + {"name":"map_1m","node_exit":0,"before":[{"cpu_ms":321.15699999999947,"wall_ms":387.64754915609956},{"cpu_ms":329.49400000000037,"wall_ms":348.60667609609663},{"cpu_ms":331.2080000000002,"wall_ms":339.7069170605391},{"cpu_ms":344.1679999999998,"wall_ms":351.68572794646025},{"cpu_ms":328.9609999999996,"wall_ms":411.51867993175983},{"cpu_ms":325.8329999999994,"wall_ms":346.3766190689057},{"cpu_ms":327.80699999999996,"wall_ms":378.3071378711611},{"cpu_ms":327.9360000000011,"wall_ms":348.73039717786014},{"cpu_ms":321.2469999999996,"wall_ms":333.49903812631965},{"cpu_ms":313.37299999999857,"wall_ms":423.8452611025423},{"cpu_ms":314.00799999999765,"wall_ms":353.1606250908226},{"cpu_ms":328.0110000000036,"wall_ms":357.5593321584165},{"cpu_ms":326.7760000000024,"wall_ms":341.30964195355773},{"cpu_ms":319.70799999999855,"wall_ms":373.0765350628644},{"cpu_ms":341.35700000000213,"wall_ms":424.51708717271686}],"before_median_cpu_ms":327.80699999999996,"after":[{"cpu_ms":320.72700000000157,"wall_ms":334.18488991446793},{"cpu_ms":338.37799999999874,"wall_ms":398.9149008411914},{"cpu_ms":325.31400000000235,"wall_ms":432.7041229698807},{"cpu_ms":305.27999999999975,"wall_ms":334.01477290317416},{"cpu_ms":305.1150000000007,"wall_ms":324.5919868350029},{"cpu_ms":320.95999999999947,"wall_ms":362.34106682240963},{"cpu_ms":328.1209999999994,"wall_ms":351.2161110993475},{"cpu_ms":319.0659999999994,"wall_ms":330.2944309543818},{"cpu_ms":319.0090000000012,"wall_ms":354.9111250322312},{"cpu_ms":324.38099999999895,"wall_ms":356.2403591349721},{"cpu_ms":326.52000000000214,"wall_ms":361.15236580371857},{"cpu_ms":321.3539999999995,"wall_ms":406.18564607575536},{"cpu_ms":336.3079999999954,"wall_ms":377.4655149318278},{"cpu_ms":330.33500000000515,"wall_ms":340.814036084339},{"cpu_ms":314.4369999999981,"wall_ms":348.7489619292319}],"after_median_cpu_ms":321.3539999999995}, + {"name":"object_deep_clone","node_exit":0,"before":[{"cpu_ms":30.17600000000442,"wall_ms":45.78796005807817},{"cpu_ms":29.558999999999003,"wall_ms":44.8216840159148},{"cpu_ms":29.5109999999994,"wall_ms":44.8029600083828},{"cpu_ms":29.83300000000355,"wall_ms":40.31199892051518},{"cpu_ms":27.07200000000398,"wall_ms":41.474289959296584},{"cpu_ms":26.454000000001088,"wall_ms":40.91077693738043},{"cpu_ms":27.36299999999403,"wall_ms":38.144570076838136},{"cpu_ms":27.41200000000532,"wall_ms":40.88945803232491},{"cpu_ms":29.3969999999959,"wall_ms":45.680479146540165},{"cpu_ms":26.88600000000463,"wall_ms":44.351004995405674},{"cpu_ms":28.171999999997865,"wall_ms":45.60244409367442},{"cpu_ms":28.303999999998553,"wall_ms":38.449529092758894},{"cpu_ms":26.9880000000029,"wall_ms":44.614568119868636},{"cpu_ms":27.98100000000403,"wall_ms":44.62429601699114},{"cpu_ms":26.409000000001015,"wall_ms":42.45031392201781}],"before_median_cpu_ms":27.98100000000403,"after":[{"cpu_ms":30.89199999999437,"wall_ms":44.787149876356125},{"cpu_ms":31.092000000001008,"wall_ms":44.433669885620475},{"cpu_ms":28.323999999997795,"wall_ms":45.47940380871296},{"cpu_ms":27.62899999999746,"wall_ms":42.13698906823993},{"cpu_ms":28.351999999998156,"wall_ms":42.70120314322412},{"cpu_ms":30.178999999996847,"wall_ms":42.02771419659257},{"cpu_ms":29.884000000002686,"wall_ms":67.18717282637954},{"cpu_ms":25.354999999997574,"wall_ms":42.751146014779806},{"cpu_ms":28.266000000002123,"wall_ms":45.152733102440834},{"cpu_ms":27.09000000000117,"wall_ms":33.40146690607071},{"cpu_ms":28.917999999997335,"wall_ms":43.01542998291552},{"cpu_ms":26.519000000000403,"wall_ms":42.15551307424903},{"cpu_ms":28.967999999998995,"wall_ms":36.44391079433262},{"cpu_ms":28.354999999997688,"wall_ms":46.253640903159976},{"cpu_ms":27.251999999997167,"wall_ms":42.602027067914605}],"after_median_cpu_ms":28.351999999998156}, + {"name":"promise_all_chains","node_exit":0,"before":[{"cpu_ms":159.40200000000004,"wall_ms":172.58650809526443},{"cpu_ms":159.45899999999824,"wall_ms":171.9417329877615},{"cpu_ms":167.56600000000077,"wall_ms":197.3738179076463},{"cpu_ms":164.30300000000386,"wall_ms":168.7044808641076},{"cpu_ms":172.6069999999993,"wall_ms":204.53487406484783},{"cpu_ms":156.2940000000026,"wall_ms":165.65538197755814},{"cpu_ms":153.82000000000318,"wall_ms":160.7836161274463},{"cpu_ms":159.90599999999944,"wall_ms":163.57492399401963},{"cpu_ms":163.4170000000026,"wall_ms":192.63124419376254},{"cpu_ms":165.15799999999814,"wall_ms":175.44800508767366},{"cpu_ms":169.33199999999715,"wall_ms":183.732058852911},{"cpu_ms":155.64200000000028,"wall_ms":165.800292044878},{"cpu_ms":164.129999999993,"wall_ms":176.40761914663017},{"cpu_ms":181.74300000000443,"wall_ms":188.40538407675922},{"cpu_ms":188.5249999999985,"wall_ms":247.68529599532485}],"before_median_cpu_ms":164.129999999993,"after":[{"cpu_ms":154.57599999999871,"wall_ms":184.04481513425708},{"cpu_ms":160.91000000000122,"wall_ms":181.3412308692932},{"cpu_ms":163.41800000000006,"wall_ms":187.4737141188234},{"cpu_ms":156.05499999999495,"wall_ms":162.74600406177342},{"cpu_ms":166.67699999999996,"wall_ms":170.68726802244782},{"cpu_ms":171.36899999999855,"wall_ms":181.8441809155047},{"cpu_ms":204.59400000000016,"wall_ms":215.7804719172418},{"cpu_ms":173.50700000000074,"wall_ms":180.4344558622688},{"cpu_ms":176.75599999999747,"wall_ms":200.30440506525338},{"cpu_ms":173.83199999999732,"wall_ms":189.3137930892408},{"cpu_ms":176.05500000000518,"wall_ms":208.197969943285},{"cpu_ms":137.4840000000006,"wall_ms":144.20205214992166},{"cpu_ms":163.29300000000302,"wall_ms":168.7152311205864},{"cpu_ms":189.66499999999797,"wall_ms":211.1082288902253},{"cpu_ms":187.14800000000054,"wall_ms":219.69835390336812}],"after_median_cpu_ms":171.36899999999855}, + {"name":"regex_replace","node_exit":0,"before":[{"cpu_ms":6614.903999999996,"wall_ms":6681.515750940889},{"cpu_ms":6540.084000000007,"wall_ms":6603.9007601793855},{"cpu_ms":6464.850999999996,"wall_ms":6555.65999285318},{"cpu_ms":6037.236000000007,"wall_ms":6118.999780155718},{"cpu_ms":6702.635000000001,"wall_ms":6764.353852020577},{"cpu_ms":6497.793999999999,"wall_ms":6623.391842003912},{"cpu_ms":6267.12999999998,"wall_ms":6324.95822920464},{"cpu_ms":6602.202000000006,"wall_ms":6705.560089088976},{"cpu_ms":6153.973000000007,"wall_ms":6199.788762954995},{"cpu_ms":4848.4339999999975,"wall_ms":4868.982532061636},{"cpu_ms":3882.9259999999977,"wall_ms":3900.9636379778385},{"cpu_ms":3912.9670000000087,"wall_ms":3925.3085548989475},{"cpu_ms":4443.982000000005,"wall_ms":4469.3796979263425},{"cpu_ms":5687.6499999999905,"wall_ms":5712.084352038801},{"cpu_ms":5144.302000000011,"wall_ms":5164.642516057938}],"before_median_cpu_ms":6153.973000000007,"after":[{"cpu_ms":5448.045,"wall_ms":5554.430914111435},{"cpu_ms":5477.632999999997,"wall_ms":5517.922207014635},{"cpu_ms":5690.043000000003,"wall_ms":5794.42719114013},{"cpu_ms":5467.097999999993,"wall_ms":5509.076608112082},{"cpu_ms":5604.403000000005,"wall_ms":5643.932017032057},{"cpu_ms":5739.834000000002,"wall_ms":6080.569704994559},{"cpu_ms":5620.226000000002,"wall_ms":5746.890272013843},{"cpu_ms":5354.0529999999935,"wall_ms":5386.81697496213},{"cpu_ms":4745.294999999998,"wall_ms":4792.503774166107},{"cpu_ms":3309.6649999999954,"wall_ms":3328.3802911173552},{"cpu_ms":3351.189000000005,"wall_ms":3365.4886758886278},{"cpu_ms":3315.5380000000036,"wall_ms":3329.8418070189655},{"cpu_ms":3360.683999999992,"wall_ms":3375.0386191532016},{"cpu_ms":3370.5420000000004,"wall_ms":3388.09163402766},{"cpu_ms":3412.988000000013,"wall_ms":3426.3609279878438}],"after_median_cpu_ms":5354.0529999999935}, + {"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":42.66200000000708,"wall_ms":48.98651293478906},{"cpu_ms":39.748000000003,"wall_ms":48.89081488363445},{"cpu_ms":49.291000000010854,"wall_ms":52.74041905067861},{"cpu_ms":32.37100000001192,"wall_ms":39.875795831903815},{"cpu_ms":44.429000000008045,"wall_ms":50.74788490310311},{"cpu_ms":43.568999999990865,"wall_ms":51.46547593176365},{"cpu_ms":42.45599999998717,"wall_ms":48.88390190899372},{"cpu_ms":44.944000000015194,"wall_ms":49.38855511136353},{"cpu_ms":41.184999999984484,"wall_ms":46.54599213972688},{"cpu_ms":39.954000000022916,"wall_ms":46.31053190678358},{"cpu_ms":41.95300000000657,"wall_ms":51.33899999782443},{"cpu_ms":43.919000000016695,"wall_ms":52.5167309679091},{"cpu_ms":49.68099999999254,"wall_ms":53.99402207694948},{"cpu_ms":44.81400000000235,"wall_ms":53.16761811263859},{"cpu_ms":27.52799999998956,"wall_ms":32.92065695859492}],"before_median_cpu_ms":42.66200000000708,"after":[{"cpu_ms":42.997999999982994,"wall_ms":49.41976279951632},{"cpu_ms":44.49299999998857,"wall_ms":49.89332798868418},{"cpu_ms":45.62799999999356,"wall_ms":49.98975899070501},{"cpu_ms":43.63499999999476,"wall_ms":52.922178991138935},{"cpu_ms":43.8299999999856,"wall_ms":51.3609410263598},{"cpu_ms":37.316000000004124,"wall_ms":50.49653584137559},{"cpu_ms":43.87700000000905,"wall_ms":49.22929685562849},{"cpu_ms":45.277999999996155,"wall_ms":49.64984301477671},{"cpu_ms":41.954000000004044,"wall_ms":48.31466800533235},{"cpu_ms":44.84699999997588,"wall_ms":51.13474791869521},{"cpu_ms":47.83199999999965,"wall_ms":52.165533881634474},{"cpu_ms":47.8559999999959,"wall_ms":52.51733190380037},{"cpu_ms":47.11100000000101,"wall_ms":52.481736056506634},{"cpu_ms":33.19600000000378,"wall_ms":41.57533193938434},{"cpu_ms":29.268000000001848,"wall_ms":34.42919603548944}],"after_median_cpu_ms":43.87700000000905}, + {"name":"string_split_map_join","node_exit":0,"before":[{"cpu_ms":281.3110000000165,"wall_ms":287.6785558182746},{"cpu_ms":279.1649999999777,"wall_ms":284.5079100225121},{"cpu_ms":310.9290000000158,"wall_ms":315.2855320367962},{"cpu_ms":308.9779999999962,"wall_ms":318.4256609529257},{"cpu_ms":312.97899999998435,"wall_ms":319.3614180199802},{"cpu_ms":310.69700000000466,"wall_ms":317.1082278713584},{"cpu_ms":311.90699999999083,"wall_ms":317.29328306391835},{"cpu_ms":261.44800000000146,"wall_ms":272.7814728859812},{"cpu_ms":294.3789999999922,"wall_ms":300.0323430169374},{"cpu_ms":312.75300000001494,"wall_ms":318.1630379986018},{"cpu_ms":308.9849999999785,"wall_ms":313.36115300655365},{"cpu_ms":307.45600000000195,"wall_ms":313.8745229225606},{"cpu_ms":260.33599999999524,"wall_ms":268.1716070510447},{"cpu_ms":312.2199999999964,"wall_ms":316.6506008710712},{"cpu_ms":250.28299999999604,"wall_ms":257.64109613373876}],"before_median_cpu_ms":308.9779999999962,"after":[{"cpu_ms":185.30799999999203,"wall_ms":191.60403800196946},{"cpu_ms":271.78600000002007,"wall_ms":275.17228201031685},{"cpu_ms":267.2329999999761,"wall_ms":272.64357591047883},{"cpu_ms":302.6169999999979,"wall_ms":310.0083989556879},{"cpu_ms":308.73600000001034,"wall_ms":313.10693989507854},{"cpu_ms":304.25500000001193,"wall_ms":310.6351690366864},{"cpu_ms":306.29099999998743,"wall_ms":311.64521793834865},{"cpu_ms":306.4290000000085,"wall_ms":313.82482103072107},{"cpu_ms":307.848000000007,"wall_ms":314.2231849487871},{"cpu_ms":304.2199999999866,"wall_ms":310.6146811041981},{"cpu_ms":302.57600000001617,"wall_ms":309.9450988229364},{"cpu_ms":305.09399999999687,"wall_ms":309.7236349713057},{"cpu_ms":307.0950000000039,"wall_ms":312.52882815897465},{"cpu_ms":300.5100000000027,"wall_ms":307.9026520717889},{"cpu_ms":306.45300000000475,"wall_ms":310.84526097401977}],"after_median_cpu_ms":304.25500000001193}, + {"name":"string_template_interp","node_exit":0,"before":[{"cpu_ms":77.58000000001175,"wall_ms":94.23826495185494},{"cpu_ms":78.32299999998327,"wall_ms":86.76141197793186},{"cpu_ms":81.96300000000178,"wall_ms":86.33596799336374},{"cpu_ms":76.43899999999348,"wall_ms":86.93774207495153},{"cpu_ms":78.5370000000114,"wall_ms":85.86572692729533},{"cpu_ms":71.73600000001557,"wall_ms":77.17906194739044},{"cpu_ms":81.50100000000293,"wall_ms":86.88971097581089},{"cpu_ms":80.60300000002485,"wall_ms":86.9570670183748},{"cpu_ms":79.7239999999988,"wall_ms":87.13040105067194},{"cpu_ms":77.85200000000714,"wall_ms":86.25724911689758},{"cpu_ms":74.33700000001409,"wall_ms":81.79386705160141},{"cpu_ms":78.79699999998024,"wall_ms":86.22890594415367},{"cpu_ms":82.51100000001088,"wall_ms":86.94551582448184},{"cpu_ms":82.14799999998945,"wall_ms":87.59674290195107},{"cpu_ms":84.31600000000117,"wall_ms":88.81566999480128}],"before_median_cpu_ms":78.79699999998024,"after":[{"cpu_ms":76.23399999999947,"wall_ms":84.63434712029994},{"cpu_ms":77.67100000000937,"wall_ms":85.04511485807598},{"cpu_ms":77.95400000000541,"wall_ms":85.36775805987418},{"cpu_ms":75.64299999998525,"wall_ms":84.980373037979},{"cpu_ms":78.63199999999892,"wall_ms":84.08437808975577},{"cpu_ms":66.35099999999738,"wall_ms":73.86835501529276},{"cpu_ms":76.51399999997466,"wall_ms":82.87222287617624},{"cpu_ms":79.23999999999864,"wall_ms":85.63913381658494},{"cpu_ms":75.34299999997529,"wall_ms":84.7672838717699},{"cpu_ms":79.87300000002051,"wall_ms":85.33016708679497},{"cpu_ms":79.88799999998264,"wall_ms":85.27242904528975},{"cpu_ms":80.82000000001699,"wall_ms":87.26269798353314},{"cpu_ms":76.96999999998866,"wall_ms":85.47010808251798},{"cpu_ms":83.36199999999394,"wall_ms":91.93848702125251},{"cpu_ms":82.28600000001052,"wall_ms":85.63860296271741}],"after_median_cpu_ms":77.95400000000541} +] diff --git a/benchmarks/regexp-construction/compare.py b/benchmarks/regexp-construction/compare.py new file mode 100644 index 0000000000..da2370d585 --- /dev/null +++ b/benchmarks/regexp-construction/compare.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Interleaved before/after runtime benchmark checks, with the pinned Node oracle. + +Each directory must hold a compiler and the static archives from the same build. +Run this on the build host. Timing is child user CPU, avoiding scheduler wait. +""" +import argparse +import json +import os +from pathlib import Path +import resource +import shutil +import statistics +import subprocess +import time + + +def positive_integer(value): + try: + number = int(value) + except ValueError: + raise argparse.ArgumentTypeError("expected a positive integer") from None + if number < 1: + raise argparse.ArgumentTypeError("expected a positive integer") + return number + + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument("--before", type=Path, required=True) +parser.add_argument("--after", type=Path) +parser.add_argument("--output", type=Path, required=True) +parser.add_argument("--runs", type=positive_integer, default=12) +parser.add_argument("--filter", default="") +parser.add_argument("--paired-controls", action="store_true", help="Add two identical-copy labels per build to each round") +args = parser.parse_args() +if args.paired_controls and args.after is None: + parser.error('--paired-controls requires --after') +args.output.mkdir(parents=True, exist_ok=True) +runner = args.output / "run-benchmark" +root = Path(__file__).resolve().parents[2] +rows = [] + + +def run(command, **kwargs): + before = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime + start = time.monotonic() + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + timeout=180, **kwargs) + cpu = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime - before + return result, cpu, time.monotonic() - start + + +for source in sorted((root / "benchmarks/app-patterns/kernels").glob("*.ts")): + if args.filter not in source.stem: + continue + row = {"name": source.stem} + binaries = {} + for label, directory in [("before", args.before), ("after", args.after)]: + if directory is None: + continue + binary = args.output / f"{source.stem}-{label}" + environment = dict(os.environ, PERRY_RUNTIME_DIR=str(directory)) + result, _, _ = run([str(directory / "perry"), "compile", str(source), + "--no-auto-optimize", "-o", str(binary)], env=environment) + (args.output / f"{source.stem}-{label}.compile.log").write_bytes(result.stderr) + if result.returncode: + row[label + "_error"] = "compile" + else: + binaries[label] = binary + oracle, _, _ = run(["node", "--experimental-strip-types", str(source)]) + expected = oracle.stdout.strip() + row["node_exit"] = oracle.returncode + samples = {label: [] for label in binaries} + if args.paired_controls: + for label in ('before', 'after'): + binaries[label + '_control'] = binaries[label] + samples[label + '_control'] = [] + for iteration in range(args.runs + 1): + order = list(binaries) + if args.paired_controls: + order = [('before', 'after', 'after_control', 'before_control'), + ('after', 'before', 'before_control', 'after_control'), + ('before_control', 'after_control', 'after', 'before'), + ('after_control', 'before_control', 'before', 'after')][iteration % 4] + elif iteration % 2: + order.reverse() + for label in order: + # argv[0]/execPath length can change startup allocation and GC + # layout. An A/A control showed ~1.7% for byte-identical binaries + # named *-before and *-after. Copy outside the timed window and + # execute both builds at the same pathname/inode. + shutil.copyfile(binaries[label], runner) + runner.chmod(0o755) + result, cpu, wall = run([str(runner)]) + if result.returncode or result.stdout.strip() != expected: + row[label + "_error"] = { + "exit": result.returncode, + "stdout": result.stdout.decode(errors="replace")[:500], + "node": expected.decode(errors="replace")[:500], + } + if iteration: + samples[label].append({"cpu_ms": cpu * 1000, "wall_ms": wall * 1000}) + for label, values in samples.items(): + row[label] = values + row[label + "_median_cpu_ms"] = statistics.median(v["cpu_ms"] for v in values) + rows.append(row) + (args.output / "results.json").write_text(json.dumps(rows, indent=2) + "\n") + print(json.dumps({k: v for k, v in row.items() if k not in samples}), flush=True) + +if any(row["node_exit"] != 0 or any(key.endswith("_error") for key in row) for row in rows): + raise SystemExit("A compiler, runtime, or Node oracle failed; see results.json") diff --git a/benchmarks/regexp-construction/controls-samples.json b/benchmarks/regexp-construction/controls-samples.json new file mode 100644 index 0000000000..d9a4b2a009 --- /dev/null +++ b/benchmarks/regexp-construction/controls-samples.json @@ -0,0 +1,11 @@ +[ + {"kind":"A/A fixed path 101 runs","rows":[{"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":137.2840000000002,"wall_ms":140.69012412801385},{"cpu_ms":134.4979999999998,"wall_ms":141.86378614977002},{"cpu_ms":133.61299999999997,"wall_ms":140.98806004039943},{"cpu_ms":120.54700000000018,"wall_ms":128.86665295809507},{"cpu_ms":122.91099999999977,"wall_ms":129.31745499372482},{"cpu_ms":126.77800000000028,"wall_ms":133.1113639753312},{"cpu_ms":136.23100000000045,"wall_ms":140.61628584749997},{"cpu_ms":133.1699999999998,"wall_ms":141.59009489230812},{"cpu_ms":75.21299999999975,"wall_ms":80.0163121894002},{"cpu_ms":127.04599999999999,"wall_ms":131.47135986946523},{"cpu_ms":85.26200000000017,"wall_ms":95.5453950446099},{"cpu_ms":79.00599999999969,"wall_ms":84.95366596616805},{"cpu_ms":133.934,"wall_ms":141.3262221030891},{"cpu_ms":135.5119999999994,"wall_ms":140.9056659322232},{"cpu_ms":132.44599999999986,"wall_ms":141.8174400459975},{"cpu_ms":128.35200000000046,"wall_ms":133.71988013386726},{"cpu_ms":122.1350000000001,"wall_ms":130.51742711104453},{"cpu_ms":118.61799999999967,"wall_ms":124.00058098137379},{"cpu_ms":116.42799999999997,"wall_ms":122.80313391238451},{"cpu_ms":132.6320000000001,"wall_ms":134.02620307169855},{"cpu_ms":123.64599999999993,"wall_ms":131.02866197004914},{"cpu_ms":124.56400000000033,"wall_ms":128.91798908822238},{"cpu_ms":127.66000000000055,"wall_ms":134.0669090859592},{"cpu_ms":132.3749999999997,"wall_ms":137.70686998032033},{"cpu_ms":131.59100000000024,"wall_ms":134.93775599636137},{"cpu_ms":128.48299999999924,"wall_ms":135.9171059448272},{"cpu_ms":135.48600000000022,"wall_ms":138.87016288936138},{"cpu_ms":134.02500000000117,"wall_ms":137.39295420236886},{"cpu_ms":124.5510000000003,"wall_ms":132.85237993113697},{"cpu_ms":128.71799999999922,"wall_ms":135.1150469854474},{"cpu_ms":138.63599999999997,"wall_ms":144.04822792857885},{"cpu_ms":136.93699999999964,"wall_ms":142.30674388818443},{"cpu_ms":134.07699999999954,"wall_ms":141.46127412095666},{"cpu_ms":118.15599999999904,"wall_ms":122.47062311507761},{"cpu_ms":131.47299999999973,"wall_ms":133.81880498491228},{"cpu_ms":121.05299999999986,"wall_ms":126.47661799564958},{"cpu_ms":130.31400000000025,"wall_ms":137.69404590129852},{"cpu_ms":123.29399999999957,"wall_ms":127.67672911286354},{"cpu_ms":130.45100000000076,"wall_ms":136.7716530803591},{"cpu_ms":130.47699999999907,"wall_ms":135.818962007761},{"cpu_ms":125.56199999999862,"wall_ms":131.93507492542267},{"cpu_ms":132.14600000000053,"wall_ms":134.49766393750906},{"cpu_ms":136.36400000000037,"wall_ms":143.71762005612254},{"cpu_ms":139.38899999999956,"wall_ms":145.89860499836504},{"cpu_ms":134.6039999999995,"wall_ms":139.96688299812376},{"cpu_ms":135.46499999999995,"wall_ms":143.8829388935119},{"cpu_ms":139.91900000000078,"wall_ms":144.29193385876715},{"cpu_ms":126.28999999999913,"wall_ms":131.6909990273416},{"cpu_ms":129.08199999999857,"wall_ms":137.53367611207068},{"cpu_ms":137.9210000000004,"wall_ms":145.32518410123885},{"cpu_ms":132.26700000000056,"wall_ms":139.6800170186907},{"cpu_ms":135.72699999999926,"wall_ms":142.0882660895586},{"cpu_ms":136.15899999999925,"wall_ms":142.53921899944544},{"cpu_ms":135.69999999999993,"wall_ms":142.11520599201322},{"cpu_ms":129.5579999999994,"wall_ms":138.91239208169281},{"cpu_ms":130.60899999999975,"wall_ms":141.00026316009462},{"cpu_ms":135.19199999999998,"wall_ms":138.55014485307038},{"cpu_ms":129.4200000000032,"wall_ms":134.81000810861588},{"cpu_ms":136.93300000000264,"wall_ms":142.30972016230226},{"cpu_ms":141.43599999999878,"wall_ms":144.81919817626476},{"cpu_ms":144.01200000000003,"wall_ms":149.39185185357928},{"cpu_ms":120.77800000000138,"wall_ms":130.1577859558165},{"cpu_ms":139.70199999999977,"wall_ms":144.09333188086748},{"cpu_ms":131.86999999999927,"wall_ms":138.29988799989223},{"cpu_ms":133.33300000000037,"wall_ms":138.6941538657993},{"cpu_ms":127.67299999999793,"wall_ms":134.0408909600228},{"cpu_ms":92.75300000000186,"wall_ms":97.16895991005003},{"cpu_ms":136.7529999999988,"wall_ms":144.14234389550984},{"cpu_ms":140.28700000000072,"wall_ms":143.69001798331738},{"cpu_ms":123.78800000000112,"wall_ms":130.80400205217302},{"cpu_ms":76.10700000000037,"wall_ms":82.04358979128301},{"cpu_ms":119.23399999999873,"wall_ms":124.6178939472884},{"cpu_ms":116.7909999999992,"wall_ms":123.15623415634036},{"cpu_ms":120.02999999999986,"wall_ms":125.42039388790727},{"cpu_ms":132.84399999999863,"wall_ms":138.27370898798108},{"cpu_ms":131.03100000000012,"wall_ms":134.3781310133636},{"cpu_ms":128.47499999999812,"wall_ms":135.8747670892626},{"cpu_ms":79.28000000000068,"wall_ms":89.54220893792808},{"cpu_ms":148.64999999999995,"wall_ms":155.9973240364343},{"cpu_ms":152.8199999999984,"wall_ms":160.21998296491802},{"cpu_ms":154.57100000000068,"wall_ms":158.96104113198817},{"cpu_ms":146.30400000000066,"wall_ms":157.88514004088938},{"cpu_ms":151.06400000000164,"wall_ms":157.41857909597456},{"cpu_ms":148.92000000000039,"wall_ms":156.3199069350958},{"cpu_ms":118.92599999999831,"wall_ms":129.81452397070825},{"cpu_ms":132.28800000000263,"wall_ms":137.66682497225702},{"cpu_ms":138.2700000000021,"wall_ms":141.6544357780367},{"cpu_ms":138.6839999999978,"wall_ms":142.07705506123602},{"cpu_ms":73.28499999999849,"wall_ms":79.60864994674921},{"cpu_ms":87.80800000000255,"wall_ms":91.09940915368497},{"cpu_ms":125.826,"wall_ms":139.15775110945106},{"cpu_ms":135.62299999999894,"wall_ms":139.95653321035206},{"cpu_ms":138.14500000000152,"wall_ms":141.50949520990252},{"cpu_ms":135.84900000000033,"wall_ms":141.23782701790333},{"cpu_ms":141.1250000000024,"wall_ms":147.5254548713565},{"cpu_ms":148.38599999999857,"wall_ms":157.79996197670698},{"cpu_ms":135.84900000000033,"wall_ms":140.23068500682712},{"cpu_ms":137.25099999999912,"wall_ms":141.6333969682455},{"cpu_ms":135.32800000000123,"wall_ms":138.9914199244231},{"cpu_ms":133.60199999999978,"wall_ms":138.02311080507934},{"cpu_ms":72.73700000000005,"wall_ms":79.07614507712424}],"before_median_cpu_ms":132.28800000000263,"after":[{"cpu_ms":134.23499999999987,"wall_ms":143.5470310971141},{"cpu_ms":137.5090000000001,"wall_ms":143.86714017018676},{"cpu_ms":131.189,"wall_ms":136.599960969761},{"cpu_ms":126.11699999999982,"wall_ms":132.5443040113896},{"cpu_ms":133.07100000000017,"wall_ms":136.41579705290496},{"cpu_ms":135.85499999999985,"wall_ms":141.2802771665156},{"cpu_ms":130.94899999999976,"wall_ms":137.381412088871},{"cpu_ms":138.67600000000024,"wall_ms":142.10177096538246},{"cpu_ms":135.30399999999963,"wall_ms":140.7136470079422},{"cpu_ms":131.88699999999986,"wall_ms":138.2610851433128},{"cpu_ms":139.5810000000006,"wall_ms":142.96443294733763},{"cpu_ms":76.79800000000014,"wall_ms":79.1144659742713},{"cpu_ms":139.59100000000024,"wall_ms":144.04373988509178},{"cpu_ms":128.63200000000052,"wall_ms":134.02339816093445},{"cpu_ms":133.7849999999996,"wall_ms":143.14405899494886},{"cpu_ms":128.47000000000008,"wall_ms":132.8409891575575},{"cpu_ms":124.65999999999954,"wall_ms":130.10434503667057},{"cpu_ms":126.61500000000015,"wall_ms":134.2773619107902},{"cpu_ms":118.35400000000007,"wall_ms":124.72511501982808},{"cpu_ms":128.7250000000002,"wall_ms":134.119767928496},{"cpu_ms":120.30999999999992,"wall_ms":126.73116312362254},{"cpu_ms":121.25999999999948,"wall_ms":127.66724196262658},{"cpu_ms":128.3750000000001,"wall_ms":134.8188139963895},{"cpu_ms":129.03900000000056,"wall_ms":133.38457490317523},{"cpu_ms":127.49299999999941,"wall_ms":132.87317799404263},{"cpu_ms":130.79100000000034,"wall_ms":136.12872106023133},{"cpu_ms":133.55999999999923,"wall_ms":139.9980599526316},{"cpu_ms":127.11299999999959,"wall_ms":135.46010199934244},{"cpu_ms":131.3449999999996,"wall_ms":135.72746189311147},{"cpu_ms":133.53000000000037,"wall_ms":139.85782815143466},{"cpu_ms":130.10300000000007,"wall_ms":136.56536699272692},{"cpu_ms":138.52800000000087,"wall_ms":141.90899091772735},{"cpu_ms":130.64600000000047,"wall_ms":137.07439810968935},{"cpu_ms":114.08800000000063,"wall_ms":118.43832884915173},{"cpu_ms":123.31700000000012,"wall_ms":127.7058538980782},{"cpu_ms":124.60300000000046,"wall_ms":131.98669208213687},{"cpu_ms":125.46799999999969,"wall_ms":131.86607719399035},{"cpu_ms":131.30000000000132,"wall_ms":136.72759011387825},{"cpu_ms":134.9539999999987,"wall_ms":137.33128807507455},{"cpu_ms":102.53100000000082,"wall_ms":112.8173558972776},{"cpu_ms":126.7510000000005,"wall_ms":133.06953595019877},{"cpu_ms":136.57000000000073,"wall_ms":143.05972098372877},{"cpu_ms":137.1859999999998,"wall_ms":144.5939070545137},{"cpu_ms":133.84699999999938,"wall_ms":141.2199039477855},{"cpu_ms":138.00100000000094,"wall_ms":142.35005481168628},{"cpu_ms":138.88000000000034,"wall_ms":142.2655968926847},{"cpu_ms":139.01199999999926,"wall_ms":144.36717401258647},{"cpu_ms":139.4200000000012,"wall_ms":143.79722904413939},{"cpu_ms":136.75399999999982,"wall_ms":144.18148715049028},{"cpu_ms":136.16500000000008,"wall_ms":143.56527593918145},{"cpu_ms":137.02600000000052,"wall_ms":142.41314283572137},{"cpu_ms":136.0910000000004,"wall_ms":142.50687905587256},{"cpu_ms":141.4419999999996,"wall_ms":143.80617602728307},{"cpu_ms":136.63100000000128,"wall_ms":143.02021800540388},{"cpu_ms":138.52999999999938,"wall_ms":143.9726068638265},{"cpu_ms":137.27400000000145,"wall_ms":141.65526698343456},{"cpu_ms":137.53999999999778,"wall_ms":141.92820712924004},{"cpu_ms":93.66099999999733,"wall_ms":101.04489093646407},{"cpu_ms":135.11899999999955,"wall_ms":141.48413692601025},{"cpu_ms":140.6010000000002,"wall_ms":146.0095418151468},{"cpu_ms":137.5630000000001,"wall_ms":143.91098194755614},{"cpu_ms":140.64699999999775,"wall_ms":146.99780894443393},{"cpu_ms":138.8680000000022,"wall_ms":143.2167049497366},{"cpu_ms":133.56100000000026,"wall_ms":140.95720299519598},{"cpu_ms":137.9640000000002,"wall_ms":144.34215682558715},{"cpu_ms":127.44400000000056,"wall_ms":139.8000509943813},{"cpu_ms":137.11999999999946,"wall_ms":142.4893350340426},{"cpu_ms":136.50399999999863,"wall_ms":141.87886496074498},{"cpu_ms":135.53200000000132,"wall_ms":141.8520649895072},{"cpu_ms":133.65399999999994,"wall_ms":143.08261405676603},{"cpu_ms":126.22599999999906,"wall_ms":134.6478839404881},{"cpu_ms":129.0559999999985,"wall_ms":135.41917502880096},{"cpu_ms":124.66600000000128,"wall_ms":133.00483510829508},{"cpu_ms":140.034,"wall_ms":143.35598493926227},{"cpu_ms":134.48100000000096,"wall_ms":139.81573982164264},{"cpu_ms":130.6030000000007,"wall_ms":137.0267989113927},{"cpu_ms":128.89300000000148,"wall_ms":134.31689608842134},{"cpu_ms":74.23599999999908,"wall_ms":78.53124709799886},{"cpu_ms":75.31000000000176,"wall_ms":80.62764396890998},{"cpu_ms":154.93900000000238,"wall_ms":159.2861779499799},{"cpu_ms":151.91899999999947,"wall_ms":160.3227350860834},{"cpu_ms":154.1359999999976,"wall_ms":159.5480281393975},{"cpu_ms":150.40600000000026,"wall_ms":157.79862902127206},{"cpu_ms":120.94699999999747,"wall_ms":128.35143110714853},{"cpu_ms":128.15300000000107,"wall_ms":134.54513298347592},{"cpu_ms":138.25100000000035,"wall_ms":145.71286900900304},{"cpu_ms":137.75099999999796,"wall_ms":143.17022706381977},{"cpu_ms":131.25000000000142,"wall_ms":140.65162208862603},{"cpu_ms":125.09599999999921,"wall_ms":131.2968940474093},{"cpu_ms":80.69799999999816,"wall_ms":89.18559295125306},{"cpu_ms":82.37700000000103,"wall_ms":87.6954891718924},{"cpu_ms":137.3490000000004,"wall_ms":143.77949596382678},{"cpu_ms":138.98299999999963,"wall_ms":143.34785914979875},{"cpu_ms":139.44399999999746,"wall_ms":145.78964211978018},{"cpu_ms":135.15100000000047,"wall_ms":143.51785695180297},{"cpu_ms":137.10400000000078,"wall_ms":143.48039706237614},{"cpu_ms":131.9119999999998,"wall_ms":139.27095290273428},{"cpu_ms":135.12599999999964,"wall_ms":141.46087388508022},{"cpu_ms":132.72500000000065,"wall_ms":138.0964380223304},{"cpu_ms":132.35899999999745,"wall_ms":137.75038113817573},{"cpu_ms":125.05300000000119,"wall_ms":132.44004896841943}],"after_median_cpu_ms":133.56100000000026}]}, + {"kind":"A/B fixed path JSON 101 runs","rows":[{"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":133.98699999999985,"wall_ms":142.4270390998572},{"cpu_ms":104.72800000000015,"wall_ms":110.09515100158751},{"cpu_ms":136.83200000000005,"wall_ms":145.18516208045185},{"cpu_ms":135.06899999999968,"wall_ms":141.39718492515385},{"cpu_ms":134.75199999999975,"wall_ms":142.12008588947356},{"cpu_ms":133.94400000000007,"wall_ms":141.2931508384645},{"cpu_ms":136.22500000000005,"wall_ms":143.6228728853166},{"cpu_ms":136.19199999999987,"wall_ms":142.58700795471668},{"cpu_ms":131.24599999999998,"wall_ms":136.69978897087276},{"cpu_ms":114.41899999999983,"wall_ms":120.83255196921527},{"cpu_ms":73.7729999999992,"wall_ms":81.74482197500765},{"cpu_ms":125.26800000000016,"wall_ms":131.66121416725218},{"cpu_ms":128.89899999999966,"wall_ms":132.26859900169075},{"cpu_ms":131.81500000000045,"wall_ms":135.23271796293557},{"cpu_ms":125.11600000000023,"wall_ms":133.50236392579973},{"cpu_ms":129.08700000000016,"wall_ms":133.63961107097566},{"cpu_ms":130.2159999999999,"wall_ms":134.62987099774182},{"cpu_ms":129.43200000000044,"wall_ms":134.80894593521953},{"cpu_ms":130.50799999999984,"wall_ms":135.93226508237422},{"cpu_ms":124.57999999999991,"wall_ms":130.96592505462468},{"cpu_ms":134.0250000000003,"wall_ms":137.44175503961742},{"cpu_ms":131.27399999999943,"wall_ms":138.67664197459817},{"cpu_ms":131.07700000000034,"wall_ms":135.4440120048821},{"cpu_ms":129.162,"wall_ms":133.56303796172142},{"cpu_ms":106.51400000000066,"wall_ms":110.85291695781052},{"cpu_ms":131.89399999999907,"wall_ms":138.2979538757354},{"cpu_ms":131.37199999999893,"wall_ms":135.7617259491235},{"cpu_ms":131.48400000000038,"wall_ms":137.9086570814252},{"cpu_ms":109.85899999999837,"wall_ms":114.26991107873619},{"cpu_ms":77.53000000000121,"wall_ms":82.9329111147672},{"cpu_ms":123.79600000000046,"wall_ms":129.24884795211256},{"cpu_ms":118.49199999999982,"wall_ms":128.86721501126885},{"cpu_ms":133.0480000000005,"wall_ms":139.42515081726015},{"cpu_ms":128.0000000000001,"wall_ms":134.3847329262644},{"cpu_ms":124.90600000000107,"wall_ms":133.30322201363742},{"cpu_ms":128.78100000000003,"wall_ms":133.15168907865882},{"cpu_ms":124.69099999999855,"wall_ms":131.10249117016792},{"cpu_ms":120.80100000000016,"wall_ms":125.16242195852101},{"cpu_ms":127.90700000000044,"wall_ms":135.2682840079069},{"cpu_ms":131.69399999999953,"wall_ms":135.02432801760733},{"cpu_ms":132.09599999999887,"wall_ms":138.49551300518215},{"cpu_ms":129.24899999999974,"wall_ms":136.6994478739798},{"cpu_ms":123.31200000000031,"wall_ms":130.70215308107436},{"cpu_ms":120.70699999999945,"wall_ms":127.14128009974957},{"cpu_ms":126.56499999999937,"wall_ms":132.01913400553167},{"cpu_ms":122.35900000000122,"wall_ms":128.66984610445797},{"cpu_ms":144.32299999999998,"wall_ms":148.75486306846142},{"cpu_ms":123.32900000000002,"wall_ms":125.74774608947337},{"cpu_ms":124.32699999999919,"wall_ms":130.35415206104517},{"cpu_ms":133.13800000000066,"wall_ms":139.15406400337815},{"cpu_ms":128.01499999999953,"wall_ms":141.60734810866416},{"cpu_ms":126.36099999999928,"wall_ms":140.02162590622902},{"cpu_ms":121.39299999999942,"wall_ms":151.83771192096174},{"cpu_ms":142.07200000000063,"wall_ms":148.93004903569818},{"cpu_ms":131.09299999999814,"wall_ms":137.0746491011232},{"cpu_ms":120.64800000000275,"wall_ms":127.66853393986821},{"cpu_ms":127.1880000000003,"wall_ms":152.65642199665308},{"cpu_ms":128.17799999999835,"wall_ms":143.61591101624072},{"cpu_ms":126.02600000000308,"wall_ms":144.46113002486527},{"cpu_ms":118.78999999999706,"wall_ms":138.19571305066347},{"cpu_ms":124.82400000000027,"wall_ms":156.98936791159213},{"cpu_ms":121.42100000000156,"wall_ms":132.6575460843742},{"cpu_ms":128.47899999999868,"wall_ms":140.9953241236508},{"cpu_ms":128.5720000000019,"wall_ms":149.02040781453252},{"cpu_ms":125.5039999999994,"wall_ms":137.10363302379847},{"cpu_ms":133.1230000000012,"wall_ms":156.74448991194367},{"cpu_ms":129.1019999999996,"wall_ms":147.65704097226262},{"cpu_ms":130.61099999999826,"wall_ms":133.52631009183824},{"cpu_ms":123.57199999999935,"wall_ms":132.16059701517224},{"cpu_ms":125.86600000000203,"wall_ms":135.30965195968747},{"cpu_ms":126.95899999999938,"wall_ms":131.3942871056497},{"cpu_ms":131.6720000000018,"wall_ms":137.14290806092322},{"cpu_ms":114.19799999999825,"wall_ms":118.53757500648499},{"cpu_ms":107.76500000000055,"wall_ms":117.09882598370314},{"cpu_ms":127.22800000000234,"wall_ms":134.69600514508784},{"cpu_ms":127.5909999999989,"wall_ms":134.02361795306206},{"cpu_ms":130.80400000000125,"wall_ms":139.165956992656},{"cpu_ms":138.04700000000025,"wall_ms":143.4438289143145},{"cpu_ms":89.98499999999865,"wall_ms":97.03031089156866},{"cpu_ms":136.59900000000036,"wall_ms":143.95411196164787},{"cpu_ms":97.68299999999996,"wall_ms":102.00645797885954},{"cpu_ms":103.56800000000277,"wall_ms":107.89922787807882},{"cpu_ms":140.22699999999944,"wall_ms":148.62358709797263},{"cpu_ms":140.6080000000003,"wall_ms":145.9865600336343},{"cpu_ms":126.32899999999836,"wall_ms":130.86165999993682},{"cpu_ms":121.39000000000166,"wall_ms":129.81560709886253},{"cpu_ms":123.44000000000221,"wall_ms":130.85616007447243},{"cpu_ms":127.4179999999987,"wall_ms":131.87156803905964},{"cpu_ms":122.166,"wall_ms":139.87892912700772},{"cpu_ms":132.90100000000038,"wall_ms":136.57697895541787},{"cpu_ms":129.56399999999846,"wall_ms":273.556079948321},{"cpu_ms":121.37799999999999,"wall_ms":387.60368595831096},{"cpu_ms":131.88399999999945,"wall_ms":506.4586328808218},{"cpu_ms":134.49299999999909,"wall_ms":280.137576861307},{"cpu_ms":131.9059999999972,"wall_ms":217.87865296937525},{"cpu_ms":136.922000000002,"wall_ms":256.37460895814},{"cpu_ms":142.8500000000028,"wall_ms":161.88871092163026},{"cpu_ms":135.0789999999975,"wall_ms":169.90579990670085},{"cpu_ms":139.73299999999966,"wall_ms":197.94269418343902},{"cpu_ms":135.270000000002,"wall_ms":183.01902106031775},{"cpu_ms":137.43499999999997,"wall_ms":161.94522799924016}],"before_median_cpu_ms":128.5720000000019,"after":[{"cpu_ms":174.90499999999986,"wall_ms":183.31390200182796},{"cpu_ms":98.2829999999999,"wall_ms":102.5872128084302},{"cpu_ms":92.4450000000001,"wall_ms":97.77007321827114},{"cpu_ms":140.32800000000023,"wall_ms":146.70929103158414},{"cpu_ms":140.15900000000013,"wall_ms":147.55693404003978},{"cpu_ms":136.58899999999986,"wall_ms":143.99986807256937},{"cpu_ms":139.602,"wall_ms":143.98506004363298},{"cpu_ms":139.2510000000007,"wall_ms":142.6325631327927},{"cpu_ms":138.45699999999982,"wall_ms":146.85588399879634},{"cpu_ms":79.0329999999999,"wall_ms":81.36303792707622},{"cpu_ms":92.17700000000039,"wall_ms":96.47220792248845},{"cpu_ms":122.83700000000053,"wall_ms":129.21173800714314},{"cpu_ms":133.5829999999998,"wall_ms":137.95233913697302},{"cpu_ms":129.12999999999997,"wall_ms":133.51760408841074},{"cpu_ms":129.38099999999952,"wall_ms":136.8047350551933},{"cpu_ms":131.7649999999997,"wall_ms":135.18769294023514},{"cpu_ms":128.45700000000005,"wall_ms":134.8566839005798},{"cpu_ms":128.6259999999997,"wall_ms":135.0690508261323},{"cpu_ms":126.34100000000004,"wall_ms":134.69382980838418},{"cpu_ms":126.13300000000027,"wall_ms":133.54612607508898},{"cpu_ms":135.46000000000015,"wall_ms":136.85760297812521},{"cpu_ms":132.12700000000055,"wall_ms":138.51393782533705},{"cpu_ms":130.00399999999956,"wall_ms":138.3978221565485},{"cpu_ms":127.50600000000034,"wall_ms":134.91957285441458},{"cpu_ms":127.0939999999996,"wall_ms":132.43363797664642},{"cpu_ms":129.60499999999973,"wall_ms":136.94707094691694},{"cpu_ms":133.24400000000125,"wall_ms":137.6680477987975},{"cpu_ms":132.93900000000036,"wall_ms":138.39514600113034},{"cpu_ms":132.0300000000003,"wall_ms":140.5751290731132},{"cpu_ms":132.13599999999914,"wall_ms":138.54507612995803},{"cpu_ms":129.19400000000002,"wall_ms":135.56770398281515},{"cpu_ms":128.7350000000007,"wall_ms":131.16357498802245},{"cpu_ms":124.62799999999952,"wall_ms":132.0297629572451},{"cpu_ms":126.59999999999982,"wall_ms":133.94268695265055},{"cpu_ms":129.31899999999885,"wall_ms":133.6722718551755},{"cpu_ms":122.40300000000026,"wall_ms":129.76125511340797},{"cpu_ms":133.21000000000004,"wall_ms":136.58640696667135},{"cpu_ms":132.2100000000006,"wall_ms":136.5944619756192},{"cpu_ms":129.40099999999967,"wall_ms":135.8030941337347},{"cpu_ms":130.95200000000062,"wall_ms":138.37650208733976},{"cpu_ms":134.57900000000046,"wall_ms":140.0246701668948},{"cpu_ms":130.72900000000055,"wall_ms":138.10497405938804},{"cpu_ms":134.7480000000001,"wall_ms":143.15180410631},{"cpu_ms":132.37600000000072,"wall_ms":136.74185681156814},{"cpu_ms":131.6159999999993,"wall_ms":135.98385103978217},{"cpu_ms":124.6779999999994,"wall_ms":129.05631680041552},{"cpu_ms":126.26099999999951,"wall_ms":129.61995997466147},{"cpu_ms":131.6360000000003,"wall_ms":137.97659380361438},{"cpu_ms":127.42500000000057,"wall_ms":131.75712386146188},{"cpu_ms":130.20999999999992,"wall_ms":136.60858804360032},{"cpu_ms":129.59900000000067,"wall_ms":228.7285749334842},{"cpu_ms":126.80100000000039,"wall_ms":172.3358710296452},{"cpu_ms":128.25600000000037,"wall_ms":180.5277969688177},{"cpu_ms":129.2429999999989,"wall_ms":134.4161219894886},{"cpu_ms":127.13900000000145,"wall_ms":133.11049179174006},{"cpu_ms":131.99899999999687,"wall_ms":161.2771290820092},{"cpu_ms":129.42300000000273,"wall_ms":178.68668702431023},{"cpu_ms":128.4230000000015,"wall_ms":146.53507503680885},{"cpu_ms":128.69899999999745,"wall_ms":154.18903483077884},{"cpu_ms":144.97300000000024,"wall_ms":150.9598819538951},{"cpu_ms":125.18299999999982,"wall_ms":138.2565579842776},{"cpu_ms":128.81600000000049,"wall_ms":179.08323695883155},{"cpu_ms":130.62299999999993,"wall_ms":144.15096980519593},{"cpu_ms":129.77999999999668,"wall_ms":142.48144207522273},{"cpu_ms":125.077000000001,"wall_ms":130.45984995551407},{"cpu_ms":127.90299999999988,"wall_ms":149.49034713208675},{"cpu_ms":125.11500000000098,"wall_ms":131.44527096301317},{"cpu_ms":129.9279999999996,"wall_ms":135.27785195037723},{"cpu_ms":128.47400000000064,"wall_ms":135.5186419095844},{"cpu_ms":127.6830000000011,"wall_ms":135.66554617136717},{"cpu_ms":131.32699999999886,"wall_ms":138.81608308292925},{"cpu_ms":133.1939999999996,"wall_ms":138.5738600511104},{"cpu_ms":94.41699999999997,"wall_ms":103.96577906794846},{"cpu_ms":94.26699999999855,"wall_ms":99.66611512936652},{"cpu_ms":89.9940000000008,"wall_ms":94.32466514408588},{"cpu_ms":125.65599999999932,"wall_ms":130.01823495142162},{"cpu_ms":104.92299999999943,"wall_ms":110.30600406229496},{"cpu_ms":77.37299999999792,"wall_ms":80.65509516745806},{"cpu_ms":114.02900000000216,"wall_ms":119.3866110406816},{"cpu_ms":124.00399999999934,"wall_ms":129.3993080034852},{"cpu_ms":122.61799999999923,"wall_ms":130.0023349467665},{"cpu_ms":101.66699999999906,"wall_ms":106.02881386876106},{"cpu_ms":135.77199999999934,"wall_ms":142.1368969604373},{"cpu_ms":141.28300000000138,"wall_ms":147.66746084205806},{"cpu_ms":136.15700000000075,"wall_ms":142.84894801676273},{"cpu_ms":125.30499999999734,"wall_ms":131.70501589775085},{"cpu_ms":123.56199999999973,"wall_ms":131.93619810044765},{"cpu_ms":119.19500000000127,"wall_ms":127.15337192639709},{"cpu_ms":129.8080000000006,"wall_ms":133.24892101809382},{"cpu_ms":130.9139999999971,"wall_ms":134.93193592876196},{"cpu_ms":133.31400000000215,"wall_ms":142.30341790243983},{"cpu_ms":139.3979999999999,"wall_ms":171.50248284451663},{"cpu_ms":131.23200000000068,"wall_ms":248.0369401164353},{"cpu_ms":129.4940000000011,"wall_ms":323.76779499463737},{"cpu_ms":123.05600000000183,"wall_ms":288.63781900145113},{"cpu_ms":144.4890000000001,"wall_ms":256.76093995571136},{"cpu_ms":136.848999999998,"wall_ms":229.7267101239413},{"cpu_ms":136.91100000000134,"wall_ms":235.54268688894808},{"cpu_ms":134.91399999999842,"wall_ms":217.73298108018935},{"cpu_ms":141.35900000000134,"wall_ms":158.75706006772816},{"cpu_ms":143.12799999999726,"wall_ms":180.8281580451876}],"after_median_cpu_ms":129.42300000000273}]}, + {"kind":"aa-fixed-batch","rows":[{"name":"batch","node_exit":0,"before":[{"cpu_ms":28.974999999999973,"wall_ms":136.19241095148027},{"cpu_ms":22.931999999999952,"wall_ms":132.8699819277972},{"cpu_ms":23.97899999999997,"wall_ms":124.25282783806324},{"cpu_ms":36.893000000000065,"wall_ms":90.25843488052487},{"cpu_ms":34.11799999999987,"wall_ms":110.81053502857685},{"cpu_ms":27.035000000000142,"wall_ms":103.50989084690809},{"cpu_ms":27.14900000000009,"wall_ms":88.08058500289917},{"cpu_ms":32.84599999999993,"wall_ms":86.67694195173681},{"cpu_ms":42.40300000000019,"wall_ms":107.63710411265492},{"cpu_ms":33.11299999999973,"wall_ms":166.30834504030645},{"cpu_ms":32.29000000000015,"wall_ms":68.00035713240504},{"cpu_ms":39.09699999999994,"wall_ms":142.55355997011065},{"cpu_ms":29.367000000000143,"wall_ms":100.12971400283277},{"cpu_ms":48.71800000000004,"wall_ms":89.95237294584513},{"cpu_ms":38.18699999999975,"wall_ms":111.08460719697177},{"cpu_ms":48.234000000000336,"wall_ms":114.32825890369713},{"cpu_ms":34.21200000000013,"wall_ms":71.83284894563258},{"cpu_ms":53.306999999999775,"wall_ms":142.2914699651301},{"cpu_ms":27.946000000000026,"wall_ms":149.1316631436348},{"cpu_ms":26.889000000000163,"wall_ms":116.89044698141515},{"cpu_ms":30.78499999999984,"wall_ms":117.103316122666},{"cpu_ms":31.994000000000078,"wall_ms":89.3349489197135},{"cpu_ms":33.90399999999971,"wall_ms":113.12493006698787},{"cpu_ms":27.213000000000154,"wall_ms":90.05834092386067},{"cpu_ms":32.31599999999979,"wall_ms":64.9932969827205},{"cpu_ms":28.07000000000004,"wall_ms":183.79475502297282},{"cpu_ms":31.050000000000022,"wall_ms":85.4858048260212},{"cpu_ms":29.820999999999653,"wall_ms":85.79251798801124},{"cpu_ms":33.12999999999988,"wall_ms":107.74435382336378},{"cpu_ms":27.963000000000182,"wall_ms":112.24848311394453},{"cpu_ms":42.19999999999979,"wall_ms":89.80654086917639},{"cpu_ms":33.77400000000019,"wall_ms":101.69534920714796},{"cpu_ms":40.99300000000028,"wall_ms":107.33860591426492},{"cpu_ms":40.66499999999973,"wall_ms":109.46813621558249},{"cpu_ms":30.067999999999984,"wall_ms":89.47809599339962},{"cpu_ms":24.919000000000135,"wall_ms":44.19039795175195},{"cpu_ms":38.92699999999971,"wall_ms":129.07044985331595},{"cpu_ms":24.016000000000037,"wall_ms":137.62467703782022},{"cpu_ms":30.979000000000312,"wall_ms":69.89365490153432},{"cpu_ms":31.118999999999897,"wall_ms":112.89923801086843},{"cpu_ms":43.3400000000006,"wall_ms":94.08286190591753},{"cpu_ms":34.9870000000001,"wall_ms":102.95066516846418},{"cpu_ms":34.16500000000067,"wall_ms":80.67049598321319},{"cpu_ms":35.84199999999971,"wall_ms":75.94808004796505},{"cpu_ms":33.960000000000434,"wall_ms":79.28360300138593},{"cpu_ms":44.329999999999536,"wall_ms":100.08796490728855},{"cpu_ms":31.228000000000478,"wall_ms":92.01306407339871},{"cpu_ms":33.00400000000003,"wall_ms":80.3028589580208},{"cpu_ms":27.131999999999934,"wall_ms":53.60179697163403},{"cpu_ms":25.018000000000207,"wall_ms":98.42574899084866},{"cpu_ms":29.023999999999717,"wall_ms":92.3707929905504},{"cpu_ms":33.07600000000033,"wall_ms":94.51044094748795},{"cpu_ms":25.03399999999978,"wall_ms":67.35354708507657},{"cpu_ms":25.04799999999996,"wall_ms":70.7934859674424},{"cpu_ms":26.00699999999989,"wall_ms":87.27328502573073},{"cpu_ms":23.718999999999824,"wall_ms":58.58854576945305},{"cpu_ms":26.876999999999818,"wall_ms":49.04990014620125},{"cpu_ms":24.46600000000032,"wall_ms":74.89801803603768},{"cpu_ms":22.046000000000454,"wall_ms":65.39914500899613},{"cpu_ms":24.167999999999523,"wall_ms":38.513438077643514},{"cpu_ms":17.992000000000452,"wall_ms":94.24905199557543},{"cpu_ms":26.969999999999494,"wall_ms":64.21160488389432},{"cpu_ms":39.1130000000004,"wall_ms":111.38613917864859},{"cpu_ms":29.0619999999997,"wall_ms":96.77686612121761},{"cpu_ms":26.189000000000462,"wall_ms":95.15105793252587},{"cpu_ms":27.116999999999614,"wall_ms":119.76575991138816},{"cpu_ms":32.81199999999984,"wall_ms":115.73549802415073},{"cpu_ms":24.949000000000332,"wall_ms":123.12895595096052},{"cpu_ms":24.997000000000824,"wall_ms":121.3226499967277},{"cpu_ms":28.545999999999516,"wall_ms":61.3097520545125},{"cpu_ms":23.639000000000188,"wall_ms":27.862813090905547},{"cpu_ms":24.73399999999959,"wall_ms":29.72225798293948},{"cpu_ms":23.468000000000266,"wall_ms":27.80554606579244},{"cpu_ms":25.457999999999537,"wall_ms":27.770860819146037},{"cpu_ms":25.04699999999982,"wall_ms":28.342119185253978},{"cpu_ms":24.58399999999994,"wall_ms":27.806808007881045},{"cpu_ms":25.23799999999987,"wall_ms":28.53410691022873},{"cpu_ms":21.619000000000277,"wall_ms":28.830421157181263},{"cpu_ms":25.005000000000166,"wall_ms":28.281905921176076},{"cpu_ms":25.868000000000002,"wall_ms":28.13463006168604},{"cpu_ms":24.684000000000594,"wall_ms":27.915742015466094},{"cpu_ms":22.70599999999945,"wall_ms":27.912896825000644},{"cpu_ms":24.389999999999468,"wall_ms":27.728192042559385},{"cpu_ms":22.62400000000042,"wall_ms":27.8394790366292},{"cpu_ms":24.486000000000452,"wall_ms":28.840200044214725},{"cpu_ms":25.433999999999735,"wall_ms":27.653361903503537},{"cpu_ms":24.90599999999965,"wall_ms":28.17164990119636},{"cpu_ms":22.687000000000346,"wall_ms":28.93183007836342},{"cpu_ms":25.55200000000024,"wall_ms":28.777892934158444},{"cpu_ms":25.36700000000014,"wall_ms":29.616340063512325},{"cpu_ms":22.667000000000215,"wall_ms":28.907726053148508},{"cpu_ms":22.4479999999998,"wall_ms":27.838887879624963},{"cpu_ms":22.172000000000303,"wall_ms":27.483483077958226},{"cpu_ms":22.58899999999997,"wall_ms":27.766734128817916},{"cpu_ms":25.271000000000043,"wall_ms":27.58167707361281},{"cpu_ms":24.535999999999447,"wall_ms":27.89306896738708},{"cpu_ms":23.398000000000252,"wall_ms":27.766282204538584},{"cpu_ms":26.536000000000115,"wall_ms":27.835913002490997},{"cpu_ms":26.360999999999635,"wall_ms":27.64804195612669},{"cpu_ms":25.37300000000009,"wall_ms":27.664262102916837},{"cpu_ms":26.740000000000208,"wall_ms":31.067491974681616}],"before_median_cpu_ms":27.035000000000142,"after":[{"cpu_ms":27.891000000000112,"wall_ms":116.08142615295947},{"cpu_ms":26.79699999999996,"wall_ms":72.82655709423125},{"cpu_ms":29.962999999999962,"wall_ms":90.22095496766269},{"cpu_ms":34.65600000000002,"wall_ms":83.48729112185538},{"cpu_ms":28.810000000000002,"wall_ms":128.68365901522338},{"cpu_ms":27.810999999999808,"wall_ms":91.72108885832131},{"cpu_ms":22.244000000000153,"wall_ms":59.94309904053807},{"cpu_ms":27.964000000000098,"wall_ms":64.28536400198936},{"cpu_ms":29.945999999999806,"wall_ms":116.88551912084222},{"cpu_ms":38.24899999999998,"wall_ms":155.71640990674496},{"cpu_ms":31.865000000000254,"wall_ms":128.66280996240675},{"cpu_ms":45.094999999999885,"wall_ms":114.66600093990564},{"cpu_ms":23.9189999999998,"wall_ms":79.1020451579243},{"cpu_ms":24.16000000000018,"wall_ms":66.9840881600976},{"cpu_ms":36.00199999999987,"wall_ms":81.42951410263777},{"cpu_ms":38.04699999999972,"wall_ms":127.66230897977948},{"cpu_ms":29.046000000000127,"wall_ms":110.55310303345323},{"cpu_ms":31.932000000000293,"wall_ms":76.20954886078835},{"cpu_ms":47.868999999999495,"wall_ms":114.06452697701752},{"cpu_ms":36.75499999999987,"wall_ms":103.3975810278207},{"cpu_ms":30.96000000000032,"wall_ms":108.215605141595},{"cpu_ms":35.813000000000095,"wall_ms":99.78767391294241},{"cpu_ms":27.85599999999988,"wall_ms":78.11477896757424},{"cpu_ms":29.93300000000021,"wall_ms":171.43916385248303},{"cpu_ms":27.805999999999997,"wall_ms":100.69973906502128},{"cpu_ms":31.824000000000296,"wall_ms":91.78580902516842},{"cpu_ms":32.27899999999995,"wall_ms":75.95953205600381},{"cpu_ms":33.88800000000014,"wall_ms":94.12350808270276},{"cpu_ms":32.78900000000018,"wall_ms":56.93205096758902},{"cpu_ms":25.97000000000005,"wall_ms":118.32115100696683},{"cpu_ms":44.48099999999977,"wall_ms":79.39667510800064},{"cpu_ms":33.028999999999975,"wall_ms":87.4497548211366},{"cpu_ms":44.500999999999905,"wall_ms":83.28250795602798},{"cpu_ms":32.6970000000002,"wall_ms":82.69340614788234},{"cpu_ms":36.34799999999983,"wall_ms":67.95462081208825},{"cpu_ms":28.993999999999964,"wall_ms":125.69108512252569},{"cpu_ms":34.899000000000235,"wall_ms":88.08482182212174},{"cpu_ms":23.102999999999874,"wall_ms":100.85627087391913},{"cpu_ms":42.478000000000016,"wall_ms":99.78731302544475},{"cpu_ms":36.05100000000006,"wall_ms":90.53545189090073},{"cpu_ms":46.09599999999947,"wall_ms":99.47464894503355},{"cpu_ms":37.26099999999999,"wall_ms":72.25977699272335},{"cpu_ms":38.107999999999365,"wall_ms":85.62682894989848},{"cpu_ms":35.08099999999992,"wall_ms":76.53979491442442},{"cpu_ms":46.98799999999981,"wall_ms":108.88908291235566},{"cpu_ms":34.895000000000564,"wall_ms":120.92818296514452},{"cpu_ms":30.229999999999535,"wall_ms":90.00614192336798},{"cpu_ms":22.605999999999682,"wall_ms":58.35597193799913},{"cpu_ms":31.778000000000084,"wall_ms":119.04466291889548},{"cpu_ms":34.91800000000023,"wall_ms":66.18897011503577},{"cpu_ms":39.27399999999981,"wall_ms":63.81093617528677},{"cpu_ms":29.965999999999937,"wall_ms":84.17029702104628},{"cpu_ms":28.878999999999877,"wall_ms":69.00101690553129},{"cpu_ms":26.057999999999915,"wall_ms":60.446699149906635},{"cpu_ms":25.97100000000019,"wall_ms":86.21774311177433},{"cpu_ms":28.573999999999877,"wall_ms":40.664378087967634},{"cpu_ms":28.051000000000492,"wall_ms":41.39541392214596},{"cpu_ms":22.940000000000182,"wall_ms":89.03417596593499},{"cpu_ms":28.061999999999365,"wall_ms":117.57631902582943},{"cpu_ms":21.94099999999999,"wall_ms":55.100767873227596},{"cpu_ms":27.84000000000031,"wall_ms":114.70471206121147},{"cpu_ms":25.782999999999667,"wall_ms":52.90549504570663},{"cpu_ms":30.02600000000033,"wall_ms":105.36926495842636},{"cpu_ms":37.047999999999526,"wall_ms":107.62975900433958},{"cpu_ms":31.023000000000245,"wall_ms":173.57881204225123},{"cpu_ms":28.099000000000096,"wall_ms":196.98824104852974},{"cpu_ms":28.099000000000096,"wall_ms":80.57146007195115},{"cpu_ms":29.891999999999364,"wall_ms":144.1436898894608},{"cpu_ms":23.751999999999995,"wall_ms":65.60285505838692},{"cpu_ms":21.77899999999955,"wall_ms":72.59735721163452},{"cpu_ms":22.75900000000064,"wall_ms":29.019454028457403},{"cpu_ms":22.501000000000104,"wall_ms":27.65237004496157},{"cpu_ms":25.405000000000122,"wall_ms":28.746393974870443},{"cpu_ms":24.42299999999964,"wall_ms":27.742568869143724},{"cpu_ms":22.658000000000733,"wall_ms":27.856780914589763},{"cpu_ms":25.879999999999903,"wall_ms":28.1869990285486},{"cpu_ms":27.328999999999937,"wall_ms":28.619406977668405},{"cpu_ms":26.475999999999722,"wall_ms":27.79380395077169},{"cpu_ms":24.32000000000034,"wall_ms":28.69131090119481},{"cpu_ms":23.4829999999997,"wall_ms":27.85668196156621},{"cpu_ms":24.505999999999695,"wall_ms":27.84417779184878},{"cpu_ms":25.447999999999915,"wall_ms":27.79643889516592},{"cpu_ms":24.372000000000504,"wall_ms":27.667428134009242},{"cpu_ms":25.581999999999994,"wall_ms":27.89742685854435},{"cpu_ms":24.466999999999572,"wall_ms":27.803753037005663},{"cpu_ms":24.2899999999997,"wall_ms":27.596866013482213},{"cpu_ms":25.25500000000047,"wall_ms":30.64956096932292},{"cpu_ms":25.777999999999857,"wall_ms":28.037719894200563},{"cpu_ms":26.61699999999989,"wall_ms":28.955725952982903},{"cpu_ms":25.770999999999766,"wall_ms":28.029173146933317},{"cpu_ms":21.48400000000006,"wall_ms":28.64892198704183},{"cpu_ms":23.735999999999535,"wall_ms":27.95789111405611},{"cpu_ms":24.59400000000045,"wall_ms":27.95313112437725},{"cpu_ms":24.217999999999407,"wall_ms":27.510393876582384},{"cpu_ms":25.446000000000524,"wall_ms":27.74672582745552},{"cpu_ms":25.2340000000002,"wall_ms":27.508860919624567},{"cpu_ms":23.693999999999882,"wall_ms":27.932193130254745},{"cpu_ms":23.606000000000016,"wall_ms":27.8073288500309},{"cpu_ms":23.709000000000202,"wall_ms":27.92843501083553},{"cpu_ms":21.622999999999948,"wall_ms":27.780529111623764},{"cpu_ms":26.832999999999885,"wall_ms":28.188832104206085}],"after_median_cpu_ms":27.964000000000098}]}, + {"kind":"app-fixed-batch","rows":[{"name":"batch","node_exit":0,"before":[{"cpu_ms":47.22800000000005,"wall_ms":52.64248396269977},{"cpu_ms":47.05000000000004,"wall_ms":52.375194150954485},{"cpu_ms":45.27599999999987,"wall_ms":52.69634397700429},{"cpu_ms":33.977000000000146,"wall_ms":39.37081014737487},{"cpu_ms":33.60099999999999,"wall_ms":35.90181586332619},{"cpu_ms":48.352000000000395,"wall_ms":51.74062820151448},{"cpu_ms":36.03299999999976,"wall_ms":40.38612707518041},{"cpu_ms":47.433999999999976,"wall_ms":54.828878957778215},{"cpu_ms":30.96399999999999,"wall_ms":37.297846050933},{"cpu_ms":32.906000000000105,"wall_ms":39.409622084349394},{"cpu_ms":26.521999999999935,"wall_ms":29.90768407471478},{"cpu_ms":35.09800000000007,"wall_ms":36.47803398780525},{"cpu_ms":25.860000000000216,"wall_ms":28.121966868638992},{"cpu_ms":27.52299999999952,"wall_ms":31.75863390788436},{"cpu_ms":33.776999999999724,"wall_ms":36.13798809237778},{"cpu_ms":24.944000000000077,"wall_ms":30.30870296061039},{"cpu_ms":26.718999999999937,"wall_ms":33.029818907380104},{"cpu_ms":24.806000000000328,"wall_ms":28.047987958416343},{"cpu_ms":32.9240000000004,"wall_ms":38.27455104328692},{"cpu_ms":25.526999999999855,"wall_ms":28.740182053297758},{"cpu_ms":25.61599999999986,"wall_ms":30.924223829060793},{"cpu_ms":24.57799999999999,"wall_ms":34.79894599877298},{"cpu_ms":30.066000000000148,"wall_ms":37.088403943926096},{"cpu_ms":26.12799999999993,"wall_ms":32.828803872689605},{"cpu_ms":24.304999999999577,"wall_ms":31.020574970170856},{"cpu_ms":24.91100000000035,"wall_ms":29.1660581715405},{"cpu_ms":32.49400000000024,"wall_ms":40.63506284728646},{"cpu_ms":26.98299999999998,"wall_ms":30.674728099256754},{"cpu_ms":23.209000000000035,"wall_ms":28.520883060991764},{"cpu_ms":25.00100000000005,"wall_ms":30.35419899970293},{"cpu_ms":25.036000000000058,"wall_ms":28.324174927547574},{"cpu_ms":27.0640000000002,"wall_ms":30.384105164557695},{"cpu_ms":24.51099999999995,"wall_ms":29.699845938012004},{"cpu_ms":35.16599999999981,"wall_ms":39.46818201802671},{"cpu_ms":44.42700000000022,"wall_ms":47.84156009554863},{"cpu_ms":28.53099999999964,"wall_ms":32.83766005188227},{"cpu_ms":27.476000000000056,"wall_ms":28.785597998648882},{"cpu_ms":26.21799999999963,"wall_ms":29.53998721204698},{"cpu_ms":25.924999999999976,"wall_ms":29.2539419606328},{"cpu_ms":24.43099999999987,"wall_ms":27.788382954895496},{"cpu_ms":25.097999999999843,"wall_ms":29.40508513711393},{"cpu_ms":26.329999999999742,"wall_ms":28.63544598221779},{"cpu_ms":22.780000000000022,"wall_ms":28.023062041029334},{"cpu_ms":29.944999999999666,"wall_ms":33.28923392109573},{"cpu_ms":26.493000000000322,"wall_ms":28.816795209422708},{"cpu_ms":25.127999999999595,"wall_ms":29.27854796871543},{"cpu_ms":38.006000000000206,"wall_ms":43.38676622137427},{"cpu_ms":42.99199999999992,"wall_ms":49.474552972242236},{"cpu_ms":46.816000000000635,"wall_ms":51.13494605757296},{"cpu_ms":34.62799999999966,"wall_ms":41.99486318975687},{"cpu_ms":34.06299999999973,"wall_ms":41.40201606787741},{"cpu_ms":25.724000000000302,"wall_ms":31.95086307823658},{"cpu_ms":26.348999999999734,"wall_ms":30.696138041093946},{"cpu_ms":28.757000000000588,"wall_ms":31.038057059049606},{"cpu_ms":46.04699999999973,"wall_ms":54.31951815262437},{"cpu_ms":46.85399999999973,"wall_ms":50.23732781410217},{"cpu_ms":43.26600000000003,"wall_ms":49.524025060236454},{"cpu_ms":43.28600000000016,"wall_ms":51.58083909191191},{"cpu_ms":48.08199999999996,"wall_ms":51.49114178493619},{"cpu_ms":46.24199999999945,"wall_ms":49.61725906468928},{"cpu_ms":45.850999999999864,"wall_ms":49.21290394850075},{"cpu_ms":30.249000000000414,"wall_ms":31.544323079288006},{"cpu_ms":45.729999999999826,"wall_ms":53.54972812347114},{"cpu_ms":37.56299999999957,"wall_ms":41.88225301913917},{"cpu_ms":42.10099999999972,"wall_ms":50.49616121686995},{"cpu_ms":45.96400000000056,"wall_ms":52.3810051381588},{"cpu_ms":51.21999999999982,"wall_ms":53.57914394699037},{"cpu_ms":48.72399999999999,"wall_ms":53.07685490697622},{"cpu_ms":50.170999999999744,"wall_ms":54.66858996078372},{"cpu_ms":49.722000000000044,"wall_ms":56.06408813036978},{"cpu_ms":53.42700000000011,"wall_ms":58.8519680313766},{"cpu_ms":50.71799999999982,"wall_ms":56.10441300086677},{"cpu_ms":49.98600000000053,"wall_ms":54.382544942200184},{"cpu_ms":47.14200000000002,"wall_ms":271.22216508723795},{"cpu_ms":46.89000000000032,"wall_ms":55.24115893058479},{"cpu_ms":50.345999999999336,"wall_ms":59.78465103544295},{"cpu_ms":42.23299999999952,"wall_ms":187.4330318532884},{"cpu_ms":54.09299999999995,"wall_ms":189.68807603232563},{"cpu_ms":56.0179999999999,"wall_ms":132.58296297863126},{"cpu_ms":47.242999999999924,"wall_ms":92.86933206021786},{"cpu_ms":51.92599999999992,"wall_ms":141.1801299545914},{"cpu_ms":53.893999999999664,"wall_ms":146.38712094165385},{"cpu_ms":41.90000000000005,"wall_ms":102.10690600797534},{"cpu_ms":48.88899999999996,"wall_ms":192.7685730624944},{"cpu_ms":39.085000000000036,"wall_ms":112.75694100186229},{"cpu_ms":56.0470000000004,"wall_ms":112.17610514722764},{"cpu_ms":29.929000000000094,"wall_ms":87.204315001145},{"cpu_ms":35.1549999999996,"wall_ms":99.7780729085207},{"cpu_ms":53.00599999999989,"wall_ms":255.42394816875458},{"cpu_ms":49.099000000000004,"wall_ms":206.83137490414083},{"cpu_ms":27.281000000000333,"wall_ms":87.82226918265224},{"cpu_ms":31.85599999999944,"wall_ms":81.99685299769044},{"cpu_ms":25.952000000000197,"wall_ms":82.75782386772335},{"cpu_ms":27.919999999999945,"wall_ms":82.56833092309535},{"cpu_ms":37.66400000000125,"wall_ms":78.60789890401065},{"cpu_ms":30.363999999998725,"wall_ms":69.10138297826052},{"cpu_ms":27.12600000000087,"wall_ms":67.59650004096329},{"cpu_ms":27.286999999999395,"wall_ms":30.713741201907396},{"cpu_ms":47.80599999999957,"wall_ms":57.31065711006522},{"cpu_ms":24.827999999999406,"wall_ms":32.057351898401976},{"cpu_ms":29.379000000000488,"wall_ms":66.22108002193272}],"before_median_cpu_ms":33.977000000000146,"after":[{"cpu_ms":35.37799999999991,"wall_ms":39.66201515868306},{"cpu_ms":47.547000000000004,"wall_ms":52.93427919968963},{"cpu_ms":45.21200000000003,"wall_ms":51.59276211634278},{"cpu_ms":48.31699999999994,"wall_ms":50.69424188695848},{"cpu_ms":27.880999999999823,"wall_ms":32.16408099979162},{"cpu_ms":41.2969999999997,"wall_ms":48.72093489393592},{"cpu_ms":26.6390000000003,"wall_ms":30.001790961250663},{"cpu_ms":29.498000000000246,"wall_ms":31.828525010496378},{"cpu_ms":26.098999999999872,"wall_ms":29.409512877464294},{"cpu_ms":28.254,"wall_ms":32.54286898300052},{"cpu_ms":31.085999999999725,"wall_ms":35.47067102044821},{"cpu_ms":30.479000000000145,"wall_ms":42.99891018308699},{"cpu_ms":25.263999999999953,"wall_ms":27.64565614052117},{"cpu_ms":32.96600000000005,"wall_ms":39.26935093477368},{"cpu_ms":25.38100000000032,"wall_ms":30.00216092914343},{"cpu_ms":23.638000000000048,"wall_ms":28.834037948399782},{"cpu_ms":26.52499999999991,"wall_ms":28.938543051481247},{"cpu_ms":25.427999999999784,"wall_ms":32.15323085896671},{"cpu_ms":23.842999999999837,"wall_ms":29.092280892655253},{"cpu_ms":31.07799999999994,"wall_ms":36.12185805104673},{"cpu_ms":28.30200000000005,"wall_ms":31.691408017650247},{"cpu_ms":24.27500000000027,"wall_ms":29.483410995453596},{"cpu_ms":23.781999999999748,"wall_ms":30.904167098924518},{"cpu_ms":25.894000000000084,"wall_ms":30.332988826557994},{"cpu_ms":25.107000000000212,"wall_ms":28.4063289873302},{"cpu_ms":24.91399999999988,"wall_ms":29.199039097875357},{"cpu_ms":25.860999999999912,"wall_ms":29.13097315467894},{"cpu_ms":24.801000000000073,"wall_ms":30.709021957591176},{"cpu_ms":23.996999999999602,"wall_ms":29.35759606771171},{"cpu_ms":28.246000000000215,"wall_ms":32.44602796621621},{"cpu_ms":25.58299999999969,"wall_ms":28.918656054884195},{"cpu_ms":26.29999999999999,"wall_ms":29.59961793385446},{"cpu_ms":26.53900000000009,"wall_ms":29.8799320589751},{"cpu_ms":24.869999999999948,"wall_ms":28.133507817983627},{"cpu_ms":32.7980000000001,"wall_ms":37.28757705539465},{"cpu_ms":27.134000000000214,"wall_ms":32.31838997453451},{"cpu_ms":25.366,"wall_ms":28.696370078250766},{"cpu_ms":26.489000000000207,"wall_ms":27.78900391422212},{"cpu_ms":26.06300000000017,"wall_ms":29.36214511282742},{"cpu_ms":26.797999999999877,"wall_ms":28.10597699135542},{"cpu_ms":22.18100000000023,"wall_ms":27.50939200632274},{"cpu_ms":25.37300000000009,"wall_ms":33.423195127397776},{"cpu_ms":24.103000000000208,"wall_ms":29.478922951966524},{"cpu_ms":33.55900000000034,"wall_ms":39.12329790182412},{"cpu_ms":41.085999999999956,"wall_ms":48.47486410290003},{"cpu_ms":26.202999999999754,"wall_ms":31.60379477776587},{"cpu_ms":26.546000000000625,"wall_ms":30.936185969039798},{"cpu_ms":43.59099999999927,"wall_ms":47.98876587301493},{"cpu_ms":44.72699999999996,"wall_ms":50.112545024603605},{"cpu_ms":43.90800000000006,"wall_ms":47.227514209225774},{"cpu_ms":44.99000000000031,"wall_ms":50.27914606034756},{"cpu_ms":33.05500000000006,"wall_ms":36.361857084557414},{"cpu_ms":44.75099999999976,"wall_ms":50.14173919335008},{"cpu_ms":25.8389999999995,"wall_ms":30.117345973849297},{"cpu_ms":28.114000000000416,"wall_ms":31.399191124364734},{"cpu_ms":46.816000000000635,"wall_ms":51.18796485476196},{"cpu_ms":42.97099999999965,"wall_ms":47.34319890849292},{"cpu_ms":29.922000000000004,"wall_ms":35.30513099394739},{"cpu_ms":28.999999999999915,"wall_ms":36.39905620366335},{"cpu_ms":47.279000000000515,"wall_ms":51.58103909343481},{"cpu_ms":31.23100000000001,"wall_ms":35.54578102193773},{"cpu_ms":45.26899999999934,"wall_ms":52.52985283732414},{"cpu_ms":45.52600000000062,"wall_ms":51.82514595799148},{"cpu_ms":46.08900000000027,"wall_ms":51.49714183062315},{"cpu_ms":43.48200000000002,"wall_ms":47.89068200625479},{"cpu_ms":42.86999999999974,"wall_ms":50.219764932990074},{"cpu_ms":48.27300000000001,"wall_ms":50.64099305309355},{"cpu_ms":47.80699999999971,"wall_ms":52.180238999426365},{"cpu_ms":46.337000000000295,"wall_ms":53.68454102426767},{"cpu_ms":53.2010000000005,"wall_ms":57.66347702592611},{"cpu_ms":50.340999999999525,"wall_ms":55.67449005320668},{"cpu_ms":51.640000000000796,"wall_ms":57.00113903731108},{"cpu_ms":49.63499999999943,"wall_ms":54.09014807082713},{"cpu_ms":31.71599999999941,"wall_ms":126.24889612197876},{"cpu_ms":53.37000000000014,"wall_ms":60.81615900620818},{"cpu_ms":56.00100000000019,"wall_ms":76.39393210411072},{"cpu_ms":52.998000000000545,"wall_ms":198.41675902716815},{"cpu_ms":56.0590000000003,"wall_ms":365.8157701138407},{"cpu_ms":47.01299999999975,"wall_ms":130.3804770577699},{"cpu_ms":57.3750000000004,"wall_ms":125.73739909566939},{"cpu_ms":53.66300000000024,"wall_ms":130.98537689074874},{"cpu_ms":51.17800000000017,"wall_ms":105.67225003615022},{"cpu_ms":32.246999999999915,"wall_ms":80.93069982714951},{"cpu_ms":33.98699999999977,"wall_ms":94.34941806830466},{"cpu_ms":37.974000000000174,"wall_ms":178.57523704878986},{"cpu_ms":30.257999999999896,"wall_ms":93.43827585689723},{"cpu_ms":33.92499999999998,"wall_ms":115.0861019268632},{"cpu_ms":48.974000000000295,"wall_ms":106.31398903205991},{"cpu_ms":49.37500000000039,"wall_ms":81.95090712979436},{"cpu_ms":24.687000000000126,"wall_ms":57.66744399443269},{"cpu_ms":32.11899999999979,"wall_ms":94.414581079036},{"cpu_ms":31.788000000000594,"wall_ms":87.78618206270039},{"cpu_ms":32.95099999999884,"wall_ms":88.23901903815567},{"cpu_ms":30.99000000000096,"wall_ms":64.26869099959731},{"cpu_ms":32.55699999999884,"wall_ms":68.41801502741873},{"cpu_ms":32.06500000000112,"wall_ms":69.42928512580693},{"cpu_ms":26.7489999999988,"wall_ms":55.770618841052055},{"cpu_ms":23.761999999999617,"wall_ms":32.94189483858645},{"cpu_ms":24.859000000001075,"wall_ms":47.99256194382906},{"cpu_ms":29.55799999999975,"wall_ms":39.05920684337616},{"cpu_ms":28.538000000001063,"wall_ms":38.110895082354546}],"after_median_cpu_ms":31.23100000000001}]}, + {"kind":"aa-fixed-promise_all_chains","rows":[{"name":"promise_all_chains","node_exit":0,"before":[{"cpu_ms":103.769,"wall_ms":133.46390891820192},{"cpu_ms":103.19999999999996,"wall_ms":116.5084708482027},{"cpu_ms":96.06000000000003,"wall_ms":103.35046984255314},{"cpu_ms":90.56599999999992,"wall_ms":137.62192893773317},{"cpu_ms":115.85299999999998,"wall_ms":124.19605790637434},{"cpu_ms":93.51999999999983,"wall_ms":97.78267401270568},{"cpu_ms":94.13699999999992,"wall_ms":118.45818418078125},{"cpu_ms":100.11800000000015,"wall_ms":122.78663390316069},{"cpu_ms":107.78100000000012,"wall_ms":128.06319491937757},{"cpu_ms":93.93300000000026,"wall_ms":211.16349007934332},{"cpu_ms":174.39099999999996,"wall_ms":241.8832208495587},{"cpu_ms":136.12599999999998,"wall_ms":228.85446203872561},{"cpu_ms":148.55600000000013,"wall_ms":302.45712702162564},{"cpu_ms":96.0030000000005,"wall_ms":105.30962096527219},{"cpu_ms":165.43500000000046,"wall_ms":196.90612400881946},{"cpu_ms":162.28199999999936,"wall_ms":187.61872709728777},{"cpu_ms":106.57300000000004,"wall_ms":124.8548780567944},{"cpu_ms":136.62499999999955,"wall_ms":229.69445097260177},{"cpu_ms":162.38900000000012,"wall_ms":165.77241895720363},{"cpu_ms":158.49799999999982,"wall_ms":163.84570789523423},{"cpu_ms":156.1950000000003,"wall_ms":162.5636420212686},{"cpu_ms":154.94200000000012,"wall_ms":167.29046497493982},{"cpu_ms":178.40200000000016,"wall_ms":202.85657397471368},{"cpu_ms":112.27699999999973,"wall_ms":145.48375108279288},{"cpu_ms":164.9119999999993,"wall_ms":180.264581926167},{"cpu_ms":177.0720000000008,"wall_ms":260.4649569839239},{"cpu_ms":126.41099999999916,"wall_ms":226.1664168909192},{"cpu_ms":136.67899999999912,"wall_ms":224.87636702135205},{"cpu_ms":135.15699999999953,"wall_ms":250.67084305919707},{"cpu_ms":109.25500000000099,"wall_ms":228.6315441597253},{"cpu_ms":103.13999999999979,"wall_ms":178.5811660811305},{"cpu_ms":108.64200000000146,"wall_ms":138.09021282941103},{"cpu_ms":123.65199999999987,"wall_ms":147.90695998817682},{"cpu_ms":107.64299999999949,"wall_ms":183.73721186071634},{"cpu_ms":100.92600000000118,"wall_ms":129.20215306803584},{"cpu_ms":100.6229999999988,"wall_ms":304.5890990179032},{"cpu_ms":117.88099999999879,"wall_ms":343.0730798281729},{"cpu_ms":155.06799999999998,"wall_ms":281.17064316757023},{"cpu_ms":179.8099999999998,"wall_ms":226.87507094815373},{"cpu_ms":164.15699999999944,"wall_ms":239.60194597020745},{"cpu_ms":195.52100000000115,"wall_ms":247.82905098982155},{"cpu_ms":179.85999999999967,"wall_ms":204.2588139884174},{"cpu_ms":152.8620000000007,"wall_ms":261.7081589996815},{"cpu_ms":104.73999999999961,"wall_ms":129.2102278675884},{"cpu_ms":163.43400000000054,"wall_ms":250.04520290531218},{"cpu_ms":164.57199999999972,"wall_ms":210.97058709710836},{"cpu_ms":115.03399999999964,"wall_ms":202.41908496245742},{"cpu_ms":160.27599999999964,"wall_ms":230.84243410266936},{"cpu_ms":117.92599999999887,"wall_ms":211.3271129783243},{"cpu_ms":122.03000000000053,"wall_ms":276.9111308734864},{"cpu_ms":99.86600000000045,"wall_ms":133.15696315839887},{"cpu_ms":91.04499999999938,"wall_ms":152.38988702185452},{"cpu_ms":109.99100000000084,"wall_ms":170.32622708939016},{"cpu_ms":100.71099999999866,"wall_ms":192.76639493182302},{"cpu_ms":98.51800000000033,"wall_ms":181.50950712151825},{"cpu_ms":102.5229999999997,"wall_ms":182.3374640662223},{"cpu_ms":89.00200000000069,"wall_ms":113.31129400059581},{"cpu_ms":98.8970000000009,"wall_ms":175.10803299956024},{"cpu_ms":162.07299999999947,"wall_ms":183.45884094014764},{"cpu_ms":146.92799999999906,"wall_ms":244.96079911477864},{"cpu_ms":97.97100000000114,"wall_ms":149.05180782079697},{"cpu_ms":185.49300000000102,"wall_ms":224.02335982769728},{"cpu_ms":187.53399999999942,"wall_ms":305.3150731138885},{"cpu_ms":187.59499999999818,"wall_ms":281.3124950043857},{"cpu_ms":174.64100000000116,"wall_ms":302.94823995791376},{"cpu_ms":136.45400000000052,"wall_ms":328.9261581376195},{"cpu_ms":109.77899999999963,"wall_ms":223.33040391094983},{"cpu_ms":176.23000000000033,"wall_ms":514.1446059569716},{"cpu_ms":115.88899999999924,"wall_ms":167.59072407148778},{"cpu_ms":146.87199999999834,"wall_ms":414.12189812399447},{"cpu_ms":115.35700000000304,"wall_ms":235.34544906578958},{"cpu_ms":136.35299999999972,"wall_ms":217.05022710375488},{"cpu_ms":117.91900000000055,"wall_ms":162.2385720256716},{"cpu_ms":100.35699999999892,"wall_ms":160.276043927297},{"cpu_ms":102.71600000000092,"wall_ms":156.6812749952078},{"cpu_ms":137.8459999999997,"wall_ms":185.1614119950682},{"cpu_ms":168.85199999999756,"wall_ms":255.09817292913795},{"cpu_ms":97.63700000000242,"wall_ms":153.8025951012969},{"cpu_ms":107.96000000000205,"wall_ms":172.294985037297},{"cpu_ms":201.17899999999977,"wall_ms":302.61771101504564},{"cpu_ms":187.5409999999995,"wall_ms":308.6877539753914},{"cpu_ms":169.76500000000172,"wall_ms":229.07956806011498},{"cpu_ms":173.90499999999776,"wall_ms":228.28642488457263},{"cpu_ms":185.69100000000205,"wall_ms":285.90102796442807},{"cpu_ms":169.73199999999977,"wall_ms":219.20361113734543},{"cpu_ms":171.32399999999848,"wall_ms":196.6147250495851},{"cpu_ms":98.28499999999707,"wall_ms":115.65063497982919},{"cpu_ms":94.95300000000029,"wall_ms":114.28527301177382},{"cpu_ms":88.77500000000182,"wall_ms":96.03648888878524},{"cpu_ms":88.4079999999976,"wall_ms":100.62186815775931},{"cpu_ms":96.79300000000168,"wall_ms":170.98229099065065},{"cpu_ms":96.17999999999682,"wall_ms":190.72554889135063},{"cpu_ms":88.72600000000119,"wall_ms":96.07068309560418},{"cpu_ms":87.48499999999737,"wall_ms":92.81078982166946},{"cpu_ms":93.99600000000063,"wall_ms":101.68380197137594},{"cpu_ms":119.09700000000001,"wall_ms":124.46126993745565},{"cpu_ms":174.3799999999993,"wall_ms":186.8218630552292},{"cpu_ms":183.42300000000122,"wall_ms":190.83684706129134},{"cpu_ms":169.29400000000072,"wall_ms":174.70030998811126},{"cpu_ms":170.70899999999867,"wall_ms":180.10015110485256},{"cpu_ms":160.58599999999856,"wall_ms":168.94940193742514}],"before_median_cpu_ms":122.03000000000053,"after":[{"cpu_ms":112.76099999999988,"wall_ms":151.0155510623008},{"cpu_ms":93.13100000000007,"wall_ms":108.28037303872406},{"cpu_ms":88.22900000000011,"wall_ms":104.57920609042048},{"cpu_ms":93.19199999999972,"wall_ms":109.49671687558293},{"cpu_ms":97.67300000000034,"wall_ms":104.04691100120544},{"cpu_ms":92.7389999999999,"wall_ms":99.01358396746218},{"cpu_ms":93.90900000000002,"wall_ms":230.06975301541388},{"cpu_ms":97.4029999999999,"wall_ms":119.38380403444171},{"cpu_ms":95.76999999999991,"wall_ms":147.95024110935628},{"cpu_ms":162.90899999999996,"wall_ms":425.1577400136739},{"cpu_ms":113.06799999999973,"wall_ms":228.50899607874453},{"cpu_ms":170.6289999999999,"wall_ms":197.90813699364662},{"cpu_ms":99.98299999999993,"wall_ms":164.3382189795375},{"cpu_ms":90.22599999999947,"wall_ms":95.51827213726938},{"cpu_ms":167.86600000000007,"wall_ms":222.28611609898508},{"cpu_ms":166.099,"wall_ms":183.48341807723045},{"cpu_ms":114.15100000000055,"wall_ms":268.6687191016972},{"cpu_ms":161.21600000000046,"wall_ms":174.58953708410263},{"cpu_ms":130.62799999999976,"wall_ms":192.5364867784083},{"cpu_ms":158.7890000000005,"wall_ms":166.13758099265397},{"cpu_ms":157.95499999999942,"wall_ms":165.69106606766582},{"cpu_ms":146.02700000000013,"wall_ms":209.38938297331333},{"cpu_ms":164.51499999999976,"wall_ms":198.030716041103},{"cpu_ms":145.721,"wall_ms":177.01018997468054},{"cpu_ms":108.4430000000003,"wall_ms":126.84766296297312},{"cpu_ms":152.393,"wall_ms":175.90066697448492},{"cpu_ms":118.24900000000049,"wall_ms":132.66391213983297},{"cpu_ms":108.53100000000104,"wall_ms":191.48096512071788},{"cpu_ms":178.33100000000002,"wall_ms":208.82676192559302},{"cpu_ms":138.59899999999925,"wall_ms":216.3827419281006},{"cpu_ms":153.13199999999938,"wall_ms":273.3631220180541},{"cpu_ms":94.2779999999992,"wall_ms":110.64969096332788},{"cpu_ms":97.40400000000093,"wall_ms":116.61410704255104},{"cpu_ms":95.79300000000046,"wall_ms":241.95518414489925},{"cpu_ms":92.24899999999892,"wall_ms":108.40535606257617},{"cpu_ms":154.42400000000055,"wall_ms":321.2602350395173},{"cpu_ms":161.26900000000077,"wall_ms":323.88895796611905},{"cpu_ms":188.77600000000072,"wall_ms":287.1137890033424},{"cpu_ms":139.85800000000026,"wall_ms":211.2355730496347},{"cpu_ms":133.4560000000007,"wall_ms":245.3762770164758},{"cpu_ms":95.85399999999922,"wall_ms":106.29713581874967},{"cpu_ms":156.83499999999918,"wall_ms":203.2089619897306},{"cpu_ms":156.49900000000017,"wall_ms":227.3160549812019},{"cpu_ms":184.74099999999893,"wall_ms":288.9928910881281},{"cpu_ms":138.96700000000095,"wall_ms":185.29961095191538},{"cpu_ms":173.449999999999,"wall_ms":221.9152101315558},{"cpu_ms":124.9910000000014,"wall_ms":208.62985216081142},{"cpu_ms":122.42099999999922,"wall_ms":180.05371699109674},{"cpu_ms":184.27300000000102,"wall_ms":299.0481921005994},{"cpu_ms":159.08100000000047,"wall_ms":208.43331492505968},{"cpu_ms":93.94499999999972,"wall_ms":185.23928802460432},{"cpu_ms":98.86099999999942,"wall_ms":152.42103510536253},{"cpu_ms":112.49300000000062,"wall_ms":152.0098769105971},{"cpu_ms":100.26500000000027,"wall_ms":234.77783892303705},{"cpu_ms":91.781000000001,"wall_ms":144.05084191821516},{"cpu_ms":101.07399999999878,"wall_ms":121.43789208494127},{"cpu_ms":108.07499999999948,"wall_ms":197.49120506457984},{"cpu_ms":106.621999999998,"wall_ms":117.87410103715956},{"cpu_ms":105.6580000000018,"wall_ms":149.25739192403853},{"cpu_ms":157.91800000000222,"wall_ms":181.25884886831045},{"cpu_ms":114.72299999999791,"wall_ms":225.86184600368142},{"cpu_ms":126.99199999999777,"wall_ms":221.39958711341023},{"cpu_ms":191.05200000000266,"wall_ms":274.56387295387685},{"cpu_ms":114.10800000000165,"wall_ms":172.38623602315784},{"cpu_ms":164.44699999999912,"wall_ms":263.1570037920028},{"cpu_ms":160.99299999999772,"wall_ms":207.441960927099},{"cpu_ms":130.2330000000005,"wall_ms":172.1504759043455},{"cpu_ms":131.1889999999991,"wall_ms":247.73432197980583},{"cpu_ms":144.87500000000253,"wall_ms":296.1002809461206},{"cpu_ms":173.83400000000293,"wall_ms":259.1291479766369},{"cpu_ms":130.86199999999693,"wall_ms":241.8244960717857},{"cpu_ms":112.2439999999969,"wall_ms":197.85765814594924},{"cpu_ms":105.91600000000057,"wall_ms":347.07274101674557},{"cpu_ms":113.83600000000271,"wall_ms":131.14220718853176},{"cpu_ms":124.76399999999899,"wall_ms":168.04710705764592},{"cpu_ms":167.54599999999797,"wall_ms":218.1722540408373},{"cpu_ms":127.18300000000227,"wall_ms":203.53303384035826},{"cpu_ms":99.58199999999806,"wall_ms":185.55214093066752},{"cpu_ms":108.00000000000054,"wall_ms":217.39156520925462},{"cpu_ms":144.24899999999852,"wall_ms":213.80458096973598},{"cpu_ms":168.85699999999915,"wall_ms":211.7473459802568},{"cpu_ms":183.84,"wall_ms":263.1945041939616},{"cpu_ms":171.1740000000006,"wall_ms":264.69325507059693},{"cpu_ms":176.19399999999885,"wall_ms":237.6364590600133},{"cpu_ms":175.71600000000132,"wall_ms":203.0434801708907},{"cpu_ms":125.43499999999952,"wall_ms":179.60924399085343},{"cpu_ms":97.74100000000274,"wall_ms":110.15706812031567},{"cpu_ms":90.36900000000259,"wall_ms":155.92910000123084},{"cpu_ms":90.49199999999757,"wall_ms":114.6541319321841},{"cpu_ms":102.88800000000009,"wall_ms":186.1639239359647},{"cpu_ms":87.71000000000129,"wall_ms":114.93884399533272},{"cpu_ms":96.47000000000361,"wall_ms":101.79014108143747},{"cpu_ms":91.0909999999987,"wall_ms":98.3612930867821},{"cpu_ms":89.84200000000087,"wall_ms":95.11197102256119},{"cpu_ms":89.52400000000083,"wall_ms":96.83167492039502},{"cpu_ms":94.80699999999942,"wall_ms":102.10960800759494},{"cpu_ms":123.08399999999864,"wall_ms":129.31737606413662},{"cpu_ms":179.8169999999999,"wall_ms":185.22601085714996},{"cpu_ms":182.11200000000005,"wall_ms":188.47350007854402},{"cpu_ms":129.2340000000003,"wall_ms":132.2419720236212},{"cpu_ms":161.93300000000121,"wall_ms":165.28808907605708}],"after_median_cpu_ms":124.9910000000014}]}, + {"kind":"app-fixed-promise_all_chains","rows":[{"name":"promise_all_chains","node_exit":0,"before":[{"cpu_ms":109.60500000000017,"wall_ms":267.494146944955},{"cpu_ms":105.99099999999994,"wall_ms":160.05774401128292},{"cpu_ms":154.744,"wall_ms":355.1932731643319},{"cpu_ms":188.87100000000024,"wall_ms":282.2940950281918},{"cpu_ms":104.3639999999999,"wall_ms":166.77740192972124},{"cpu_ms":103.65500000000027,"wall_ms":162.01638500206172},{"cpu_ms":120.73499999999981,"wall_ms":201.15553005598485},{"cpu_ms":101.93900000000022,"wall_ms":136.30168698728085},{"cpu_ms":132.77300000000025,"wall_ms":264.0825810376555},{"cpu_ms":103.07599999999972,"wall_ms":279.61128181777894},{"cpu_ms":122.98700000000018,"wall_ms":237.55683796480298},{"cpu_ms":128.87299999999956,"wall_ms":186.33875995874405},{"cpu_ms":179.9080000000002,"wall_ms":337.1938138734549},{"cpu_ms":189.8219999999995,"wall_ms":289.1475029755384},{"cpu_ms":108.56099999999991,"wall_ms":193.6821818817407},{"cpu_ms":103.05900000000001,"wall_ms":155.42237111367285},{"cpu_ms":101.24500000000047,"wall_ms":208.8124379515648},{"cpu_ms":101.57199999999911,"wall_ms":156.20993287302554},{"cpu_ms":105.1479999999998,"wall_ms":196.61658606491983},{"cpu_ms":102.03299999999959,"wall_ms":149.36947915703058},{"cpu_ms":100.84299999999935,"wall_ms":213.93141406588256},{"cpu_ms":131.57900000000035,"wall_ms":215.65649891272187},{"cpu_ms":104.47000000000006,"wall_ms":241.04074900969863},{"cpu_ms":171.46099999999987,"wall_ms":213.25872698798776},{"cpu_ms":136.8639999999992,"wall_ms":186.42639205791056},{"cpu_ms":162.58200000000045,"wall_ms":215.1309170294553},{"cpu_ms":139.82799999999963,"wall_ms":197.1565750427544},{"cpu_ms":150.37500000000037,"wall_ms":268.0008120369166},{"cpu_ms":102.87299999999888,"wall_ms":183.4539771080017},{"cpu_ms":107.34000000000066,"wall_ms":175.9808510541916},{"cpu_ms":121.02100000000071,"wall_ms":252.4732940364629},{"cpu_ms":169.31999999999903,"wall_ms":245.87874789722264},{"cpu_ms":126.57000000000096,"wall_ms":209.58791789598763},{"cpu_ms":113.73899999999892,"wall_ms":195.49192301928997},{"cpu_ms":130.07100000000094,"wall_ms":228.90167101286352},{"cpu_ms":102.00599999999937,"wall_ms":175.32946285791695},{"cpu_ms":130.51900000000137,"wall_ms":210.2125841192901},{"cpu_ms":116.70599999999887,"wall_ms":188.55893705040216},{"cpu_ms":137.9160000000006,"wall_ms":233.43942081555724},{"cpu_ms":113.79699999999993,"wall_ms":176.63736687973142},{"cpu_ms":174.03099999999938,"wall_ms":279.5087869744748},{"cpu_ms":146.91600000000093,"wall_ms":280.25501198135316},{"cpu_ms":141.648,"wall_ms":284.666946856305},{"cpu_ms":162.95400000000092,"wall_ms":285.81231785938144},{"cpu_ms":133.69499999999945,"wall_ms":211.63620403967798},{"cpu_ms":145.95499999999896,"wall_ms":274.29082500748336},{"cpu_ms":101.74700000000136,"wall_ms":274.64837301522493},{"cpu_ms":184.9229999999995,"wall_ms":313.2576479110867},{"cpu_ms":118.38900000000052,"wall_ms":226.65897919796407},{"cpu_ms":114.7999999999989,"wall_ms":189.06372995115817},{"cpu_ms":109.96499999999898,"wall_ms":228.26628293842077},{"cpu_ms":101.99300000000022,"wall_ms":278.297012206167},{"cpu_ms":107.00899999999969,"wall_ms":135.81025716848671},{"cpu_ms":90.57199999999987,"wall_ms":154.28011515177786},{"cpu_ms":141.55600000000135,"wall_ms":293.2178759947419},{"cpu_ms":99.76199999999835,"wall_ms":199.55221097916365},{"cpu_ms":117.20599999999948,"wall_ms":190.7493588514626},{"cpu_ms":124.645000000001,"wall_ms":210.98874392919242},{"cpu_ms":138.22000000000045,"wall_ms":246.78734503686428},{"cpu_ms":153.4419999999983,"wall_ms":229.86463899724185},{"cpu_ms":168.07000000000016,"wall_ms":332.46576390229166},{"cpu_ms":137.06900000000033,"wall_ms":204.4887258671224},{"cpu_ms":189.43000000000154,"wall_ms":256.6374728921801},{"cpu_ms":181.91999999999808,"wall_ms":293.3195261284709},{"cpu_ms":186.73199999999923,"wall_ms":238.09792613610625},{"cpu_ms":170.81300000000255,"wall_ms":246.55527994036674},{"cpu_ms":140.30400000000043,"wall_ms":245.9182699676603},{"cpu_ms":120.74799999999897,"wall_ms":348.2687051873654},{"cpu_ms":110.96099999999964,"wall_ms":196.2843828368932},{"cpu_ms":97.30300000000014,"wall_ms":151.99801907874644},{"cpu_ms":154.54899999999938,"wall_ms":303.10302786529064},{"cpu_ms":97.3569999999988,"wall_ms":167.22059715539217},{"cpu_ms":158.85499999999908,"wall_ms":322.1769449301064},{"cpu_ms":143.6540000000015,"wall_ms":263.16831703297794},{"cpu_ms":146.59400000000034,"wall_ms":218.58159196563065},{"cpu_ms":124.34400000000068,"wall_ms":310.2560758125037},{"cpu_ms":189.83300000000014,"wall_ms":409.5919169485569},{"cpu_ms":137.37899999999925,"wall_ms":392.23660295829177},{"cpu_ms":188.99000000000044,"wall_ms":266.421525971964},{"cpu_ms":124.73400000000012,"wall_ms":357.9610779415816},{"cpu_ms":132.4639999999988,"wall_ms":241.801796015352},{"cpu_ms":179.5560000000016,"wall_ms":311.6795450914651},{"cpu_ms":158.74000000000166,"wall_ms":252.63345916755497},{"cpu_ms":164.42299999999932,"wall_ms":226.98161914013326},{"cpu_ms":162.8120000000024,"wall_ms":372.02948797494173},{"cpu_ms":98.61099999999823,"wall_ms":155.71679803542793},{"cpu_ms":111.98299999999861,"wall_ms":313.3319830521941},{"cpu_ms":116.29100000000037,"wall_ms":247.75694496929646},{"cpu_ms":112.87899999999951,"wall_ms":144.28220805712044},{"cpu_ms":187.380000000001,"wall_ms":234.78824808262289},{"cpu_ms":101.42400000000151,"wall_ms":186.63090094923973},{"cpu_ms":102.10400000000064,"wall_ms":200.53050597198308},{"cpu_ms":105.45000000000115,"wall_ms":155.40541498921812},{"cpu_ms":96.75899999999871,"wall_ms":149.00773903355002},{"cpu_ms":112.76600000000059,"wall_ms":200.75503480620682},{"cpu_ms":105.59100000000043,"wall_ms":212.50239899381995},{"cpu_ms":160.37999999999997,"wall_ms":309.5097988843918},{"cpu_ms":150.48699999999826,"wall_ms":255.02327387221158},{"cpu_ms":145.2460000000002,"wall_ms":211.87817212194204},{"cpu_ms":132.35799999999998,"wall_ms":236.12693906761706},{"cpu_ms":100.55499999999995,"wall_ms":165.6774519942701}],"before_median_cpu_ms":126.57000000000096,"after":[{"cpu_ms":176.70600000000002,"wall_ms":199.0628798957914},{"cpu_ms":100.12700000000008,"wall_ms":180.57835404761136},{"cpu_ms":169.13699999999966,"wall_ms":258.9813380036503},{"cpu_ms":164.40600000000006,"wall_ms":258.0586038529873},{"cpu_ms":96.02099999999992,"wall_ms":172.44478315114975},{"cpu_ms":100.5910000000001,"wall_ms":145.62110393308103},{"cpu_ms":111.19399999999979,"wall_ms":184.67386811971664},{"cpu_ms":194.5800000000002,"wall_ms":277.8699768241495},{"cpu_ms":112.13599999999957,"wall_ms":229.2158310301602},{"cpu_ms":178.25999999999988,"wall_ms":245.80386118032038},{"cpu_ms":179.47200000000052,"wall_ms":240.7401967793703},{"cpu_ms":106.81000000000029,"wall_ms":181.02323496714234},{"cpu_ms":106.32999999999981,"wall_ms":173.96446294151247},{"cpu_ms":179.92400000000063,"wall_ms":303.2846429850906},{"cpu_ms":147.32799999999992,"wall_ms":264.62018489837646},{"cpu_ms":106.6319999999994,"wall_ms":235.18841085024178},{"cpu_ms":103.30300000000037,"wall_ms":165.79509596340358},{"cpu_ms":101.9730000000001,"wall_ms":187.26894701831043},{"cpu_ms":100.26000000000046,"wall_ms":142.69949309527874},{"cpu_ms":97.1000000000002,"wall_ms":193.55604704469442},{"cpu_ms":100.9480000000007,"wall_ms":208.34927284158766},{"cpu_ms":105.03600000000013,"wall_ms":152.37674908712506},{"cpu_ms":101.74899999999987,"wall_ms":149.98453808948398},{"cpu_ms":119.11299999999957,"wall_ms":181.89272889867425},{"cpu_ms":156.58100000000098,"wall_ms":193.1266039609909},{"cpu_ms":138.2949999999994,"wall_ms":195.78658416867256},{"cpu_ms":99.80600000000095,"wall_ms":159.015575889498},{"cpu_ms":107.70099999999871,"wall_ms":143.46634596586227},{"cpu_ms":110.89700000000136,"wall_ms":169.1809429321438},{"cpu_ms":128.0140000000003,"wall_ms":187.3736521229148},{"cpu_ms":131.14399999999904,"wall_ms":233.4561231546104},{"cpu_ms":137.7450000000007,"wall_ms":206.5616708714515},{"cpu_ms":137.4209999999998,"wall_ms":189.2421639058739},{"cpu_ms":133.7980000000005,"wall_ms":211.0661999322474},{"cpu_ms":132.1399999999997,"wall_ms":237.05352586694062},{"cpu_ms":121.25299999999939,"wall_ms":200.82517084665596},{"cpu_ms":140.32599999999996,"wall_ms":218.95529190078378},{"cpu_ms":159.3049999999998,"wall_ms":217.9433919955045},{"cpu_ms":108.08899999999966,"wall_ms":162.51274105161428},{"cpu_ms":120.99900000000119,"wall_ms":183.392632054165},{"cpu_ms":104.8379999999991,"wall_ms":185.32323883846402},{"cpu_ms":187.82500000000013,"wall_ms":347.1212650183588},{"cpu_ms":122.27199999999883,"wall_ms":337.19773800112307},{"cpu_ms":107.9030000000003,"wall_ms":172.2377350088209},{"cpu_ms":149.74600000000038,"wall_ms":252.99111986532807},{"cpu_ms":143.1520000000006,"wall_ms":295.79022503457963},{"cpu_ms":109.93599999999937,"wall_ms":351.17270494811237},{"cpu_ms":130.9729999999991,"wall_ms":345.37208499386907},{"cpu_ms":110.33000000000115,"wall_ms":202.9625040013343},{"cpu_ms":107.41200000000006,"wall_ms":202.07918412052095},{"cpu_ms":92.77800000000092,"wall_ms":155.95403290353715},{"cpu_ms":105.85400000000078,"wall_ms":245.99268077872694},{"cpu_ms":108.9159999999989,"wall_ms":135.45865006744862},{"cpu_ms":90.21900000000116,"wall_ms":221.43474593758583},{"cpu_ms":129.02899999999917,"wall_ms":337.82712393440306},{"cpu_ms":148.09500000000142,"wall_ms":282.144000986591},{"cpu_ms":113.06199999999933,"wall_ms":322.7249598130584},{"cpu_ms":145.99399999999818,"wall_ms":221.32577188313007},{"cpu_ms":117.6540000000017,"wall_ms":195.87990688160062},{"cpu_ms":144.85099999999917,"wall_ms":201.27312699332833},{"cpu_ms":128.15700000000163,"wall_ms":278.6884328816086},{"cpu_ms":99.8410000000014,"wall_ms":242.07823514007032},{"cpu_ms":179.95699999999815,"wall_ms":295.3342110849917},{"cpu_ms":121.9140000000003,"wall_ms":240.21649407222867},{"cpu_ms":97.04299999999932,"wall_ms":141.6528660338372},{"cpu_ms":191.04899999999958,"wall_ms":255.5612209253013},{"cpu_ms":168.07599999999923,"wall_ms":297.5925300270319},{"cpu_ms":106.50400000000104,"wall_ms":154.1588061954826},{"cpu_ms":97.76899999999955,"wall_ms":186.2962970044464},{"cpu_ms":148.91400000000132,"wall_ms":348.32261595875025},{"cpu_ms":109.48300000000089,"wall_ms":165.07767280563712},{"cpu_ms":103.11199999999943,"wall_ms":372.7977361995727},{"cpu_ms":99.73700000000107,"wall_ms":162.92987810447812},{"cpu_ms":163.25499999999948,"wall_ms":258.8118261191994},{"cpu_ms":103.19499999999948,"wall_ms":188.6284938082099},{"cpu_ms":195.5159999999978,"wall_ms":294.8094590101391},{"cpu_ms":184.2240000000004,"wall_ms":333.820995176211},{"cpu_ms":105.22600000000182,"wall_ms":171.12314910627902},{"cpu_ms":105.06399999999871,"wall_ms":222.7094059344381},{"cpu_ms":133.9269999999999,"wall_ms":221.45079309120774},{"cpu_ms":145.80899999999986,"wall_ms":215.06789489649236},{"cpu_ms":151.23000000000175,"wall_ms":310.8247078489512},{"cpu_ms":129.4659999999972,"wall_ms":183.0679529812187},{"cpu_ms":137.90399999999892,"wall_ms":319.4298290181905},{"cpu_ms":189.13099999999972,"wall_ms":379.7111650928855},{"cpu_ms":135.03100000000146,"wall_ms":215.4013179242611},{"cpu_ms":181.61800000000028,"wall_ms":296.7095591593534},{"cpu_ms":104.89700000000113,"wall_ms":171.2660570628941},{"cpu_ms":111.5649999999988,"wall_ms":186.3364998716861},{"cpu_ms":184.9729999999994,"wall_ms":291.9969509821385},{"cpu_ms":129.8579999999987,"wall_ms":224.09560508094728},{"cpu_ms":103.84300000000124,"wall_ms":215.41460207663476},{"cpu_ms":116.40199999999723,"wall_ms":158.95806509070098},{"cpu_ms":142.23300000000094,"wall_ms":261.7628979496658},{"cpu_ms":118.83800000000022,"wall_ms":200.4041199106723},{"cpu_ms":187.7829999999996,"wall_ms":259.1601039748639},{"cpu_ms":158.07099999999963,"wall_ms":214.48858198709786},{"cpu_ms":140.26300000000091,"wall_ms":240.27809710241854},{"cpu_ms":165.46900000000164,"wall_ms":234.05475681647658},{"cpu_ms":190.4590000000006,"wall_ms":274.6640578843653},{"cpu_ms":185.21599999999694,"wall_ms":313.81385191343725}],"after_median_cpu_ms":128.0140000000003}]}, + {"kind":"aa-fixed-string_concat_csv","rows":[{"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":26.937999999999906,"wall_ms":186.89666595309973},{"cpu_ms":34.05700000000023,"wall_ms":163.588437018916},{"cpu_ms":49.886000000000095,"wall_ms":239.84210193157196},{"cpu_ms":28.043000000000042,"wall_ms":72.57237494923174},{"cpu_ms":41.88700000000001,"wall_ms":118.05542395450175},{"cpu_ms":42.006999999999906,"wall_ms":128.41856107115746},{"cpu_ms":41.03799999999991,"wall_ms":151.49800200015306},{"cpu_ms":39.15200000000007,"wall_ms":99.11139099858701},{"cpu_ms":38.2340000000001,"wall_ms":78.894869890064},{"cpu_ms":43.832999999999785,"wall_ms":152.82117505557835},{"cpu_ms":48.89299999999963,"wall_ms":109.1557121835649},{"cpu_ms":47.87600000000003,"wall_ms":318.5807301197201},{"cpu_ms":51.20099999999983,"wall_ms":120.91326504014432},{"cpu_ms":36.68800000000027,"wall_ms":93.48052809946239},{"cpu_ms":46.04700000000017,"wall_ms":218.80257199518383},{"cpu_ms":47.97300000000027,"wall_ms":176.33056011982262},{"cpu_ms":30.1250000000004,"wall_ms":81.73685101792216},{"cpu_ms":45.95499999999974,"wall_ms":253.15250596031547},{"cpu_ms":42.936000000000085,"wall_ms":107.30541497468948},{"cpu_ms":37.0689999999998,"wall_ms":120.61934499070048},{"cpu_ms":47.61399999999982,"wall_ms":57.00426106341183},{"cpu_ms":48.76799999999992,"wall_ms":55.13964616693556},{"cpu_ms":51.261999999999915,"wall_ms":57.67316999845207},{"cpu_ms":29.856000000000105,"wall_ms":33.248865976929665},{"cpu_ms":29.570000000000096,"wall_ms":33.9672788977623},{"cpu_ms":28.884000000000132,"wall_ms":35.750491078943014},{"cpu_ms":33.457999999999984,"wall_ms":37.864831974729896},{"cpu_ms":31.273000000000106,"wall_ms":35.61612009070814},{"cpu_ms":39.667000000000115,"wall_ms":47.1234149299562},{"cpu_ms":51.293000000000255,"wall_ms":56.697247084230185},{"cpu_ms":33.11600000000059,"wall_ms":40.92204594053328},{"cpu_ms":43.377999999999695,"wall_ms":53.77583694644272},{"cpu_ms":52.11299999999941,"wall_ms":55.52195105701685},{"cpu_ms":47.64900000000072,"wall_ms":53.983835969120264},{"cpu_ms":50.0809999999996,"wall_ms":55.46815902926028},{"cpu_ms":51.24399999999962,"wall_ms":55.65721308812499},{"cpu_ms":31.346000000000096,"wall_ms":33.746787114068866},{"cpu_ms":44.14300000000004,"wall_ms":48.504295060411096},{"cpu_ms":47.61899999999919,"wall_ms":55.98744098097086},{"cpu_ms":44.97600000000013,"wall_ms":54.37660007737577},{"cpu_ms":46.70899999999989,"wall_ms":55.083209881559014},{"cpu_ms":45.06999999999994,"wall_ms":51.48185999132693},{"cpu_ms":49.62399999999967,"wall_ms":54.09268895164132},{"cpu_ms":46.90300000000036,"wall_ms":54.381538880988955},{"cpu_ms":51.58200000000068,"wall_ms":55.01352995634079},{"cpu_ms":39.13999999999973,"wall_ms":45.64422997646034},{"cpu_ms":48.91600000000018,"wall_ms":55.35304406657815},{"cpu_ms":45.13899999999982,"wall_ms":50.58615584857762},{"cpu_ms":46.67399999999944,"wall_ms":55.03567215055227},{"cpu_ms":47.546999999999784,"wall_ms":54.91663981229067},{"cpu_ms":40.24300000000025,"wall_ms":43.615980073809624},{"cpu_ms":46.54899999999973,"wall_ms":52.9209990054369},{"cpu_ms":31.782999999999895,"wall_ms":39.124226197600365},{"cpu_ms":47.62600000000017,"wall_ms":54.96341711841524},{"cpu_ms":28.642999999999752,"wall_ms":33.91106380149722},{"cpu_ms":30.49199999999974,"wall_ms":35.93116789124906},{"cpu_ms":39.74399999999978,"wall_ms":47.132150968536735},{"cpu_ms":48.346000000000444,"wall_ms":52.84693092107773},{"cpu_ms":47.60399999999976,"wall_ms":54.02095499448478},{"cpu_ms":47.68799999999995,"wall_ms":54.190222173929214},{"cpu_ms":27.961000000000347,"wall_ms":34.29701481945813},{"cpu_ms":45.78099999999985,"wall_ms":54.17554290033877},{"cpu_ms":29.456999999999844,"wall_ms":34.73313990980387},{"cpu_ms":30.540000000000234,"wall_ms":34.876477904617786},{"cpu_ms":44.54600000000042,"wall_ms":54.87681389786303},{"cpu_ms":47.99600000000037,"wall_ms":53.37795312516391},{"cpu_ms":33.953999999999596,"wall_ms":38.25012198649347},{"cpu_ms":28.114000000000416,"wall_ms":35.845258040353656},{"cpu_ms":48.669000000000295,"wall_ms":57.020680978894234},{"cpu_ms":51.67300000000008,"wall_ms":57.039565872401},{"cpu_ms":50.87099999999989,"wall_ms":56.25280598178506},{"cpu_ms":51.66900000000041,"wall_ms":57.047700975090265},{"cpu_ms":30.435999999999908,"wall_ms":35.81901895813644},{"cpu_ms":30.408000000000435,"wall_ms":33.755091950297356},{"cpu_ms":47.868000000000244,"wall_ms":55.28420605696738},{"cpu_ms":46.161999999999814,"wall_ms":50.53904792293906},{"cpu_ms":49.171000000001186,"wall_ms":54.513456067070365},{"cpu_ms":47.81599999999919,"wall_ms":55.116883013397455},{"cpu_ms":47.34000000000016,"wall_ms":55.62046403065324},{"cpu_ms":49.274000000000484,"wall_ms":55.71172502823174},{"cpu_ms":46.829000000000676,"wall_ms":53.21889603510499},{"cpu_ms":48.46000000000039,"wall_ms":56.18683295324445},{"cpu_ms":48.632999999998816,"wall_ms":54.92277001030743},{"cpu_ms":51.64300000000033,"wall_ms":56.006336119025946},{"cpu_ms":38.39500000000129,"wall_ms":40.821478003636},{"cpu_ms":46.006999999999465,"wall_ms":53.383734077215195},{"cpu_ms":25.046000000001456,"wall_ms":34.9710239097476},{"cpu_ms":36.79499999999969,"wall_ms":43.93800091929734},{"cpu_ms":49.37999999999931,"wall_ms":53.793259896337986},{"cpu_ms":50.392000000000436,"wall_ms":52.7952138800174},{"cpu_ms":45.15100000000061,"wall_ms":52.53155203536153},{"cpu_ms":48.300999999998595,"wall_ms":51.68012995272875},{"cpu_ms":49.19300000000071,"wall_ms":55.63496192917228},{"cpu_ms":46.72799999999988,"wall_ms":52.17669811099768},{"cpu_ms":31.177000000001343,"wall_ms":38.57943695038557},{"cpu_ms":46.763999999999584,"wall_ms":53.275421960279346},{"cpu_ms":50.11099999999935,"wall_ms":55.41337700560689},{"cpu_ms":32.723999999999975,"wall_ms":38.032946875318885},{"cpu_ms":49.71300000000056,"wall_ms":54.082460002973676},{"cpu_ms":47.703999999999525,"wall_ms":55.08453189395368},{"cpu_ms":31.42000000000067,"wall_ms":35.83276504650712}],"before_median_cpu_ms":46.04700000000017,"after":[{"cpu_ms":36.04199999999991,"wall_ms":73.38061509653926},{"cpu_ms":42.012999999999856,"wall_ms":244.41776308231056},{"cpu_ms":50.038999999999945,"wall_ms":448.7263089977205},{"cpu_ms":40.10899999999995,"wall_ms":97.66357485204935},{"cpu_ms":51.099000000000004,"wall_ms":247.89377790875733},{"cpu_ms":52.77200000000004,"wall_ms":117.89213889278471},{"cpu_ms":37.00700000000001,"wall_ms":169.51940092258155},{"cpu_ms":30.750000000000277,"wall_ms":90.73041914962232},{"cpu_ms":33.166999999999724,"wall_ms":74.77219519205391},{"cpu_ms":32.95100000000018,"wall_ms":99.84750696457922},{"cpu_ms":45.80600000000024,"wall_ms":92.97922090627253},{"cpu_ms":34.91,"wall_ms":111.80315911769867},{"cpu_ms":51.87700000000017,"wall_ms":107.14167798869312},{"cpu_ms":33.199999999999896,"wall_ms":105.05177197046578},{"cpu_ms":51.99499999999979,"wall_ms":137.4720400199294},{"cpu_ms":32.84499999999957,"wall_ms":116.77425005473197},{"cpu_ms":32.98699999999988,"wall_ms":127.27851001545787},{"cpu_ms":36.98700000000032,"wall_ms":132.32793589122593},{"cpu_ms":29.00199999999975,"wall_ms":118.41267091222107},{"cpu_ms":27.53300000000003,"wall_ms":33.743411069735885},{"cpu_ms":30.17800000000026,"wall_ms":35.426335176452994},{"cpu_ms":28.380000000000294,"wall_ms":34.76219391450286},{"cpu_ms":51.10400000000004,"wall_ms":58.50585689768195},{"cpu_ms":30.432999999999932,"wall_ms":33.66843005642295},{"cpu_ms":45.16399999999976,"wall_ms":51.51350004598498},{"cpu_ms":37.037999999999904,"wall_ms":46.76101799122989},{"cpu_ms":49.94999999999994,"wall_ms":54.333298932760954},{"cpu_ms":46.98799999999981,"wall_ms":58.21773805655539},{"cpu_ms":31.752000000000002,"wall_ms":38.059787126258016},{"cpu_ms":39.13099999999936,"wall_ms":47.53529420122504},{"cpu_ms":43.096000000000245,"wall_ms":47.496090875938535},{"cpu_ms":44.74399999999967,"wall_ms":54.12320583127439},{"cpu_ms":47.564000000000384,"wall_ms":53.881915053352714},{"cpu_ms":47.43699999999951,"wall_ms":54.95204497128725},{"cpu_ms":50.43700000000051,"wall_ms":55.87952909991145},{"cpu_ms":36.907000000000245,"wall_ms":42.235700180754066},{"cpu_ms":29.37700000000021,"wall_ms":33.73154788278043},{"cpu_ms":36.34499999999985,"wall_ms":42.65704797580838},{"cpu_ms":50.63300000000037,"wall_ms":54.084583884105086},{"cpu_ms":47.32300000000045,"wall_ms":55.66371511667967},{"cpu_ms":46.70099999999966,"wall_ms":54.02304884046316},{"cpu_ms":43.52800000000023,"wall_ms":52.85122897475958},{"cpu_ms":50.061000000000355,"wall_ms":54.48009306564927},{"cpu_ms":48.941999999999375,"wall_ms":54.370989091694355},{"cpu_ms":49.85499999999998,"wall_ms":54.2358560487628},{"cpu_ms":45.74799999999968,"wall_ms":53.13582089729607},{"cpu_ms":47.81499999999994,"wall_ms":55.22682913579047},{"cpu_ms":27.622000000000035,"wall_ms":36.851858953014016},{"cpu_ms":38.82300000000072,"wall_ms":47.655008966103196},{"cpu_ms":51.121000000000194,"wall_ms":53.45719214528799},{"cpu_ms":34.96200000000016,"wall_ms":41.47168295457959},{"cpu_ms":48.802000000000234,"wall_ms":54.18614391237497},{"cpu_ms":48.826000000000036,"wall_ms":55.258619133383036},{"cpu_ms":43.28899999999969,"wall_ms":47.708227997645736},{"cpu_ms":31.169000000000224,"wall_ms":35.552500979974866},{"cpu_ms":33.37199999999996,"wall_ms":36.801546113565564},{"cpu_ms":32.96100000000024,"wall_ms":38.02088415250182},{"cpu_ms":47.75200000000002,"wall_ms":55.146477883681655},{"cpu_ms":51.00700000000025,"wall_ms":55.465003941208124},{"cpu_ms":47.723999999999656,"wall_ms":51.14028183743358},{"cpu_ms":46.76300000000033,"wall_ms":54.14431495591998},{"cpu_ms":26.085999999999387,"wall_ms":34.46498909033835},{"cpu_ms":27.918000000000553,"wall_ms":35.67866701632738},{"cpu_ms":29.450999999999894,"wall_ms":34.72814988344908},{"cpu_ms":48.35799999999946,"wall_ms":53.780676098540425},{"cpu_ms":49.78199999999955,"wall_ms":55.210147984325886},{"cpu_ms":48.78300000000024,"wall_ms":56.20675999671221},{"cpu_ms":52.535999999999916,"wall_ms":56.99520302005112},{"cpu_ms":49.92299999999972,"wall_ms":57.317125145345926},{"cpu_ms":47.66799999999982,"wall_ms":57.006804971024394},{"cpu_ms":48.62300000000008,"wall_ms":56.962060974910855},{"cpu_ms":32.00199999999942,"wall_ms":37.37103007733822},{"cpu_ms":44.90700000000025,"wall_ms":49.34003693051636},{"cpu_ms":43.46599999999956,"wall_ms":51.27559415996075},{"cpu_ms":41.7740000000002,"wall_ms":46.123865991830826},{"cpu_ms":46.86599999999963,"wall_ms":55.259479908272624},{"cpu_ms":42.96999999999951,"wall_ms":51.387844840064645},{"cpu_ms":46.60600000000059,"wall_ms":55.95930805429816},{"cpu_ms":48.41699999999882,"wall_ms":55.878656916320324},{"cpu_ms":46.84400000000011,"wall_ms":55.20984693430364},{"cpu_ms":29.84099999999934,"wall_ms":35.29309714213014},{"cpu_ms":32.03099999999992,"wall_ms":39.10700394771993},{"cpu_ms":51.11100000000057,"wall_ms":55.48447999171913},{"cpu_ms":37.29000000000049,"wall_ms":43.68912614881992},{"cpu_ms":41.827999999998866,"wall_ms":46.7926359269768},{"cpu_ms":45.747000000000426,"wall_ms":55.12406607158482},{"cpu_ms":45.91799999999857,"wall_ms":51.24989594332874},{"cpu_ms":32.30499999999914,"wall_ms":35.65922100096941},{"cpu_ms":35.60600000000136,"wall_ms":44.8934780433774},{"cpu_ms":48.0489999999989,"wall_ms":54.55956095829606},{"cpu_ms":48.989000000000615,"wall_ms":55.4090600926429},{"cpu_ms":48.70200000000047,"wall_ms":55.04736281000078},{"cpu_ms":41.75299999999993,"wall_ms":48.16035204567015},{"cpu_ms":28.995999999999356,"wall_ms":36.597945028916},{"cpu_ms":49.36499999999988,"wall_ms":55.87799591012299},{"cpu_ms":45.97599999999957,"wall_ms":53.335364209488034},{"cpu_ms":48.44500000000096,"wall_ms":55.78943993896246},{"cpu_ms":33.52199999999961,"wall_ms":37.00372320599854},{"cpu_ms":48.7169999999999,"wall_ms":53.06440684944391},{"cpu_ms":49.39099999999996,"wall_ms":54.735510144382715},{"cpu_ms":32.81699999999965,"wall_ms":40.06673698313534}],"after_median_cpu_ms":44.90700000000025}]}, + {"kind":"app-fixed-string_concat_csv","rows":[{"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":40.49699999999979,"wall_ms":46.79057304747403},{"cpu_ms":30.71500000000027,"wall_ms":35.024103010073304},{"cpu_ms":49.77999999999994,"wall_ms":56.16147490218282},{"cpu_ms":49.49099999999995,"wall_ms":55.8353450614959},{"cpu_ms":39.93400000000014,"wall_ms":45.299356104806066},{"cpu_ms":41.92200000000001,"wall_ms":47.30820003896952},{"cpu_ms":51.423999999999914,"wall_ms":53.805461851879954},{"cpu_ms":42.921000000000205,"wall_ms":47.27598000317812},{"cpu_ms":45.73399999999994,"wall_ms":54.058245150372386},{"cpu_ms":43.58299999999993,"wall_ms":49.96421397663653},{"cpu_ms":47.60000000000009,"wall_ms":52.92960489168763},{"cpu_ms":52.336999999999634,"wall_ms":56.762057123705745},{"cpu_ms":44.20499999999983,"wall_ms":50.43466296046972},{"cpu_ms":47.80899999999999,"wall_ms":55.972682079300284},{"cpu_ms":35.26000000000007,"wall_ms":40.73320305906236},{"cpu_ms":46.46399999999984,"wall_ms":52.760118152946234},{"cpu_ms":45.221000000000174,"wall_ms":48.60996198840439},{"cpu_ms":37.050999999999945,"wall_ms":44.46762194857001},{"cpu_ms":47.22500000000007,"wall_ms":52.610869985073805},{"cpu_ms":45.420000000000016,"wall_ms":53.741652984172106},{"cpu_ms":36.16400000000031,"wall_ms":38.435398833826184},{"cpu_ms":45.3619999999999,"wall_ms":48.72880387119949},{"cpu_ms":45.61500000000018,"wall_ms":50.01037009060383},{"cpu_ms":46.39399999999982,"wall_ms":52.73703602142632},{"cpu_ms":29.17499999999995,"wall_ms":37.584427976980805},{"cpu_ms":43.722999999999956,"wall_ms":54.02902001515031},{"cpu_ms":29.62800000000021,"wall_ms":35.890873055905104},{"cpu_ms":40.98800000000003,"wall_ms":46.73402709886432},{"cpu_ms":35.185999999999495,"wall_ms":42.11457399651408},{"cpu_ms":33.777999999999864,"wall_ms":40.097153978422284},{"cpu_ms":49.87700000000039,"wall_ms":55.22825079970062},{"cpu_ms":45.843999999999774,"wall_ms":56.2075509224087},{"cpu_ms":44.324999999999726,"wall_ms":53.57691505923867},{"cpu_ms":30.546000000000184,"wall_ms":35.87036509998143},{"cpu_ms":46.98599999999953,"wall_ms":50.27900217100978},{"cpu_ms":27.954000000000256,"wall_ms":34.26151815801859},{"cpu_ms":31.817999999999458,"wall_ms":38.8732070568949},{"cpu_ms":50.6000000000002,"wall_ms":53.99934505112469},{"cpu_ms":50.813999999999915,"wall_ms":56.203813990578055},{"cpu_ms":51.0250000000001,"wall_ms":58.468224946409464},{"cpu_ms":45.85100000000075,"wall_ms":51.83569109067321},{"cpu_ms":50.1459999999998,"wall_ms":57.565137976780534},{"cpu_ms":46.45100000000024,"wall_ms":51.05812707915902},{"cpu_ms":32.9839999999999,"wall_ms":40.40169296786189},{"cpu_ms":49.80200000000057,"wall_ms":55.17440987750888},{"cpu_ms":40.23399999999988,"wall_ms":45.56691600009799},{"cpu_ms":35.025000000000084,"wall_ms":45.35210411995649},{"cpu_ms":29.312999999999256,"wall_ms":33.643363043665886},{"cpu_ms":39.985999999999855,"wall_ms":45.29531812295318},{"cpu_ms":28.184999999999683,"wall_ms":34.55948503687978},{"cpu_ms":40.489,"wall_ms":47.90837108157575},{"cpu_ms":39.2929999999998,"wall_ms":40.566411102190614},{"cpu_ms":44.86800000000013,"wall_ms":52.22364608198404},{"cpu_ms":39.87499999999944,"wall_ms":50.53090211004019},{"cpu_ms":40.769000000000055,"wall_ms":47.08149586804211},{"cpu_ms":40.623000000000076,"wall_ms":46.024049865081906},{"cpu_ms":49.0699999999995,"wall_ms":52.44832602329552},{"cpu_ms":46.39099999999985,"wall_ms":50.81221787258983},{"cpu_ms":40.19699999999915,"wall_ms":45.57104385457933},{"cpu_ms":40.71700000000078,"wall_ms":46.07328213751316},{"cpu_ms":43.34500000000041,"wall_ms":47.94173315167427},{"cpu_ms":38.55399999999953,"wall_ms":43.844125932082534},{"cpu_ms":42.4490000000004,"wall_ms":49.41124003380537},{"cpu_ms":45.14599999999991,"wall_ms":53.581333020702004},{"cpu_ms":47.8019999999999,"wall_ms":52.118869964033365},{"cpu_ms":40.184,"wall_ms":48.58819185756147},{"cpu_ms":44.24799999999962,"wall_ms":52.54238098859787},{"cpu_ms":44.25100000000004,"wall_ms":48.66875195875764},{"cpu_ms":49.21299999999995,"wall_ms":55.60825113207102},{"cpu_ms":49.99900000000057,"wall_ms":54.351632948964834},{"cpu_ms":35.965000000000025,"wall_ms":41.21051402762532},{"cpu_ms":30.7599999999999,"wall_ms":37.81714313663542},{"cpu_ms":43.993999999999645,"wall_ms":49.47909596376121},{"cpu_ms":46.2530000000001,"wall_ms":52.58028209209442},{"cpu_ms":39.60499999999989,"wall_ms":45.092368964105844},{"cpu_ms":50.42499999999972,"wall_ms":54.832621943205595},{"cpu_ms":44.27100000000017,"wall_ms":52.5172739289701},{"cpu_ms":44.63100000000075,"wall_ms":50.95108808018267},{"cpu_ms":32.07200000000121,"wall_ms":39.032163098454475},{"cpu_ms":46.86799999999991,"wall_ms":52.29900707490742},{"cpu_ms":37.55400000000009,"wall_ms":45.05469789728522},{"cpu_ms":46.69799999999924,"wall_ms":53.276904160156846},{"cpu_ms":34.558999999999784,"wall_ms":41.019156808033586},{"cpu_ms":30.79900000000002,"wall_ms":37.16642712242901},{"cpu_ms":39.46900000000042,"wall_ms":44.98971696011722},{"cpu_ms":43.78999999999955,"wall_ms":52.18694685027003},{"cpu_ms":43.998999999999455,"wall_ms":49.37454080209136},{"cpu_ms":43.81900000000094,"wall_ms":50.132287899032235},{"cpu_ms":35.201999999999956,"wall_ms":40.62160407193005},{"cpu_ms":31.51999999999866,"wall_ms":36.93213895894587},{"cpu_ms":45.13700000000043,"wall_ms":55.4995471611619},{"cpu_ms":42.36700000000049,"wall_ms":48.66125900298357},{"cpu_ms":43.834999999999624,"wall_ms":47.2249339800328},{"cpu_ms":45.39700000000124,"wall_ms":50.94107799232006},{"cpu_ms":47.50100000000046,"wall_ms":51.91005999222398},{"cpu_ms":31.176999999999566,"wall_ms":37.566323997452855},{"cpu_ms":40.75900000000132,"wall_ms":47.08223813213408},{"cpu_ms":46.114999999998574,"wall_ms":49.44602493196726},{"cpu_ms":39.31599999999946,"wall_ms":45.736372005194426},{"cpu_ms":42.59600000000141,"wall_ms":47.924801940098405},{"cpu_ms":45.524999999999594,"wall_ms":48.840301809832454}],"before_median_cpu_ms":43.78999999999955,"after":[{"cpu_ms":43.96200000000006,"wall_ms":51.33394384756684},{"cpu_ms":44.57299999999975,"wall_ms":49.83720602467656},{"cpu_ms":48.856000000000236,"wall_ms":55.192173924297094},{"cpu_ms":41.33900000000001,"wall_ms":45.726404059678316},{"cpu_ms":43.101999999999975,"wall_ms":49.85017003491521},{"cpu_ms":31.80000000000005,"wall_ms":40.07067484781146},{"cpu_ms":46.17199999999988,"wall_ms":50.594893051311374},{"cpu_ms":49.16499999999991,"wall_ms":53.53384488262236},{"cpu_ms":51.80000000000007,"wall_ms":55.21073821000755},{"cpu_ms":50.78899999999997,"wall_ms":54.13002800196409},{"cpu_ms":44.944000000000095,"wall_ms":50.298518035560846},{"cpu_ms":50.59000000000013,"wall_ms":55.03691313788295},{"cpu_ms":28.732000000000202,"wall_ms":35.040433052927256},{"cpu_ms":47.84100000000002,"wall_ms":63.29697999171913},{"cpu_ms":45.74400000000001,"wall_ms":51.17947491817176},{"cpu_ms":29.755000000000198,"wall_ms":34.04662711545825},{"cpu_ms":44.929999999999914,"wall_ms":53.32848010584712},{"cpu_ms":36.18199999999972,"wall_ms":40.53988098166883},{"cpu_ms":43.97099999999998,"wall_ms":50.51234713755548},{"cpu_ms":31.27499999999994,"wall_ms":37.724820198491216},{"cpu_ms":42.82399999999997,"wall_ms":49.46585209108889},{"cpu_ms":44.55199999999992,"wall_ms":50.96173798665404},{"cpu_ms":48.27799999999982,"wall_ms":53.6365769803524},{"cpu_ms":28.443000000000218,"wall_ms":34.684017999097705},{"cpu_ms":46.43899999999989,"wall_ms":52.7835029643029},{"cpu_ms":49.47700000000044,"wall_ms":53.859472973272204},{"cpu_ms":28.62899999999957,"wall_ms":32.865178072825074},{"cpu_ms":37.24099999999986,"wall_ms":43.66158484481275},{"cpu_ms":43.98100000000049,"wall_ms":52.349892212077975},{"cpu_ms":33.037000000000205,"wall_ms":39.32590293698013},{"cpu_ms":34.52699999999975,"wall_ms":38.90576702542603},{"cpu_ms":47.596000000000416,"wall_ms":55.89312291704118},{"cpu_ms":44.21799999999987,"wall_ms":52.69913491792977},{"cpu_ms":26.876999999999818,"wall_ms":35.182429011911154},{"cpu_ms":49.140000000000406,"wall_ms":53.37961600162089},{"cpu_ms":41.13100000000003,"wall_ms":46.37666093185544},{"cpu_ms":41.17000000000015,"wall_ms":48.49627893418074},{"cpu_ms":40.47399999999968,"wall_ms":47.1705230884254},{"cpu_ms":43.53000000000051,"wall_ms":50.47909589484334},{"cpu_ms":51.71700000000001,"wall_ms":58.072215877473354},{"cpu_ms":32.237999999999545,"wall_ms":38.66623993963003},{"cpu_ms":50.341999999999665,"wall_ms":54.712117183953524},{"cpu_ms":51.601999999999926,"wall_ms":58.01450810395181},{"cpu_ms":36.92899999999977,"wall_ms":45.53848318755627},{"cpu_ms":29.39500000000006,"wall_ms":35.6703819707036},{"cpu_ms":34.2039999999999,"wall_ms":41.61810618825257},{"cpu_ms":49.20200000000019,"wall_ms":55.6436269544065},{"cpu_ms":49.97700000000016,"wall_ms":54.364406038075686},{"cpu_ms":45.53200000000057,"wall_ms":54.87134400755167},{"cpu_ms":51.47000000000013,"wall_ms":54.87657315097749},{"cpu_ms":39.9029999999998,"wall_ms":49.27551606670022},{"cpu_ms":48.172000000000104,"wall_ms":52.49196686781943},{"cpu_ms":34.86800000000034,"wall_ms":42.68184397369623},{"cpu_ms":37.70499999999988,"wall_ms":47.02246584929526},{"cpu_ms":42.637000000000036,"wall_ms":46.9840238802135},{"cpu_ms":37.92400000000029,"wall_ms":46.2400340475142},{"cpu_ms":42.640000000000455,"wall_ms":47.04243293963373},{"cpu_ms":44.44300000000023,"wall_ms":48.85640321299434},{"cpu_ms":46.88400000000037,"wall_ms":54.240353871136904},{"cpu_ms":45.19899999999932,"wall_ms":49.62930711917579},{"cpu_ms":41.05000000000025,"wall_ms":45.43463792651892},{"cpu_ms":39.29500000000008,"wall_ms":42.71631804294884},{"cpu_ms":45.05099999999995,"wall_ms":49.44569384679198},{"cpu_ms":45.49700000000012,"wall_ms":48.912178026512265},{"cpu_ms":45.107999999999926,"wall_ms":49.47889712639153},{"cpu_ms":47.899000000000136,"wall_ms":53.287564078345895},{"cpu_ms":32.93299999999988,"wall_ms":38.30054705031216},{"cpu_ms":47.889999999999766,"wall_ms":54.99955383129418},{"cpu_ms":50.23,"wall_ms":53.65147511474788},{"cpu_ms":37.40100000000002,"wall_ms":44.81950891204178},{"cpu_ms":35.30999999999995,"wall_ms":38.69860991835594},{"cpu_ms":43.43999999999948,"wall_ms":49.71892503090203},{"cpu_ms":42.42600000000074,"wall_ms":46.87083209864795},{"cpu_ms":45.99099999999989,"wall_ms":52.413360215723515},{"cpu_ms":35.71300000000033,"wall_ms":42.040125001221895},{"cpu_ms":42.81299999999977,"wall_ms":50.34566600807011},{"cpu_ms":40.165000000000006,"wall_ms":45.55709706619382},{"cpu_ms":45.28900000000036,"wall_ms":49.66705804690719},{"cpu_ms":48.20099999999883,"wall_ms":51.49841005913913},{"cpu_ms":50.08599999999852,"wall_ms":53.40327997691929},{"cpu_ms":41.63100000000064,"wall_ms":46.999002108350396},{"cpu_ms":30.611000000000388,"wall_ms":37.13595005683601},{"cpu_ms":36.298000000000386,"wall_ms":40.67034483887255},{"cpu_ms":38.6539999999993,"wall_ms":43.97199512459338},{"cpu_ms":42.1990000000001,"wall_ms":50.68763508461416},{"cpu_ms":37.361999999999895,"wall_ms":44.67260395176709},{"cpu_ms":49.54700000000045,"wall_ms":51.95773905143142},{"cpu_ms":43.50600000000071,"wall_ms":51.83282610960305},{"cpu_ms":45.253999999999905,"wall_ms":49.60462101735175},{"cpu_ms":44.87300000000083,"wall_ms":51.22128198854625},{"cpu_ms":39.14699999999982,"wall_ms":44.48076616972685},{"cpu_ms":46.65099999999889,"wall_ms":53.025264060124755},{"cpu_ms":42.81399999999991,"wall_ms":50.11254083365202},{"cpu_ms":49.739999999999895,"wall_ms":54.083361057564616},{"cpu_ms":46.10199999999942,"wall_ms":50.50328001379967},{"cpu_ms":42.56600000000077,"wall_ms":48.0023268610239},{"cpu_ms":42.02999999999868,"wall_ms":44.405285036191344},{"cpu_ms":40.64900000000016,"wall_ms":47.98518493771553},{"cpu_ms":38.34400000000038,"wall_ms":41.68492089956999},{"cpu_ms":44.450999999998686,"wall_ms":55.71901798248291},{"cpu_ms":47.6220000000005,"wall_ms":51.03488406166434}],"after_median_cpu_ms":43.53000000000051}]}, + {"kind":"segment-final","rows":[{"mode":"segment","scale":100,"samples":{"before":[{"segment":45.701},{"segment":41.446},{"segment":37.787},{"segment":38.008},{"segment":44.991},{"segment":56.217},{"segment":59.482}],"after":[{"segment":43.909},{"segment":45.017},{"segment":39.561},{"segment":39.816},{"segment":37.487},{"segment":57.537},{"segment":66.359}],"bun":[{"segment":6.801},{"segment":4.861},{"segment":4.125},{"segment":4.376},{"segment":4.708},{"segment":4.773},{"segment":6.391}]},"output":["checksum 1340000"],"medians_us":{"before":{"segment":44.991},"after":{"segment":43.909},"bun":{"segment":4.773}}}]} +] diff --git a/benchmarks/regexp-construction/current-app-samples.json b/benchmarks/regexp-construction/current-app-samples.json new file mode 100644 index 0000000000..10cfd10eb3 --- /dev/null +++ b/benchmarks/regexp-construction/current-app-samples.json @@ -0,0 +1,14 @@ +[ + {"name":"batch","node_exit":0,"before":[{"cpu_ms":47.7789999999998,"wall_ms":54.72791404463351},{"cpu_ms":46.3610000000001,"wall_ms":54.34228293597698},{"cpu_ms":47.395000000000294,"wall_ms":53.780844900757074},{"cpu_ms":47.274999999999956,"wall_ms":51.788341952487826},{"cpu_ms":31.38899999999989,"wall_ms":34.80892721563578},{"cpu_ms":47.42899999999972,"wall_ms":53.745007840916514},{"cpu_ms":50.43299999999995,"wall_ms":55.96054601483047},{"cpu_ms":49.733999999999945,"wall_ms":55.62988901510835},{"cpu_ms":51.54000000000014,"wall_ms":56.02086987346411},{"cpu_ms":43.35500000000003,"wall_ms":50.8186100050807},{"cpu_ms":46.26999999999981,"wall_ms":52.731815027073026},{"cpu_ms":42.2709999999995,"wall_ms":47.57675202563405}],"before_median_cpu_ms":47.33500000000012,"after":[{"cpu_ms":45.96500000000026,"wall_ms":54.36420487239957},{"cpu_ms":44.324999999999726,"wall_ms":50.74998107738793},{"cpu_ms":44.99699999999996,"wall_ms":49.349615117534995},{"cpu_ms":46.3070000000001,"wall_ms":51.9338040612638},{"cpu_ms":32.75700000000015,"wall_ms":38.09304302558303},{"cpu_ms":50.406000000000176,"wall_ms":53.8454360794276},{"cpu_ms":47.848999999999364,"wall_ms":53.24995215050876},{"cpu_ms":49.52200000000051,"wall_ms":56.9278949405998},{"cpu_ms":57.0529999999998,"wall_ms":61.568174976855516},{"cpu_ms":48.60000000000042,"wall_ms":54.01175608858466},{"cpu_ms":39.063999999999766,"wall_ms":43.539979960769415},{"cpu_ms":34.36800000000062,"wall_ms":39.817024022340775}],"after_median_cpu_ms":46.13600000000018,"before_control":[{"cpu_ms":51.68699999999981,"wall_ms":56.17060884833336},{"cpu_ms":49.83800000000027,"wall_ms":56.29191594198346},{"cpu_ms":46.142000000000124,"wall_ms":50.70806294679642},{"cpu_ms":53.258000000000024,"wall_ms":61.137129087001085},{"cpu_ms":51.152000000000086,"wall_ms":54.57178317010403},{"cpu_ms":46.98599999999953,"wall_ms":53.448303835466504},{"cpu_ms":46.30500000000026,"wall_ms":53.79742500372231},{"cpu_ms":52.825000000000344,"wall_ms":58.32313094288111},{"cpu_ms":48.89100000000024,"wall_ms":56.30688415840268},{"cpu_ms":44.11200000000015,"wall_ms":48.53684711270034},{"cpu_ms":48.22300000000013,"wall_ms":54.527620086446404},{"cpu_ms":51.71100000000006,"wall_ms":56.16115196608007}],"before_control_median_cpu_ms":49.364500000000255,"after_control":[{"cpu_ms":51.05000000000004,"wall_ms":56.488103000447154},{"cpu_ms":49.18900000000015,"wall_ms":52.60940594598651},{"cpu_ms":48.2269999999998,"wall_ms":53.96037991158664},{"cpu_ms":48.921999999999684,"wall_ms":52.40232893265784},{"cpu_ms":46.91400000000012,"wall_ms":52.29146080091596},{"cpu_ms":47.32500000000029,"wall_ms":53.66072990000248},{"cpu_ms":50.184000000000225,"wall_ms":54.672189988195896},{"cpu_ms":50.24599999999957,"wall_ms":56.74978089518845},{"cpu_ms":47.65599999999992,"wall_ms":53.16151701845229},{"cpu_ms":45.0039999999996,"wall_ms":51.44275608472526},{"cpu_ms":52.688999999999986,"wall_ms":56.111107813194394},{"cpu_ms":48.354999999999926,"wall_ms":53.70299890637398}],"after_control_median_cpu_ms":48.63849999999981}, + {"name":"buffer_transcode","node_exit":0,"before":[{"cpu_ms":72.89100000000026,"wall_ms":91.26440901309252},{"cpu_ms":77.68899999999945,"wall_ms":99.01160397566855},{"cpu_ms":72.25900000000074,"wall_ms":87.72689895704389},{"cpu_ms":70.82199999999972,"wall_ms":87.45115390047431},{"cpu_ms":71.9189999999994,"wall_ms":87.77824603021145},{"cpu_ms":71.58700000000096,"wall_ms":88.36133498698473},{"cpu_ms":77.26100000000002,"wall_ms":96.44867503084242},{"cpu_ms":72.95400000000107,"wall_ms":88.86150899343193},{"cpu_ms":74.6450000000003,"wall_ms":92.54192607477307},{"cpu_ms":69.64499999999951,"wall_ms":84.24517512321472},{"cpu_ms":71.86199999999943,"wall_ms":97.29968500323594},{"cpu_ms":80.97699999999897,"wall_ms":100.65289097838104}],"before_median_cpu_ms":72.5750000000005,"after":[{"cpu_ms":73.67699999999999,"wall_ms":96.99748107232153},{"cpu_ms":63.78200000000067,"wall_ms":81.44175703637302},{"cpu_ms":69.09399999999977,"wall_ms":86.52932196855545},{"cpu_ms":71.27499999999998,"wall_ms":86.96732996031642},{"cpu_ms":65.81800000000015,"wall_ms":85.1904300507158},{"cpu_ms":75.08799999999916,"wall_ms":91.61363099701703},{"cpu_ms":68.92799999999966,"wall_ms":88.34483404643834},{"cpu_ms":66.87499999999957,"wall_ms":85.2374779060483},{"cpu_ms":69.80499999999878,"wall_ms":90.13806493021548},{"cpu_ms":53.86299999999977,"wall_ms":72.2109389025718},{"cpu_ms":71.708000000001,"wall_ms":93.07507192716002},{"cpu_ms":82.83300000000082,"wall_ms":99.20454421080649}],"after_median_cpu_ms":69.44949999999928,"before_control":[{"cpu_ms":78.59099999999941,"wall_ms":96.97173186577857},{"cpu_ms":76.57300000000066,"wall_ms":94.70378491096199},{"cpu_ms":58.58899999999956,"wall_ms":72.88376707583666},{"cpu_ms":69.33500000000059,"wall_ms":88.62966601736844},{"cpu_ms":73.37900000000097,"wall_ms":91.89711092039943},{"cpu_ms":74.13400000000081,"wall_ms":90.66892601549625},{"cpu_ms":74.58899999999957,"wall_ms":95.95047496259212},{"cpu_ms":70.27599999999978,"wall_ms":90.6340410001576},{"cpu_ms":67.74599999999964,"wall_ms":88.08846515603364},{"cpu_ms":73.76899999999864,"wall_ms":91.74725110642612},{"cpu_ms":73.3719999999991,"wall_ms":85.094572044909},{"cpu_ms":77.30700000000112,"wall_ms":98.21829199790955}],"before_control_median_cpu_ms":73.57399999999981,"after_control":[{"cpu_ms":69.99900000000014,"wall_ms":95.4446888063103},{"cpu_ms":80.34999999999926,"wall_ms":100.70202290080488},{"cpu_ms":80.69400000000027,"wall_ms":99.02622108347714},{"cpu_ms":71.94499999999948,"wall_ms":86.3596850540489},{"cpu_ms":73.23399999999936,"wall_ms":90.98682110197842},{"cpu_ms":71.84999999999953,"wall_ms":90.7279271632433},{"cpu_ms":75.50499999999971,"wall_ms":97.62343112379313},{"cpu_ms":70.86300000000101,"wall_ms":88.22417887859046},{"cpu_ms":72.61400000000151,"wall_ms":94.22633307985961},{"cpu_ms":62.886000000000664,"wall_ms":85.07499401457608},{"cpu_ms":72.92300000000118,"wall_ms":88.8221759814769},{"cpu_ms":74.9809999999993,"wall_ms":92.17069204896688}],"after_control_median_cpu_ms":72.76850000000135}, + {"name":"date_format_parse","node_exit":0,"before":[{"cpu_ms":48.313999999999524,"wall_ms":52.865064004436135},{"cpu_ms":41.79599999999972,"wall_ms":45.76968704350293},{"cpu_ms":39.46399999999883,"wall_ms":45.82006996497512},{"cpu_ms":31.191000000001523,"wall_ms":37.79421513900161},{"cpu_ms":47.266999999999726,"wall_ms":52.665982162579894},{"cpu_ms":45.268000000000086,"wall_ms":52.44146194308996},{"cpu_ms":44.95100000000107,"wall_ms":51.69882392510772},{"cpu_ms":47.18200000000117,"wall_ms":51.12176691181958},{"cpu_ms":47.80799999999985,"wall_ms":52.44176206178963},{"cpu_ms":35.82400000000163,"wall_ms":40.52768205292523},{"cpu_ms":48.44399999999993,"wall_ms":51.869634073227644},{"cpu_ms":49.02599999999957,"wall_ms":53.38696902617812}],"before_median_cpu_ms":46.22500000000063,"after":[{"cpu_ms":47.458000000000666,"wall_ms":52.60263313539326},{"cpu_ms":43.50699999999996,"wall_ms":51.95472319610417},{"cpu_ms":49.30599999999963,"wall_ms":52.76568792760372},{"cpu_ms":45.873999999999526,"wall_ms":51.34574417024851},{"cpu_ms":46.03300000000132,"wall_ms":61.12443492747843},{"cpu_ms":49.86500000000049,"wall_ms":53.27620101161301},{"cpu_ms":45.59999999999853,"wall_ms":50.95720896497369},{"cpu_ms":46.763999999999584,"wall_ms":51.15458811633289},{"cpu_ms":50.394000000000716,"wall_ms":51.77483707666397},{"cpu_ms":44.83299999999879,"wall_ms":50.23194383829832},{"cpu_ms":46.345000000000525,"wall_ms":50.988066010177135},{"cpu_ms":44.27300000000045,"wall_ms":49.68965100124478}],"after_median_cpu_ms":46.189000000000924,"before_control":[{"cpu_ms":42.27500000000006,"wall_ms":48.21667820215225},{"cpu_ms":43.98500000000105,"wall_ms":54.77690510451794},{"cpu_ms":45.83500000000029,"wall_ms":52.50031198374927},{"cpu_ms":49.426999999999666,"wall_ms":53.785283118486404},{"cpu_ms":45.49699999999923,"wall_ms":49.010101007297635},{"cpu_ms":47.4060000000005,"wall_ms":51.8892512191087},{"cpu_ms":46.38299999999873,"wall_ms":51.98207311332226},{"cpu_ms":46.656000000000475,"wall_ms":52.72082379087806},{"cpu_ms":40.55900000000001,"wall_ms":43.99560112506151},{"cpu_ms":41.412000000001115,"wall_ms":43.76368899829686},{"cpu_ms":47.17800000000061,"wall_ms":52.5892882142216},{"cpu_ms":49.30899999999916,"wall_ms":49.69680402427912}],"before_control_median_cpu_ms":46.10899999999951,"after_control":[{"cpu_ms":42.009000000000185,"wall_ms":48.232898116111755},{"cpu_ms":41.69899999999949,"wall_ms":47.56648396141827},{"cpu_ms":41.560000000000485,"wall_ms":44.972067000344396},{"cpu_ms":46.941999999999595,"wall_ms":58.40268009342253},{"cpu_ms":29.42900000000037,"wall_ms":33.70232880115509},{"cpu_ms":48.903999999998504,"wall_ms":56.37931893579662},{"cpu_ms":48.369000000000995,"wall_ms":50.732549047097564},{"cpu_ms":46.471999999999625,"wall_ms":50.831924891099334},{"cpu_ms":45.10099999999895,"wall_ms":47.03855700790882},{"cpu_ms":49.31999999999981,"wall_ms":53.761548129841685},{"cpu_ms":44.48899999999867,"wall_ms":48.822680953890085},{"cpu_ms":46.54799999999959,"wall_ms":49.421149073168635}],"after_control_median_cpu_ms":45.786499999999286}, + {"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":81.44500000000221,"wall_ms":87.30443101376295},{"cpu_ms":86.11799999999903,"wall_ms":95.81528208218515},{"cpu_ms":77.41400000000098,"wall_ms":83.97286501713097},{"cpu_ms":87.70800000000278,"wall_ms":91.10830794088542},{"cpu_ms":78.16000000000045,"wall_ms":86.8921100627631},{"cpu_ms":83.06299999999922,"wall_ms":89.50942987576127},{"cpu_ms":73.66100000000131,"wall_ms":79.07496392726898},{"cpu_ms":82.6279999999997,"wall_ms":89.82599107548594},{"cpu_ms":82.90300000000173,"wall_ms":90.30573791824281},{"cpu_ms":75.54199999999867,"wall_ms":84.48034012690187},{"cpu_ms":71.95399999999808,"wall_ms":77.29005184955895},{"cpu_ms":82.48999999999995,"wall_ms":86.84870484285057}],"before_median_cpu_ms":81.96750000000108,"after":[{"cpu_ms":91.44500000000022,"wall_ms":96.3808388914913},{"cpu_ms":92.58799999999923,"wall_ms":99.2394289933145},{"cpu_ms":82.72899999999694,"wall_ms":88.13717612065375},{"cpu_ms":89.33000000000035,"wall_ms":92.7437727805227},{"cpu_ms":76.88800000000029,"wall_ms":84.25233699381351},{"cpu_ms":71.04999999999961,"wall_ms":77.39960378967226},{"cpu_ms":80.3219999999989,"wall_ms":89.20867904089391},{"cpu_ms":75.95500000000044,"wall_ms":81.66112611070275},{"cpu_ms":78.3680000000011,"wall_ms":83.7219669483602},{"cpu_ms":76.14699999999885,"wall_ms":86.38539910316467},{"cpu_ms":73.51399999999941,"wall_ms":77.98397704027593},{"cpu_ms":78.47699999999946,"wall_ms":83.86969403363764}],"after_median_cpu_ms":78.42250000000028,"before_control":[{"cpu_ms":78.56599999999858,"wall_ms":88.92649109475315},{"cpu_ms":77.0060000000008,"wall_ms":88.78453611396253},{"cpu_ms":83.2000000000015,"wall_ms":96.79150604642928},{"cpu_ms":82.07399999999865,"wall_ms":88.41602806933224},{"cpu_ms":84.94899999999816,"wall_ms":91.33754600770772},{"cpu_ms":82.58800000000122,"wall_ms":90.9180419985205},{"cpu_ms":81.69499999999985,"wall_ms":87.62566093355417},{"cpu_ms":83.6749999999995,"wall_ms":88.8421528507024},{"cpu_ms":81.61999999999736,"wall_ms":87.99417898990214},{"cpu_ms":74.79399999999714,"wall_ms":83.21107807569206},{"cpu_ms":78.5390000000028,"wall_ms":92.02750213444233},{"cpu_ms":73.26299999999719,"wall_ms":81.6543810069561}],"before_control_median_cpu_ms":81.6574999999986,"after_control":[{"cpu_ms":85.39999999999992,"wall_ms":90.85149713791907},{"cpu_ms":86.63100000000057,"wall_ms":93.09760411269963},{"cpu_ms":76.63200000000003,"wall_ms":87.49202080070972},{"cpu_ms":86.4609999999999,"wall_ms":95.17348208464682},{"cpu_ms":87.15000000000117,"wall_ms":93.8383680768311},{"cpu_ms":92.27399999999975,"wall_ms":98.9410721231252},{"cpu_ms":70.59399999999982,"wall_ms":77.08359393291175},{"cpu_ms":73.24699999999851,"wall_ms":84.08981398679316},{"cpu_ms":79.10600000000301,"wall_ms":84.16715008206666},{"cpu_ms":85.86600000000288,"wall_ms":91.27671900205314},{"cpu_ms":80.04299999999986,"wall_ms":88.98715092800558},{"cpu_ms":71.40200000000263,"wall_ms":82.81413000077009}],"after_control_median_cpu_ms":82.72149999999989}, + {"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":100.00499999999946,"wall_ms":105.07619008421898},{"cpu_ms":75.7159999999999,"wall_ms":81.98339608497918},{"cpu_ms":140.91599999999715,"wall_ms":147.29644590988755},{"cpu_ms":130.24999999999665,"wall_ms":138.62565089948475},{"cpu_ms":75.28800000000047,"wall_ms":84.7226670011878},{"cpu_ms":89.3940000000022,"wall_ms":96.96882893331349},{"cpu_ms":141.4749999999998,"wall_ms":148.86832400225103},{"cpu_ms":128.19400000000059,"wall_ms":134.64051787741482},{"cpu_ms":90.69900000000075,"wall_ms":100.2055718563497},{"cpu_ms":89.71299999999971,"wall_ms":93.01409195177257},{"cpu_ms":123.89699999999948,"wall_ms":133.27741785906255},{"cpu_ms":99.31699999999921,"wall_ms":104.86770002171397}],"before_median_cpu_ms":99.66099999999933,"after":[{"cpu_ms":124.07599999999874,"wall_ms":127.42297095246613},{"cpu_ms":72.4039999999988,"wall_ms":80.7005160022527},{"cpu_ms":130.48800000000327,"wall_ms":134.8576550371945},{"cpu_ms":138.36700000000235,"wall_ms":141.75337902270257},{"cpu_ms":100.93099999999922,"wall_ms":106.53345612809062},{"cpu_ms":77.71599999999879,"wall_ms":84.48690688237548},{"cpu_ms":148.0239999999995,"wall_ms":152.46052178554237},{"cpu_ms":123.22100000000091,"wall_ms":129.62491787038743},{"cpu_ms":125.21399999999971,"wall_ms":130.6058019399643},{"cpu_ms":77.06100000000049,"wall_ms":81.38072793371975},{"cpu_ms":77.26800000000011,"wall_ms":85.62394208274782},{"cpu_ms":130.4520000000018,"wall_ms":134.79455490596592}],"after_median_cpu_ms":123.64849999999983,"before_control":[{"cpu_ms":80.97800000000177,"wall_ms":83.31594802439213},{"cpu_ms":88.80399999999966,"wall_ms":90.10919113643467},{"cpu_ms":80.0219999999996,"wall_ms":83.40743812732399},{"cpu_ms":113.8629999999985,"wall_ms":117.24654003046453},{"cpu_ms":89.77300000000099,"wall_ms":95.15426610596478},{"cpu_ms":76.94099999999793,"wall_ms":83.26366916298866},{"cpu_ms":145.05199999999974,"wall_ms":150.41526686400175},{"cpu_ms":79.01500000000183,"wall_ms":98.39900187216699},{"cpu_ms":131.3849999999981,"wall_ms":136.7556331679225},{"cpu_ms":105.68600000000217,"wall_ms":112.09591687656939},{"cpu_ms":133.3270000000013,"wall_ms":137.76403781957924},{"cpu_ms":83.0510000000011,"wall_ms":86.8999280501157}],"before_control_median_cpu_ms":89.28850000000033,"after_control":[{"cpu_ms":125.85599999999886,"wall_ms":131.15736516192555},{"cpu_ms":113.4750000000011,"wall_ms":120.83276687189937},{"cpu_ms":78.29000000000264,"wall_ms":83.56950106099248},{"cpu_ms":128.60200000000077,"wall_ms":139.0908199828118},{"cpu_ms":90.2390000000004,"wall_ms":96.1639741435647},{"cpu_ms":77.43200000000172,"wall_ms":82.63736800290644},{"cpu_ms":85.0679999999997,"wall_ms":92.70522487349808},{"cpu_ms":75.23599999999675,"wall_ms":81.46717888303101},{"cpu_ms":97.76899999999955,"wall_ms":102.16363309882581},{"cpu_ms":76.88699999999926,"wall_ms":80.1724479533732},{"cpu_ms":78.35899999999896,"wall_ms":81.74241310916841},{"cpu_ms":75.64999999999955,"wall_ms":82.0831109303981}],"after_control_median_cpu_ms":81.71349999999933}, + {"name":"map_1m","node_exit":0,"before":[{"cpu_ms":244.09900000000206,"wall_ms":256.45526498556137},{"cpu_ms":247.14900000000029,"wall_ms":255.4681869223714},{"cpu_ms":235.2249999999998,"wall_ms":240.59775797650218},{"cpu_ms":226.48300000000177,"wall_ms":233.83128410205245},{"cpu_ms":259.34500000000327,"wall_ms":271.69920899905264},{"cpu_ms":199.20100000000218,"wall_ms":210.60416707769036},{"cpu_ms":223.4070000000017,"wall_ms":230.76300392858684},{"cpu_ms":223.31199999999995,"wall_ms":228.68320508860052},{"cpu_ms":232.916000000003,"wall_ms":239.3255850765854},{"cpu_ms":219.3150000000017,"wall_ms":229.6565929427743},{"cpu_ms":246.18199999999746,"wall_ms":254.5344450045377},{"cpu_ms":216.5950000000052,"wall_ms":229.97979703359306}],"before_median_cpu_ms":229.6995000000024,"after":[{"cpu_ms":234.53599999999852,"wall_ms":247.85868893377483},{"cpu_ms":244.44400000000144,"wall_ms":252.80638015829027},{"cpu_ms":239.9470000000008,"wall_ms":255.31571102328598},{"cpu_ms":263.18199999999337,"wall_ms":277.4976321961731},{"cpu_ms":248.27599999999705,"wall_ms":257.6367009896785},{"cpu_ms":211.08199999999755,"wall_ms":219.40496494062245},{"cpu_ms":219.3460000000016,"wall_ms":227.65866899862885},{"cpu_ms":229.87299999999777,"wall_ms":237.21490893512964},{"cpu_ms":232.39199999999727,"wall_ms":240.7633950933814},{"cpu_ms":233.41699999999577,"wall_ms":240.74517004191875},{"cpu_ms":221.60799999999625,"wall_ms":231.93025402724743},{"cpu_ms":206.707999999999,"wall_ms":217.07117720507085}],"after_median_cpu_ms":232.90449999999652,"before_control":[{"cpu_ms":257.1079999999988,"wall_ms":271.8147181440145},{"cpu_ms":228.8730000000001,"wall_ms":237.24161298014224},{"cpu_ms":244.52300000000093,"wall_ms":253.87318502180278},{"cpu_ms":247.55400000000094,"wall_ms":258.89457296580076},{"cpu_ms":264.4459999999995,"wall_ms":274.8353818897158},{"cpu_ms":211.23599999999954,"wall_ms":222.56986401043832},{"cpu_ms":215.4779999999974,"wall_ms":221.8216338660568},{"cpu_ms":232.81400000000474,"wall_ms":241.14774400368333},{"cpu_ms":224.3309999999994,"wall_ms":232.71911102347076},{"cpu_ms":230.13200000000467,"wall_ms":241.463904036209},{"cpu_ms":216.81900000000098,"wall_ms":225.12547997757792},{"cpu_ms":215.1269999999954,"wall_ms":223.42466795817018}],"before_control_median_cpu_ms":229.50250000000239,"after_control":[{"cpu_ms":259.57500000000164,"wall_ms":270.91650711372495},{"cpu_ms":277.9849999999975,"wall_ms":293.4468400198966},{"cpu_ms":218.7190000000001,"wall_ms":230.0413679331541},{"cpu_ms":269.9970000000036,"wall_ms":283.88479608111084},{"cpu_ms":202.82499999999715,"wall_ms":210.175956832245},{"cpu_ms":234.00200000000382,"wall_ms":242.33916401863098},{"cpu_ms":218.73099999999823,"wall_ms":224.06718297861516},{"cpu_ms":229.75999999999885,"wall_ms":238.117977976799},{"cpu_ms":224.97999999999507,"wall_ms":230.29466718435287},{"cpu_ms":221.47999999999968,"wall_ms":233.78063598647714},{"cpu_ms":214.69600000000355,"wall_ms":224.05243385583162},{"cpu_ms":221.39800000000065,"wall_ms":229.73504895344377}],"after_control_median_cpu_ms":223.22999999999737}, + {"name":"object_deep_clone","node_exit":0,"before":[{"cpu_ms":28.019999999997935,"wall_ms":34.42729008384049},{"cpu_ms":24.3290000000016,"wall_ms":31.55384585261345},{"cpu_ms":31.66999999999831,"wall_ms":39.014457957819104},{"cpu_ms":33.502000000005694,"wall_ms":38.849297910928726},{"cpu_ms":26.479999999999393,"wall_ms":29.890334932133555},{"cpu_ms":31.077000000003352,"wall_ms":33.42872206121683},{"cpu_ms":15.872000000001663,"wall_ms":21.16549899801612},{"cpu_ms":30.24099999999663,"wall_ms":34.652970964089036},{"cpu_ms":31.056999999997004,"wall_ms":36.45758586935699},{"cpu_ms":29.90899999999641,"wall_ms":36.2928279209882},{"cpu_ms":31.650000000006173,"wall_ms":36.002684850245714},{"cpu_ms":31.244000000000938,"wall_ms":34.64175993576646}],"before_median_cpu_ms":30.648999999996818,"after":[{"cpu_ms":27.39400000000103,"wall_ms":32.88097819313407},{"cpu_ms":31.006999999995344,"wall_ms":36.39054112136364},{"cpu_ms":31.956000000000984,"wall_ms":38.3410279173404},{"cpu_ms":15.292999999999779,"wall_ms":20.371243823319674},{"cpu_ms":24.279999999997415,"wall_ms":28.84948905557394},{"cpu_ms":31.48999999999802,"wall_ms":36.959955003112555},{"cpu_ms":18.05699999999888,"wall_ms":20.505605032667518},{"cpu_ms":28.387999999999636,"wall_ms":34.69315590336919},{"cpu_ms":32.62500000000301,"wall_ms":36.06779710389674},{"cpu_ms":30.66800000000569,"wall_ms":35.03599693067372},{"cpu_ms":27.849999999993713,"wall_ms":34.19843106530607},{"cpu_ms":27.37199999999973,"wall_ms":32.725245924666524}],"after_median_cpu_ms":28.118999999996674,"before_control":[{"cpu_ms":27.160999999999547,"wall_ms":35.59102490544319},{"cpu_ms":29.14599999999723,"wall_ms":33.536332892253995},{"cpu_ms":30.718000000000245,"wall_ms":38.06013287976384},{"cpu_ms":15.843000000003826,"wall_ms":21.19761984795332},{"cpu_ms":16.98499999999825,"wall_ms":22.890395019203424},{"cpu_ms":27.913999999995553,"wall_ms":33.29906007274985},{"cpu_ms":15.731000000002382,"wall_ms":23.722219979390502},{"cpu_ms":25.860999999999024,"wall_ms":33.22471980936825},{"cpu_ms":29.92100000000164,"wall_ms":36.29960003308952},{"cpu_ms":31.28600000000148,"wall_ms":35.661127185449004},{"cpu_ms":31.028999999996643,"wall_ms":33.351849066093564},{"cpu_ms":27.952999999996564,"wall_ms":33.34825090132654}],"before_control_median_cpu_ms":27.93349999999606,"after_control":[{"cpu_ms":26.343000000004224,"wall_ms":31.801990000531077},{"cpu_ms":28.55000000000274,"wall_ms":32.994218869134784},{"cpu_ms":24.059999999998638,"wall_ms":32.45149506255984},{"cpu_ms":14.950999999996384,"wall_ms":19.24192300066352},{"cpu_ms":28.56400000000292,"wall_ms":33.92126294784248},{"cpu_ms":27.534000000002834,"wall_ms":33.85498886927962},{"cpu_ms":15.25999999999783,"wall_ms":22.146163042634726},{"cpu_ms":26.054999999999495,"wall_ms":32.46426791884005},{"cpu_ms":32.11900000000156,"wall_ms":35.51579499617219},{"cpu_ms":30.94199999999603,"wall_ms":35.32267385162413},{"cpu_ms":28.410000000000935,"wall_ms":35.68214504048228},{"cpu_ms":32.05300000000477,"wall_ms":36.48812300525606}],"after_control_median_cpu_ms":27.972000000001884}, + {"name":"promise_all_chains","node_exit":0,"before":[{"cpu_ms":168.09699999999594,"wall_ms":173.48227091133595},{"cpu_ms":163.45299999999696,"wall_ms":169.84377708286047},{"cpu_ms":181.78200000000544,"wall_ms":188.18988418206573},{"cpu_ms":163.33099999999945,"wall_ms":169.68800500035286},{"cpu_ms":121.36400000000691,"wall_ms":126.67124415747821},{"cpu_ms":148.57200000000148,"wall_ms":156.9226118735969},{"cpu_ms":156.5000000000012,"wall_ms":159.86199909821153},{"cpu_ms":136.9940000000014,"wall_ms":143.56702705845237},{"cpu_ms":175.74099999999504,"wall_ms":180.10464194230735},{"cpu_ms":180.21699999999896,"wall_ms":186.63157592527568},{"cpu_ms":176.1109999999988,"wall_ms":179.44414704106748},{"cpu_ms":113.37800000000442,"wall_ms":117.78805102221668}],"before_median_cpu_ms":163.3919999999982,"after":[{"cpu_ms":173.27800000000337,"wall_ms":176.66136613115668},{"cpu_ms":167.0610000000039,"wall_ms":177.44558188132942},{"cpu_ms":124.35800000000086,"wall_ms":132.89792696014047},{"cpu_ms":163.25699999999443,"wall_ms":171.6630479786545},{"cpu_ms":172.20599999999564,"wall_ms":178.58361918479204},{"cpu_ms":168.84900000000158,"wall_ms":173.22689201682806},{"cpu_ms":97.44500000000045,"wall_ms":103.7867700215429},{"cpu_ms":99.49399999999997,"wall_ms":102.8511798940599},{"cpu_ms":99.3090000000052,"wall_ms":104.66830804944038},{"cpu_ms":176.7899999999969,"wall_ms":183.22130991145968},{"cpu_ms":130.47199999999748,"wall_ms":132.82120204530656},{"cpu_ms":156.88299999999344,"wall_ms":164.27842690609396}],"after_median_cpu_ms":160.06999999999394,"before_control":[{"cpu_ms":137.87500000000108,"wall_ms":143.228427041322},{"cpu_ms":130.38699999999892,"wall_ms":137.83365697599947},{"cpu_ms":126.3269999999963,"wall_ms":133.685840992257},{"cpu_ms":95.26699999999977,"wall_ms":102.56033693440259},{"cpu_ms":157.78599999999443,"wall_ms":169.24225096590817},{"cpu_ms":173.8240000000033,"wall_ms":179.20107301324606},{"cpu_ms":141.89399999999353,"wall_ms":144.2352479789406},{"cpu_ms":122.4330000000009,"wall_ms":127.81616416759789},{"cpu_ms":167.56700000000535,"wall_ms":176.93607695400715},{"cpu_ms":173.35400000000334,"wall_ms":177.75738309137523},{"cpu_ms":164.5200000000031,"wall_ms":167.86398086696863},{"cpu_ms":115.4900000000012,"wall_ms":122.07569810561836}],"before_control_median_cpu_ms":139.8844999999973,"after_control":[{"cpu_ms":128.71799999999922,"wall_ms":132.07548996433616},{"cpu_ms":136.22099999999904,"wall_ms":138.64293205551803},{"cpu_ms":162.12800000000271,"wall_ms":170.4962868243456},{"cpu_ms":129.62800000000385,"wall_ms":139.97740694321692},{"cpu_ms":168.82100000000122,"wall_ms":175.20455014891922},{"cpu_ms":169.23999999999495,"wall_ms":174.581924919039},{"cpu_ms":119.37600000000259,"wall_ms":125.68811397068202},{"cpu_ms":123.22799999999745,"wall_ms":128.5587321035564},{"cpu_ms":177.0829999999961,"wall_ms":180.88175496086478},{"cpu_ms":135.89199999999835,"wall_ms":145.05982887931168},{"cpu_ms":158.1290000000024,"wall_ms":165.61536700464785},{"cpu_ms":169.4620000000029,"wall_ms":175.83305295556784}],"after_control_median_cpu_ms":147.17500000000072}, + {"name":"regex_replace","node_exit":0,"before":[{"cpu_ms":2855.5489999999963,"wall_ms":2877.874721074477},{"cpu_ms":4045.152000000002,"wall_ms":4070.7125240005553},{"cpu_ms":3755.057999999991,"wall_ms":3772.513832198456},{"cpu_ms":3983.4149999999936,"wall_ms":4010.160663863644},{"cpu_ms":2572.2319999999995,"wall_ms":2588.7253950349987},{"cpu_ms":4010.064,"wall_ms":4036.532213911414},{"cpu_ms":2839.6619999999757,"wall_ms":2848.06353203021},{"cpu_ms":2798.291000000006,"wall_ms":2813.4410411585122},{"cpu_ms":2908.894000000004,"wall_ms":2919.2825129721314},{"cpu_ms":3442.6870000000063,"wall_ms":3468.043533852324},{"cpu_ms":3718.206000000009,"wall_ms":3731.65815602988},{"cpu_ms":3414.1509999999753,"wall_ms":3431.5359799657017}],"before_median_cpu_ms":3428.418999999991,"after":[{"cpu_ms":2438.916000000006,"wall_ms":2466.293543810025},{"cpu_ms":2554.7010000000087,"wall_ms":2569.108152994886},{"cpu_ms":3277.076000000008,"wall_ms":3287.9450239706784},{"cpu_ms":3384.726999999998,"wall_ms":3400.134628871456},{"cpu_ms":3692.5549999999985,"wall_ms":3710.980199975893},{"cpu_ms":2667.166000000009,"wall_ms":2683.658191934228},{"cpu_ms":2897.0520000000024,"wall_ms":2910.589839098975},{"cpu_ms":2781.585000000007,"wall_ms":2793.05969690904},{"cpu_ms":2553.6789999999883,"wall_ms":2578.836136031896},{"cpu_ms":3383.0049999999974,"wall_ms":3406.3771858345717},{"cpu_ms":2496.6160000000173,"wall_ms":2517.0485770795494},{"cpu_ms":3381.957,"wall_ms":3398.399795871228}],"after_median_cpu_ms":2839.318500000005,"before_control":[{"cpu_ms":2556.9129999999946,"wall_ms":2579.3296669144183},{"cpu_ms":3535.0660000000007,"wall_ms":3573.4850810840726},{"cpu_ms":3964.9220000000014,"wall_ms":3989.638190018013},{"cpu_ms":3570.838999999992,"wall_ms":3587.3129691462964},{"cpu_ms":3954.9049999999966,"wall_ms":3971.2698680814356},{"cpu_ms":4439.834999999988,"wall_ms":4471.245536813512},{"cpu_ms":2948.403000000013,"wall_ms":2961.7723838891834},{"cpu_ms":2892.579000000012,"wall_ms":2910.9936910681427},{"cpu_ms":3411.290000000008,"wall_ms":3425.658468157053},{"cpu_ms":2753.6589999999987,"wall_ms":2769.98502202332},{"cpu_ms":2517.667999999986,"wall_ms":2541.01053904742},{"cpu_ms":2951.347999999996,"wall_ms":2964.7275381721556}],"before_control_median_cpu_ms":3181.3190000000022,"after_control":[{"cpu_ms":3608.875000000012,"wall_ms":3647.9470510967076},{"cpu_ms":2394.8629999999866,"wall_ms":2415.2865801006556},{"cpu_ms":3311.1559999999968,"wall_ms":3330.2912968210876},{"cpu_ms":3336.3500000000104,"wall_ms":3359.6692979335785},{"cpu_ms":2881.8920000000217,"wall_ms":2900.299376109615},{"cpu_ms":3083.6960000000036,"wall_ms":3101.3359939679503},{"cpu_ms":3586.628999999988,"wall_ms":3604.040364967659},{"cpu_ms":2840.2079999999896,"wall_ms":2856.5181768499315},{"cpu_ms":2850.6849999999986,"wall_ms":2871.077948017046},{"cpu_ms":2441.3979999999924,"wall_ms":2464.830565964803},{"cpu_ms":3217.3200000000006,"wall_ms":3229.7044249717146},{"cpu_ms":2742.881000000011,"wall_ms":2753.301765071228}],"after_control_median_cpu_ms":2982.7940000000126}, + {"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":48.10000000000514,"wall_ms":54.52924594283104},{"cpu_ms":46.87199999997915,"wall_ms":54.3440799228847},{"cpu_ms":43.546999999989566,"wall_ms":52.82938783057034},{"cpu_ms":48.270000000002256,"wall_ms":52.70729004405439},{"cpu_ms":45.700999999979786,"wall_ms":51.02378292940557},{"cpu_ms":47.18699999997966,"wall_ms":52.622511982917786},{"cpu_ms":45.95699999998715,"wall_ms":52.33115702867508},{"cpu_ms":46.3890000000049,"wall_ms":53.73684503138065},{"cpu_ms":44.709000000011656,"wall_ms":52.66516120173037},{"cpu_ms":44.24800000001028,"wall_ms":52.72203707136214},{"cpu_ms":47.49599999999532,"wall_ms":52.86477389745414},{"cpu_ms":49.11900000001879,"wall_ms":51.51506094262004}],"before_median_cpu_ms":46.630499999992026,"after":[{"cpu_ms":44.150999999999385,"wall_ms":53.354972042143345},{"cpu_ms":46.837000000010676,"wall_ms":54.210189962759614},{"cpu_ms":47.60500000000434,"wall_ms":53.955283015966415},{"cpu_ms":50.9270000000015,"wall_ms":55.342365987598896},{"cpu_ms":47.8790000000231,"wall_ms":54.263880010694265},{"cpu_ms":47.62800000000311,"wall_ms":51.976965041831136},{"cpu_ms":48.96200000001727,"wall_ms":53.3303648699075},{"cpu_ms":34.166999999996506,"wall_ms":39.57916586659849},{"cpu_ms":41.11599999998816,"wall_ms":48.483734019100666},{"cpu_ms":45.97599999999602,"wall_ms":52.35109385102987},{"cpu_ms":45.22499999998786,"wall_ms":53.67468809708953},{"cpu_ms":46.86399999999935,"wall_ms":51.25076603144407}],"after_median_cpu_ms":46.85050000000501,"before_control":[{"cpu_ms":47.227999999989834,"wall_ms":53.674388909712434},{"cpu_ms":48.041999999981044,"wall_ms":52.4356528185308},{"cpu_ms":47.60400000000686,"wall_ms":56.917933048680425},{"cpu_ms":49.79799999998136,"wall_ms":52.239175187423825},{"cpu_ms":48.61200000001986,"wall_ms":52.008574130013585},{"cpu_ms":48.32400000000803,"wall_ms":53.69615904055536},{"cpu_ms":48.25800000000413,"wall_ms":52.75615188293159},{"cpu_ms":35.67100000000778,"wall_ms":40.08824797347188},{"cpu_ms":43.87800000000652,"wall_ms":53.23832295835018},{"cpu_ms":45.71699999999623,"wall_ms":51.07683199457824},{"cpu_ms":47.048000000017964,"wall_ms":51.41988303512335},{"cpu_ms":42.982999999992444,"wall_ms":51.40540609136224}],"before_control_median_cpu_ms":47.41599999999835,"after_control":[{"cpu_ms":48.15600000000586,"wall_ms":53.58645389787853},{"cpu_ms":49.77600000000848,"wall_ms":54.13830606266856},{"cpu_ms":45.000000000015916,"wall_ms":53.22934594005346},{"cpu_ms":48.38399999999865,"wall_ms":52.84362495876849},{"cpu_ms":47.64299999999366,"wall_ms":54.127485025674105},{"cpu_ms":50.74799999999868,"wall_ms":53.147663129493594},{"cpu_ms":47.875000000004775,"wall_ms":52.30660107918084},{"cpu_ms":31.132999999982758,"wall_ms":33.43551978468895},{"cpu_ms":46.355000000005475,"wall_ms":51.74835701473057},{"cpu_ms":49.63699999998994,"wall_ms":54.05116197653115},{"cpu_ms":47.95999999998912,"wall_ms":53.4396490547806},{"cpu_ms":43.81799999998748,"wall_ms":52.191945957019925}],"after_control_median_cpu_ms":47.91749999999695}, + {"name":"string_split_map_join","node_exit":0,"before":[{"cpu_ms":277.132000000023,"wall_ms":283.5390930995345},{"cpu_ms":276.9519999999943,"wall_ms":282.35352598130703},{"cpu_ms":317.7909999999997,"wall_ms":325.16802893951535},{"cpu_ms":319.11199999998985,"wall_ms":323.49205599166453},{"cpu_ms":316.552999999999,"wall_ms":324.9520440585911},{"cpu_ms":220.7209999999975,"wall_ms":226.13399801775813},{"cpu_ms":252.37200000000826,"wall_ms":258.8004700373858},{"cpu_ms":319.6930000000009,"wall_ms":325.1398641150445},{"cpu_ms":306.57999999999674,"wall_ms":312.99490691162646},{"cpu_ms":306.3510000000065,"wall_ms":312.24055611528456},{"cpu_ms":271.3939999999866,"wall_ms":278.73099804855883},{"cpu_ms":287.94100000001777,"wall_ms":295.3894408419728}],"before_median_cpu_ms":297.1460000000121,"after":[{"cpu_ms":295.3119999999956,"wall_ms":300.739070167765},{"cpu_ms":299.0440000000092,"wall_ms":321.26074586994946},{"cpu_ms":319.4660000000056,"wall_ms":324.8755831737071},{"cpu_ms":319.5760000000121,"wall_ms":325.9943041484803},{"cpu_ms":324.3970000000047,"wall_ms":327.8083340264857},{"cpu_ms":189.83499999998799,"wall_ms":197.2265550866723},{"cpu_ms":247.3449999999957,"wall_ms":254.4023459777236},{"cpu_ms":233.3449999999857,"wall_ms":243.60400903970003},{"cpu_ms":307.4000000000012,"wall_ms":314.8583909496665},{"cpu_ms":312.8340000000094,"wall_ms":321.2234601378441},{"cpu_ms":303.17099999999186,"wall_ms":311.56420009210706},{"cpu_ms":254.13299999999595,"wall_ms":259.55161009915173}],"after_median_cpu_ms":301.1075000000005,"before_control":[{"cpu_ms":283.64799999999946,"wall_ms":290.0219629518688},{"cpu_ms":277.26200000000745,"wall_ms":285.68790992721915},{"cpu_ms":247.55400000000805,"wall_ms":252.98583204858005},{"cpu_ms":315.29999999997926,"wall_ms":321.70404214411974},{"cpu_ms":193.89599999999518,"wall_ms":201.3058108277619},{"cpu_ms":319.5389999999918,"wall_ms":325.9425659198314},{"cpu_ms":233.21699999999623,"wall_ms":237.42512706667185},{"cpu_ms":309.2489999999941,"wall_ms":315.6403230968863},{"cpu_ms":243.90399999998635,"wall_ms":248.31370217725635},{"cpu_ms":220.1329999999757,"wall_ms":225.52495799027383},{"cpu_ms":301.3580000000218,"wall_ms":308.73775691725314},{"cpu_ms":235.93600000000947,"wall_ms":241.3333710283041}],"before_control_median_cpu_ms":262.40800000000775,"after_control":[{"cpu_ms":288.83399999998005,"wall_ms":297.24887502379715},{"cpu_ms":277.47600000000716,"wall_ms":281.1468599829823},{"cpu_ms":299.051999999989,"wall_ms":306.47115502506495},{"cpu_ms":446.35900000000106,"wall_ms":452.77104899287224},{"cpu_ms":323.89700000001653,"wall_ms":328.3246390055865},{"cpu_ms":321.47000000000503,"wall_ms":326.8913389183581},{"cpu_ms":247.22400000001699,"wall_ms":251.6191741451621},{"cpu_ms":309.4090000000165,"wall_ms":316.7992199305445},{"cpu_ms":241.09700000002476,"wall_ms":253.61811998300254},{"cpu_ms":192.5990000000013,"wall_ms":202.41011399775743},{"cpu_ms":292.8519999999821,"wall_ms":298.2376560103148},{"cpu_ms":302.3719999999912,"wall_ms":310.80914894118905}],"after_control_median_cpu_ms":295.95199999998556}, + {"name":"string_template_interp","node_exit":0,"before":[{"cpu_ms":74.68900000000644,"wall_ms":81.08848705887794},{"cpu_ms":80.6180000000154,"wall_ms":88.09479582123458},{"cpu_ms":80.03800000000183,"wall_ms":86.40684001147747},{"cpu_ms":77.69300000001067,"wall_ms":87.06887788139284},{"cpu_ms":62.362000000007356,"wall_ms":68.78954404965043},{"cpu_ms":58.188999999998714,"wall_ms":64.60794596932828},{"cpu_ms":71.21599999999262,"wall_ms":78.55842704884708},{"cpu_ms":72.5439999999935,"wall_ms":79.06940206885338},{"cpu_ms":77.40800000001968,"wall_ms":84.78291402570903},{"cpu_ms":80.5949999999882,"wall_ms":88.0055888555944},{"cpu_ms":78.676999999999,"wall_ms":87.02316088601947},{"cpu_ms":71.44099999999298,"wall_ms":76.73835405148566}],"before_median_cpu_ms":76.04850000001306,"after":[{"cpu_ms":62.27099999998131,"wall_ms":69.66169411316514},{"cpu_ms":82.55099999999516,"wall_ms":85.93985694460571},{"cpu_ms":76.71799999999962,"wall_ms":85.20653611049056},{"cpu_ms":78.32299999998327,"wall_ms":84.74899991415441},{"cpu_ms":59.938999999985754,"wall_ms":70.29836485162377},{"cpu_ms":76.44100000001686,"wall_ms":84.15134297683835},{"cpu_ms":47.988000000003694,"wall_ms":56.64576101116836},{"cpu_ms":68.73100000001386,"wall_ms":76.06278103776276},{"cpu_ms":53.021000000001095,"wall_ms":60.388166923075914},{"cpu_ms":80.51900000000956,"wall_ms":86.9375120382756},{"cpu_ms":79.50099999999338,"wall_ms":84.90521204657853},{"cpu_ms":67.77300000001674,"wall_ms":79.11499706096947}],"after_median_cpu_ms":72.58600000001536,"before_control":[{"cpu_ms":76.69000000001347,"wall_ms":83.13258900307119},{"cpu_ms":74.86299999999346,"wall_ms":82.26569811813533},{"cpu_ms":77.04999999998563,"wall_ms":85.41789092123508},{"cpu_ms":51.577000000008866,"wall_ms":55.81866484135389},{"cpu_ms":78.14999999999372,"wall_ms":86.57624595798552},{"cpu_ms":73.50700000000643,"wall_ms":81.3867449760437},{"cpu_ms":61.70499999998924,"wall_ms":69.06490796245635},{"cpu_ms":53.741999999999734,"wall_ms":60.11311314068735},{"cpu_ms":73.31600000000549,"wall_ms":82.00971898622811},{"cpu_ms":81.55300000001375,"wall_ms":88.92380516044796},{"cpu_ms":83.62999999999943,"wall_ms":89.00786191225052},{"cpu_ms":72.4779999999896,"wall_ms":80.81616787239909}],"before_control_median_cpu_ms":74.18499999999995,"after_control":[{"cpu_ms":72.36599999998816,"wall_ms":79.80404607951641},{"cpu_ms":60.11200000000372,"wall_ms":69.22477786429226},{"cpu_ms":80.5430000000058,"wall_ms":85.89162700809538},{"cpu_ms":76.92600000001448,"wall_ms":84.29094403982162},{"cpu_ms":76.6619999999989,"wall_ms":84.99301713891327},{"cpu_ms":57.87899999998558,"wall_ms":67.24567781202495},{"cpu_ms":72.05500000000598,"wall_ms":82.46287610381842},{"cpu_ms":79.38099999998371,"wall_ms":84.71703995019197},{"cpu_ms":81.03999999997313,"wall_ms":87.45724381878972},{"cpu_ms":78.27299999999582,"wall_ms":86.72828110866249},{"cpu_ms":80.2240000000154,"wall_ms":86.61907585337758},{"cpu_ms":71.45499999998606,"wall_ms":74.83964413404465}],"after_control_median_cpu_ms":76.79400000000669} +] diff --git a/benchmarks/regexp-construction/current-json-control-samples.json b/benchmarks/regexp-construction/current-json-control-samples.json new file mode 100644 index 0000000000..f49dc914c3 --- /dev/null +++ b/benchmarks/regexp-construction/current-json-control-samples.json @@ -0,0 +1,3 @@ +[ + {"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":75.27400000000029,"wall_ms":83.69916398078203},{"cpu_ms":70.64700000000013,"wall_ms":77.92796101421118},{"cpu_ms":63.07499999999999,"wall_ms":70.48167800530791},{"cpu_ms":83.97200000000015,"wall_ms":86.3643039483577},{"cpu_ms":63.56599999999979,"wall_ms":70.96817693673074},{"cpu_ms":85.07700000000007,"wall_ms":95.45938693918288},{"cpu_ms":87.88899999999967,"wall_ms":92.29004592634737},{"cpu_ms":77.16000000000011,"wall_ms":83.61725090071559},{"cpu_ms":86.18199999999999,"wall_ms":90.52035887725651},{"cpu_ms":77.50999999999931,"wall_ms":87.01839600689709},{"cpu_ms":69.94400000000044,"wall_ms":76.27944205887616},{"cpu_ms":89.74100000000007,"wall_ms":96.14676120691001},{"cpu_ms":81.73299999999983,"wall_ms":89.16908386163414},{"cpu_ms":86.88699999999994,"wall_ms":91.29243181087077},{"cpu_ms":77.27799999999974,"wall_ms":83.74078199267387},{"cpu_ms":76.99299999999987,"wall_ms":80.43685904704034},{"cpu_ms":77.98000000000016,"wall_ms":85.35203197970986},{"cpu_ms":81.87399999999911,"wall_ms":85.82231192849576},{"cpu_ms":82.99500000000037,"wall_ms":91.41636290587485},{"cpu_ms":85.14800000000022,"wall_ms":90.614726068452},{"cpu_ms":88.64200000000011,"wall_ms":92.07723801955581},{"cpu_ms":87.06300000000056,"wall_ms":92.47597306966782},{"cpu_ms":84.53299999999864,"wall_ms":97.90404397062957},{"cpu_ms":88.16500000000005,"wall_ms":95.57511401362717},{"cpu_ms":89.02199999999993,"wall_ms":95.43279698118567},{"cpu_ms":85.32099999999865,"wall_ms":93.0956699885428},{"cpu_ms":87.51700000000007,"wall_ms":97.96925587579608},{"cpu_ms":64.70700000000029,"wall_ms":69.08948696218431},{"cpu_ms":88.53000000000044,"wall_ms":96.78435302339494},{"cpu_ms":88.0510000000001,"wall_ms":95.86937306448817},{"cpu_ms":86.7369999999994,"wall_ms":96.15907515399158},{"cpu_ms":86.64899999999953,"wall_ms":95.18246911466122},{"cpu_ms":86.09699999999876,"wall_ms":94.52143404632807},{"cpu_ms":91.0189999999993,"wall_ms":95.38686112500727},{"cpu_ms":81.25699999999902,"wall_ms":90.00393399037421},{"cpu_ms":87.71600000000035,"wall_ms":96.13499906845391},{"cpu_ms":86.74800000000005,"wall_ms":95.16088897362351},{"cpu_ms":89.48299999999954,"wall_ms":95.8537741098553},{"cpu_ms":89.74199999999932,"wall_ms":94.15055089630187},{"cpu_ms":89.24799999999955,"wall_ms":96.64914011955261},{"cpu_ms":90.84299999999956,"wall_ms":96.24128811992705},{"cpu_ms":61.46400000000085,"wall_ms":68.83564312011003},{"cpu_ms":65.43800000000033,"wall_ms":75.7626760751009},{"cpu_ms":51.80700000000016,"wall_ms":58.11891984194517},{"cpu_ms":86.2569999999998,"wall_ms":90.6476469244808},{"cpu_ms":84.1220000000007,"wall_ms":95.82703397609293},{"cpu_ms":86.00799999999964,"wall_ms":92.39753684960306},{"cpu_ms":82.97500000000113,"wall_ms":88.30125187523663},{"cpu_ms":89.47400000000272,"wall_ms":93.870067037642},{"cpu_ms":91.10700000000094,"wall_ms":95.86507501080632},{"cpu_ms":88.52500000000063,"wall_ms":95.93325317837298},{"cpu_ms":81.45000000000024,"wall_ms":93.94320496357977},{"cpu_ms":85.96100000000106,"wall_ms":94.37078307382762},{"cpu_ms":89.90800000000121,"wall_ms":96.31164884194732},{"cpu_ms":90.67500000000095,"wall_ms":97.70557400770485},{"cpu_ms":71.37000000000171,"wall_ms":74.69350891187787},{"cpu_ms":83.52299999999957,"wall_ms":92.94393798336387},{"cpu_ms":53.024000000000626,"wall_ms":57.782360818237066},{"cpu_ms":90.46200000000226,"wall_ms":93.87884405441582},{"cpu_ms":82.81900000000064,"wall_ms":90.2649920899421},{"cpu_ms":86.46500000000046,"wall_ms":92.89477509446442},{"cpu_ms":81.75999999999917,"wall_ms":92.75827114470303},{"cpu_ms":85.30000000000015,"wall_ms":93.30386901274323},{"cpu_ms":83.4210000000013,"wall_ms":91.75843209959567},{"cpu_ms":84.19500000000113,"wall_ms":93.62338692881167},{"cpu_ms":88.3819999999993,"wall_ms":93.81829085759819},{"cpu_ms":85.42800000000028,"wall_ms":92.8552309051156},{"cpu_ms":88.5809999999978,"wall_ms":92.95925591140985},{"cpu_ms":88.63099999999946,"wall_ms":91.04027994908392},{"cpu_ms":85.63399999999888,"wall_ms":92.99437189474702},{"cpu_ms":89.58099999999902,"wall_ms":93.9739621244371},{"cpu_ms":86.04300000000009,"wall_ms":91.45067702047527},{"cpu_ms":84.35799999999816,"wall_ms":91.69831988401711},{"cpu_ms":88.86299999999991,"wall_ms":93.2265049777925},{"cpu_ms":85.79399999999993,"wall_ms":93.18211185745895},{"cpu_ms":79.23599999999809,"wall_ms":85.62013390474021},{"cpu_ms":81.39099999999999,"wall_ms":86.86657203361392},{"cpu_ms":87.00999999999937,"wall_ms":93.43151818029583},{"cpu_ms":86.89099999999783,"wall_ms":93.27991493046284},{"cpu_ms":84.36100000000124,"wall_ms":91.7710259091109},{"cpu_ms":84.15400000000162,"wall_ms":89.58840812556446},{"cpu_ms":77.04499999999825,"wall_ms":82.47650996781886},{"cpu_ms":88.1079999999983,"wall_ms":92.49879606068134},{"cpu_ms":91.94199999999952,"wall_ms":95.40316183120012},{"cpu_ms":86.31600000000006,"wall_ms":95.67028097808361},{"cpu_ms":91.45499999999984,"wall_ms":96.84353298507631},{"cpu_ms":90.0880000000015,"wall_ms":94.5136200170964},{"cpu_ms":90.45000000000059,"wall_ms":93.87963498011231},{"cpu_ms":54.89799999999789,"wall_ms":60.24398002773523},{"cpu_ms":90.09800000000112,"wall_ms":98.52908295579255},{"cpu_ms":89.5360000000025,"wall_ms":95.9065321367234},{"cpu_ms":89.81299999999948,"wall_ms":96.23563685454428},{"cpu_ms":90.98699999999837,"wall_ms":96.44657094031572},{"cpu_ms":87.38899999999461,"wall_ms":93.83515198715031},{"cpu_ms":86.37399999999928,"wall_ms":94.76711391471326},{"cpu_ms":83.77700000000488,"wall_ms":94.18859309516847},{"cpu_ms":87.88499999999999,"wall_ms":96.29268385469913},{"cpu_ms":90.62600000000032,"wall_ms":98.00358093343675},{"cpu_ms":86.45500000000084,"wall_ms":95.82724515348673},{"cpu_ms":87.71999999999736,"wall_ms":95.08592891506851}],"before_median_cpu_ms":86.2194999999999,"after":[{"cpu_ms":75.38899999999992,"wall_ms":79.75847087800503},{"cpu_ms":81.70399999999978,"wall_ms":90.09690792299807},{"cpu_ms":79.80900000000003,"wall_ms":89.49999208562076},{"cpu_ms":80.12400000000008,"wall_ms":88.487952016294},{"cpu_ms":80.12500000000023,"wall_ms":84.52667109668255},{"cpu_ms":86.41200000000015,"wall_ms":102.83268289640546},{"cpu_ms":71.53900000000047,"wall_ms":82.9825249966234},{"cpu_ms":82.61899999999933,"wall_ms":90.92009603045881},{"cpu_ms":83.8840000000003,"wall_ms":91.27641189843416},{"cpu_ms":65.20400000000049,"wall_ms":76.77278178744018},{"cpu_ms":72.05700000000004,"wall_ms":82.39017915911973},{"cpu_ms":83.77500000000015,"wall_ms":90.21965716965497},{"cpu_ms":82.73200000000003,"wall_ms":89.18799995444715},{"cpu_ms":85.72499999999917,"wall_ms":89.13657395169139},{"cpu_ms":83.56700000000039,"wall_ms":91.99028601869941},{"cpu_ms":65.41999999999959,"wall_ms":72.7238180115819},{"cpu_ms":77.85499999999956,"wall_ms":86.99648501351476},{"cpu_ms":82.83100000000054,"wall_ms":89.73821718245745},{"cpu_ms":88.73099999999923,"wall_ms":93.19818299263716},{"cpu_ms":85.24799999999999,"wall_ms":92.60245016776025},{"cpu_ms":81.7420000000002,"wall_ms":92.23285899497569},{"cpu_ms":85.86599999999933,"wall_ms":93.25870615430176},{"cpu_ms":91.03900000000031,"wall_ms":95.44938779436052},{"cpu_ms":85.54499999999976,"wall_ms":90.9168201033026},{"cpu_ms":87.95499999999912,"wall_ms":95.35548300482333},{"cpu_ms":86.6579999999999,"wall_ms":106.37740604579449},{"cpu_ms":85.45399999999859,"wall_ms":91.91961283795536},{"cpu_ms":84.38000000000123,"wall_ms":90.78490291722119},{"cpu_ms":92.19799999999978,"wall_ms":96.30213188938797},{"cpu_ms":90.09,"wall_ms":95.57494288310409},{"cpu_ms":88.13000000000137,"wall_ms":94.54043000005186},{"cpu_ms":90.38900000000005,"wall_ms":95.78220988623798},{"cpu_ms":87.86500000000075,"wall_ms":94.54521909356117},{"cpu_ms":89.7079999999999,"wall_ms":96.86716692522168},{"cpu_ms":93.59099999999998,"wall_ms":98.04558008909225},{"cpu_ms":89.54400000000007,"wall_ms":96.22800396755338},{"cpu_ms":84.74499999999985,"wall_ms":95.25129897519946},{"cpu_ms":85.69300000000091,"wall_ms":95.05283692851663},{"cpu_ms":90.30000000000094,"wall_ms":96.75116091966629},{"cpu_ms":90.49599999999991,"wall_ms":95.92365496791899},{"cpu_ms":51.49800000000049,"wall_ms":56.90341908484697},{"cpu_ms":49.551999999998486,"wall_ms":57.818338042125106},{"cpu_ms":52.06799999999845,"wall_ms":55.39339687675238},{"cpu_ms":51.41100000000165,"wall_ms":60.656761983409524},{"cpu_ms":88.03999999999945,"wall_ms":93.45119493082166},{"cpu_ms":84.0209999999999,"wall_ms":94.4582368247211},{"cpu_ms":63.016999999998546,"wall_ms":69.87594696693122},{"cpu_ms":82.40999999999943,"wall_ms":88.80113693885505},{"cpu_ms":79.14899999999747,"wall_ms":87.55889581516385},{"cpu_ms":88.55700000000155,"wall_ms":95.99762293510139},{"cpu_ms":88.34500000000034,"wall_ms":94.78455688804388},{"cpu_ms":82.0369999999997,"wall_ms":91.46924200467765},{"cpu_ms":85.53799999999967,"wall_ms":95.02003714442253},{"cpu_ms":88.20300000000003,"wall_ms":96.64340014569461},{"cpu_ms":91.20099999999809,"wall_ms":96.62029705941677},{"cpu_ms":53.85599999999968,"wall_ms":57.27359978482127},{"cpu_ms":52.69700000000199,"wall_ms":61.67098693549633},{"cpu_ms":62.8440000000019,"wall_ms":70.20084396936},{"cpu_ms":75.8340000000004,"wall_ms":83.95580411888659},{"cpu_ms":84.4459999999998,"wall_ms":92.93769602663815},{"cpu_ms":82.17600000000047,"wall_ms":92.62385894544423},{"cpu_ms":86.85099999999935,"wall_ms":93.3243881445378},{"cpu_ms":87.7139999999983,"wall_ms":94.12901103496552},{"cpu_ms":81.67399999999958,"wall_ms":92.25642285309732},{"cpu_ms":83.12600000000003,"wall_ms":91.5877630468458},{"cpu_ms":84.90600000000015,"wall_ms":90.32749803736806},{"cpu_ms":85.9109999999994,"wall_ms":91.30084700882435},{"cpu_ms":84.60700000000188,"wall_ms":91.9840531423688},{"cpu_ms":82.69500000000107,"wall_ms":94.55357398837805},{"cpu_ms":87.74599999999921,"wall_ms":91.33303700946271},{"cpu_ms":84.11800000000014,"wall_ms":91.52125893160701},{"cpu_ms":87.08800000000139,"wall_ms":92.49033010564744},{"cpu_ms":59.01500000000226,"wall_ms":64.08917484804988},{"cpu_ms":83.70199999999883,"wall_ms":90.11106519028544},{"cpu_ms":85.58400000000077,"wall_ms":92.0155521016568},{"cpu_ms":83.4670000000024,"wall_ms":89.3709808588028},{"cpu_ms":80.34399999999664,"wall_ms":86.72636095434427},{"cpu_ms":81.04700000000165,"wall_ms":86.4688700530678},{"cpu_ms":85.07300000000129,"wall_ms":91.51342394761741},{"cpu_ms":87.88299999999793,"wall_ms":92.81634818762541},{"cpu_ms":90.55000000000035,"wall_ms":95.92308406718075},{"cpu_ms":75.63400000000087,"wall_ms":81.99308603070676},{"cpu_ms":78.0990000000017,"wall_ms":86.93519001826644},{"cpu_ms":88.47699999999747,"wall_ms":96.54987487010658},{"cpu_ms":86.31100000000202,"wall_ms":94.75045301951468},{"cpu_ms":80.7140000000004,"wall_ms":85.08256892673671},{"cpu_ms":86.7520000000006,"wall_ms":93.7962899915874},{"cpu_ms":90.33200000000008,"wall_ms":97.84721909090877},{"cpu_ms":50.9030000000017,"wall_ms":57.2076179087162},{"cpu_ms":89.14000000000044,"wall_ms":95.56024591438472},{"cpu_ms":90.719,"wall_ms":97.46450302191079},{"cpu_ms":89.2910000000029,"wall_ms":96.65250708349049},{"cpu_ms":90.52199999999999,"wall_ms":95.9284130949527},{"cpu_ms":88.54500000000343,"wall_ms":95.97252612002194},{"cpu_ms":87.15499999999565,"wall_ms":92.5655800383538},{"cpu_ms":84.44800000000185,"wall_ms":92.78154303319752},{"cpu_ms":88.43699999999899,"wall_ms":95.82782606594265},{"cpu_ms":87.22900000000067,"wall_ms":92.62464102357626},{"cpu_ms":89.2470000000003,"wall_ms":92.68419188447297},{"cpu_ms":88.64100000000263,"wall_ms":95.07254487834871}],"after_median_cpu_ms":84.98950000000072,"before_control":[{"cpu_ms":79.40399999999981,"wall_ms":84.91239114664495},{"cpu_ms":73.69700000000012,"wall_ms":78.06848408654332},{"cpu_ms":80.69499999999996,"wall_ms":87.0546130463481},{"cpu_ms":86.3879999999999,"wall_ms":90.81982797943056},{"cpu_ms":88.64600000000023,"wall_ms":99.030229030177},{"cpu_ms":68.2529999999999,"wall_ms":77.79782800935209},{"cpu_ms":71.62999999999985,"wall_ms":76.18714799173176},{"cpu_ms":86.73199999999959,"wall_ms":94.18856306001544},{"cpu_ms":80.99299999999943,"wall_ms":87.45379885658622},{"cpu_ms":74.20199999999966,"wall_ms":77.57201697677374},{"cpu_ms":83.81699999999981,"wall_ms":97.47376083396375},{"cpu_ms":85.60199999999973,"wall_ms":92.9360929876566},{"cpu_ms":82.10400000000018,"wall_ms":90.51359607838094},{"cpu_ms":82.49999999999957,"wall_ms":92.24449004977942},{"cpu_ms":84.0550000000002,"wall_ms":88.49007496610284},{"cpu_ms":78.26899999999969,"wall_ms":85.57082084007561},{"cpu_ms":73.93699999999993,"wall_ms":79.31514293886721},{"cpu_ms":83.00299999999972,"wall_ms":91.4068641141057},{"cpu_ms":83.9970000000001,"wall_ms":93.50080788135529},{"cpu_ms":85.61000000000085,"wall_ms":91.9611610006541},{"cpu_ms":89.91799999999905,"wall_ms":92.32120402157307},{"cpu_ms":88.36400000000033,"wall_ms":92.78984903357923},{"cpu_ms":86.65000000000056,"wall_ms":92.60945278219879},{"cpu_ms":85.6760000000012,"wall_ms":95.0396629050374},{"cpu_ms":90.08600000000122,"wall_ms":96.19725588709116},{"cpu_ms":82.99200000000084,"wall_ms":88.90571212396026},{"cpu_ms":86.38600000000096,"wall_ms":92.39332913421094},{"cpu_ms":85.92400000000033,"wall_ms":93.19446492008865},{"cpu_ms":86.26299999999887,"wall_ms":96.52575897052884},{"cpu_ms":88.50600000000064,"wall_ms":95.07224382832646},{"cpu_ms":86.80999999999983,"wall_ms":94.34734913520515},{"cpu_ms":86.05799999999952,"wall_ms":94.46310601197183},{"cpu_ms":89.54800000000063,"wall_ms":95.97354801371694},{"cpu_ms":80.65400000000089,"wall_ms":86.01140487007797},{"cpu_ms":90.5250000000013,"wall_ms":95.47166991978884},{"cpu_ms":89.144000000001,"wall_ms":95.49669688567519},{"cpu_ms":87.80599999999872,"wall_ms":96.25871106982231},{"cpu_ms":85.00700000000094,"wall_ms":95.43488104827702},{"cpu_ms":89.01700000000012,"wall_ms":95.41924204677343},{"cpu_ms":84.25699999999914,"wall_ms":92.58273197337985},{"cpu_ms":82.87400000000034,"wall_ms":94.27232900634408},{"cpu_ms":85.98900000000143,"wall_ms":96.39926301315427},{"cpu_ms":52.56400000000028,"wall_ms":60.00365107320249},{"cpu_ms":86.80999999999983,"wall_ms":91.21404495090246},{"cpu_ms":84.51200000000014,"wall_ms":92.94861601665616},{"cpu_ms":89.90400000000065,"wall_ms":93.30997080542147},{"cpu_ms":88.6490000000002,"wall_ms":93.0974839720875},{"cpu_ms":78.90100000000189,"wall_ms":86.3104029558599},{"cpu_ms":88.23999999999899,"wall_ms":94.64019606821239},{"cpu_ms":86.93799999999996,"wall_ms":94.4152669981122},{"cpu_ms":86.41599999999983,"wall_ms":95.82109190523624},{"cpu_ms":85.23599999999831,"wall_ms":94.66634597629309},{"cpu_ms":82.11299999999966,"wall_ms":91.53476497158408},{"cpu_ms":91.30000000000038,"wall_ms":96.72333905473351},{"cpu_ms":91.5859999999995,"wall_ms":97.01816993765533},{"cpu_ms":66.35299999999944,"wall_ms":73.68674594908953},{"cpu_ms":84.2749999999981,"wall_ms":92.69620501436293},{"cpu_ms":80.74200000000076,"wall_ms":87.12782012298703},{"cpu_ms":88.40099999999751,"wall_ms":93.78397697582841},{"cpu_ms":87.91899999999941,"wall_ms":92.32556191273034},{"cpu_ms":81.46800000000098,"wall_ms":93.04297203198075},{"cpu_ms":81.58900000000102,"wall_ms":94.0121728926897},{"cpu_ms":86.83600000000169,"wall_ms":92.23395003937185},{"cpu_ms":88.76799999999818,"wall_ms":94.68244598247111},{"cpu_ms":86.76799999999929,"wall_ms":90.20968899130821},{"cpu_ms":81.23900000000006,"wall_ms":90.2932349126786},{"cpu_ms":88.70600000000195,"wall_ms":94.14808684960008},{"cpu_ms":80.24599999999893,"wall_ms":90.9026840236038},{"cpu_ms":84.99699999999777,"wall_ms":91.93657501600683},{"cpu_ms":86.10499999999988,"wall_ms":90.52146109752357},{"cpu_ms":85.4820000000025,"wall_ms":91.96045901626348},{"cpu_ms":70.95899999999844,"wall_ms":76.41486497595906},{"cpu_ms":87.95300000000239,"wall_ms":92.3811758402735},{"cpu_ms":85.71699999999893,"wall_ms":92.09929988719523},{"cpu_ms":81.7610000000002,"wall_ms":90.24301008321345},{"cpu_ms":82.46700000000118,"wall_ms":90.82952584140003},{"cpu_ms":89.341000000001,"wall_ms":92.861213022843},{"cpu_ms":88.67799999999804,"wall_ms":95.10519495233893},{"cpu_ms":87.65599999999907,"wall_ms":93.0717361625284},{"cpu_ms":86.23599999999954,"wall_ms":94.68471002765},{"cpu_ms":84.10299999999893,"wall_ms":90.55521409027278},{"cpu_ms":57.124999999999204,"wall_ms":62.43167817592621},{"cpu_ms":85.12400000000042,"wall_ms":91.45468403585255},{"cpu_ms":88.3289999999981,"wall_ms":95.72361991740763},{"cpu_ms":91.44699999999872,"wall_ms":96.94416099227965},{"cpu_ms":88.4299999999989,"wall_ms":94.77976709604263},{"cpu_ms":82.98199999999767,"wall_ms":94.38809612765908},{"cpu_ms":73.19299999999984,"wall_ms":81.59253792837262},{"cpu_ms":66.01300000000165,"wall_ms":74.38448094762862},{"cpu_ms":89.55299999999866,"wall_ms":95.9124828223139},{"cpu_ms":91.31599999999906,"wall_ms":95.6914909183979},{"cpu_ms":88.4319999999974,"wall_ms":95.87075607851148},{"cpu_ms":88.1779999999992,"wall_ms":94.62837502360344},{"cpu_ms":83.80499999999813,"wall_ms":94.18924408964813},{"cpu_ms":90.29699999999963,"wall_ms":92.714989092201},{"cpu_ms":90.37299999999959,"wall_ms":99.8120701406151},{"cpu_ms":89.98799999999818,"wall_ms":95.40299186483026},{"cpu_ms":92.25899999999854,"wall_ms":99.59532506763935},{"cpu_ms":90.83799999999798,"wall_ms":99.23002193681896},{"cpu_ms":85.40400000000403,"wall_ms":94.99941696412861}],"before_control_median_cpu_ms":85.95650000000089,"after_control":[{"cpu_ms":75.05799999999985,"wall_ms":81.36836998164654},{"cpu_ms":74.91400000000014,"wall_ms":80.46087389811873},{"cpu_ms":77.10499999999998,"wall_ms":84.52102891169488},{"cpu_ms":89.63999999999972,"wall_ms":94.46373721584678},{"cpu_ms":55.74199999999996,"wall_ms":69.11360309459269},{"cpu_ms":82.8319999999998,"wall_ms":95.27818788774312},{"cpu_ms":78.7810000000002,"wall_ms":83.14337604679167},{"cpu_ms":84.10400000000084,"wall_ms":91.54294990003109},{"cpu_ms":85.02400000000065,"wall_ms":90.43737407773733},{"cpu_ms":75.38499999999982,"wall_ms":83.78829015418887},{"cpu_ms":79.0090000000001,"wall_ms":87.41388493217528},{"cpu_ms":80.21999999999974,"wall_ms":87.61334815062582},{"cpu_ms":84.18900000000029,"wall_ms":92.81057794578373},{"cpu_ms":91.48100000000082,"wall_ms":96.9049979466945},{"cpu_ms":83.64400000000049,"wall_ms":90.15070809982717},{"cpu_ms":75.98100000000052,"wall_ms":85.31154715456069},{"cpu_ms":84.45700000000045,"wall_ms":92.79774408787489},{"cpu_ms":84.91099999999996,"wall_ms":90.85705899633467},{"cpu_ms":90.02300000000041,"wall_ms":94.02820304967463},{"cpu_ms":79.99599999999951,"wall_ms":91.9575949665159},{"cpu_ms":84.66899999999988,"wall_ms":92.13948505930603},{"cpu_ms":86.88800000000008,"wall_ms":92.27020898833871},{"cpu_ms":87.65200000000029,"wall_ms":93.01344701088965},{"cpu_ms":54.49400000000004,"wall_ms":60.93535199761391},{"cpu_ms":91.01999999999855,"wall_ms":96.04319813661277},{"cpu_ms":90.06100000000039,"wall_ms":94.00635212659836},{"cpu_ms":83.20600000000056,"wall_ms":91.25163499265909},{"cpu_ms":83.950999999999,"wall_ms":89.18729890137911},{"cpu_ms":91.2620000000004,"wall_ms":96.61638806574047},{"cpu_ms":87.78199999999892,"wall_ms":95.18235991708934},{"cpu_ms":87.66100000000066,"wall_ms":96.57569299452007},{"cpu_ms":90.19200000000005,"wall_ms":95.65117605961859},{"cpu_ms":77.78599999999969,"wall_ms":84.13174306042492},{"cpu_ms":89.36800000000034,"wall_ms":95.88939091190696},{"cpu_ms":83.99099999999926,"wall_ms":94.39007984474301},{"cpu_ms":87.0949999999997,"wall_ms":95.5362010281533},{"cpu_ms":90.28900000000029,"wall_ms":97.25070395506918},{"cpu_ms":90.32399999999896,"wall_ms":96.27354796975851},{"cpu_ms":84.91599999999977,"wall_ms":95.3200371004641},{"cpu_ms":87.54900000000099,"wall_ms":94.95814004912972},{"cpu_ms":85.90399999999931,"wall_ms":94.34455400332808},{"cpu_ms":82.56899999999945,"wall_ms":87.99600298516452},{"cpu_ms":59.787000000000035,"wall_ms":69.08338610082865},{"cpu_ms":85.02599999999916,"wall_ms":91.4412799756974},{"cpu_ms":84.84500000000139,"wall_ms":91.23929194174707},{"cpu_ms":86.587999999999,"wall_ms":93.04928383789957},{"cpu_ms":82.2730000000007,"wall_ms":91.82713110931218},{"cpu_ms":80.91699999999946,"wall_ms":88.31105101853609},{"cpu_ms":90.25099999999853,"wall_ms":94.66695599257946},{"cpu_ms":90.55999999999997,"wall_ms":94.99125205911696},{"cpu_ms":89.01899999999685,"wall_ms":96.10333992168307},{"cpu_ms":84.2470000000013,"wall_ms":87.63713203370571},{"cpu_ms":89.37300000000192,"wall_ms":95.8311720751226},{"cpu_ms":90.28399999999692,"wall_ms":96.72077395953238},{"cpu_ms":89.73200000000148,"wall_ms":96.13632108084857},{"cpu_ms":49.523999999998125,"wall_ms":55.915834149345756},{"cpu_ms":73.20200000000199,"wall_ms":81.69825607910752},{"cpu_ms":90.32299999999793,"wall_ms":95.69935593754053},{"cpu_ms":85.86599999999933,"wall_ms":91.26019105315208},{"cpu_ms":86.23099999999795,"wall_ms":92.6588240545243},{"cpu_ms":87.00699999999983,"wall_ms":92.41996821947396},{"cpu_ms":84.99899999999982,"wall_ms":91.55902988277376},{"cpu_ms":83.15199999999834,"wall_ms":92.56460797041655},{"cpu_ms":83.46300000000184,"wall_ms":92.35028806142509},{"cpu_ms":87.76299999999893,"wall_ms":94.11131800152361},{"cpu_ms":91.89800000000048,"wall_ms":95.98937793634832},{"cpu_ms":86.17500000000078,"wall_ms":91.57518995925784},{"cpu_ms":86.90400000000054,"wall_ms":93.51987391710281},{"cpu_ms":84.21500000000037,"wall_ms":91.6274469345808},{"cpu_ms":89.23200000000264,"wall_ms":92.59329200722277},{"cpu_ms":80.98199999999878,"wall_ms":93.25995785184205},{"cpu_ms":80.25299999999902,"wall_ms":90.69390292279422},{"cpu_ms":80.13799999999804,"wall_ms":90.5587209854275},{"cpu_ms":87.87900000000093,"wall_ms":93.32639188505709},{"cpu_ms":86.76700000000181,"wall_ms":93.21980318054557},{"cpu_ms":87.41399999999899,"wall_ms":93.79423595964909},{"cpu_ms":91.29500000000235,"wall_ms":93.74946192838252},{"cpu_ms":82.99399999999935,"wall_ms":89.37728288583457},{"cpu_ms":84.47200000000166,"wall_ms":88.86313298717141},{"cpu_ms":88.35499999999996,"wall_ms":92.76242810301483},{"cpu_ms":84.78900000000067,"wall_ms":90.94008291140199},{"cpu_ms":65.30000000000058,"wall_ms":71.68398494832218},{"cpu_ms":83.08300000000202,"wall_ms":89.49451195076108},{"cpu_ms":87.6940000000026,"wall_ms":95.0603811070323},{"cpu_ms":88.81600000000134,"wall_ms":95.23322409950197},{"cpu_ms":87.27700000000027,"wall_ms":94.76086194626987},{"cpu_ms":85.09600000000006,"wall_ms":96.4550981298089},{"cpu_ms":86.67999999999765,"wall_ms":93.10022904537618},{"cpu_ms":58.075999999999794,"wall_ms":61.486021149903536},{"cpu_ms":90.77700000000277,"wall_ms":96.23989602550864},{"cpu_ms":88.29999999999671,"wall_ms":97.76647784747183},{"cpu_ms":74.90299999999905,"wall_ms":82.42846000939608},{"cpu_ms":90.44400000000508,"wall_ms":96.880221972242},{"cpu_ms":88.81699999999881,"wall_ms":96.52685094624758},{"cpu_ms":86.15800000000462,"wall_ms":92.57874405011535},{"cpu_ms":83.28199999999697,"wall_ms":92.1293159481138},{"cpu_ms":91.92700000000542,"wall_ms":99.32899684645236},{"cpu_ms":96.28800000000126,"wall_ms":101.73741704784334},{"cpu_ms":93.94999999999953,"wall_ms":97.36581891775131},{"cpu_ms":92.31599999999673,"wall_ms":96.73221595585346}],"after_control_median_cpu_ms":85.88499999999932} +] diff --git a/benchmarks/regexp-construction/current-other-control-samples.json b/benchmarks/regexp-construction/current-other-control-samples.json new file mode 100644 index 0000000000..47131050bf --- /dev/null +++ b/benchmarks/regexp-construction/current-other-control-samples.json @@ -0,0 +1,5 @@ +[ + {"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":149.40100000000012,"wall_ms":153.82930589839816},{"cpu_ms":146.71199999999996,"wall_ms":151.1600750964135},{"cpu_ms":138.23099999999977,"wall_ms":143.66628578864038},{"cpu_ms":143.61600000000018,"wall_ms":148.00400496460497},{"cpu_ms":142.06699999999995,"wall_ms":147.45788392610848},{"cpu_ms":111.83799999999965,"wall_ms":119.23857009969652},{"cpu_ms":140.19199999999987,"wall_ms":145.5973160918802},{"cpu_ms":129.07199999999986,"wall_ms":137.48199515976012},{"cpu_ms":126.03999999999971,"wall_ms":134.47693595662713},{"cpu_ms":129.96200000000078,"wall_ms":137.3580419458449},{"cpu_ms":137.98200000000006,"wall_ms":144.37029091641307},{"cpu_ms":133.1029999999993,"wall_ms":141.5247020777315},{"cpu_ms":131.86299999999918,"wall_ms":139.20391304418445},{"cpu_ms":116.83199999999871,"wall_ms":124.61922690272331},{"cpu_ms":132.41700000000023,"wall_ms":142.81355100683868},{"cpu_ms":135.87099999999987,"wall_ms":141.2857340183109},{"cpu_ms":147.77899999999988,"wall_ms":150.15824092552066},{"cpu_ms":134.67500000000143,"wall_ms":140.12212795205414},{"cpu_ms":142.71100000000024,"wall_ms":152.13579684495926},{"cpu_ms":144.52799999999934,"wall_ms":150.98365303128958},{"cpu_ms":138.73799999999824,"wall_ms":143.16363306716084},{"cpu_ms":143.75299999999845,"wall_ms":150.1899790018797},{"cpu_ms":138.12399999999946,"wall_ms":146.51480806060135},{"cpu_ms":138.52200000000005,"wall_ms":147.97418611124158},{"cpu_ms":134.53299999999933,"wall_ms":138.923215912655},{"cpu_ms":134.01200000000023,"wall_ms":136.41840312629938},{"cpu_ms":134.60700000000259,"wall_ms":138.98967998102307},{"cpu_ms":77.65699999999853,"wall_ms":82.94613100588322},{"cpu_ms":146.01400000000098,"wall_ms":153.44273787923157},{"cpu_ms":94.07700000000219,"wall_ms":98.52061700075865},{"cpu_ms":132.06599999999824,"wall_ms":139.48023691773415},{"cpu_ms":143.83399999999824,"wall_ms":149.20507604256272},{"cpu_ms":137.76200000000216,"wall_ms":143.224986968562},{"cpu_ms":132.95900000000316,"wall_ms":139.33799997903407},{"cpu_ms":149.26900000000032,"wall_ms":152.6662870310247},{"cpu_ms":145.48399999999972,"wall_ms":151.85480890795588},{"cpu_ms":76.66800000000151,"wall_ms":83.94264406524599},{"cpu_ms":143.0720000000001,"wall_ms":149.43746896460652},{"cpu_ms":134.43499999999986,"wall_ms":140.83516807295382},{"cpu_ms":147.8389999999976,"wall_ms":152.22525992430747},{"cpu_ms":143.2600000000015,"wall_ms":146.6582037974149},{"cpu_ms":136.15500000000225,"wall_ms":143.496974138543},{"cpu_ms":148.07400000000115,"wall_ms":152.4637071415782},{"cpu_ms":139.28399999999996,"wall_ms":147.76173699647188},{"cpu_ms":134.37199999999905,"wall_ms":140.78041492030025},{"cpu_ms":140.05700000000232,"wall_ms":147.4672849290073},{"cpu_ms":144.52400000000054,"wall_ms":150.92468890361488},{"cpu_ms":101.89899999999952,"wall_ms":108.94340905360878},{"cpu_ms":140.1520000000005,"wall_ms":147.53117505460978},{"cpu_ms":140.38900000000254,"wall_ms":146.8168490100652},{"cpu_ms":145.1190000000011,"wall_ms":153.52679300121963},{"cpu_ms":139.25300000000007,"wall_ms":144.6740529499948},{"cpu_ms":141.8740000000014,"wall_ms":147.26878399960697},{"cpu_ms":139.56500000000105,"wall_ms":144.99848010018468},{"cpu_ms":120.00700000000109,"wall_ms":128.75169701874256},{"cpu_ms":77.71800000000084,"wall_ms":84.58704594522715},{"cpu_ms":77.98199999999866,"wall_ms":83.33959500305355},{"cpu_ms":121.27900000000125,"wall_ms":127.70515098236501},{"cpu_ms":123.00400000000167,"wall_ms":127.43331305682659},{"cpu_ms":129.99700000000303,"wall_ms":135.40596701204777},{"cpu_ms":120.2080000000052,"wall_ms":125.64234319142997},{"cpu_ms":139.46500000000128,"wall_ms":145.8946941420436},{"cpu_ms":145.308,"wall_ms":151.74496010877192},{"cpu_ms":140.1129999999995,"wall_ms":146.6211921069771},{"cpu_ms":109.9969999999999,"wall_ms":116.38504802249372},{"cpu_ms":98.77799999999581,"wall_ms":104.13908609189093},{"cpu_ms":115.27199999999738,"wall_ms":120.65448914654553},{"cpu_ms":101.76000000000585,"wall_ms":107.17535181902349},{"cpu_ms":132.06900000000132,"wall_ms":137.4370618723333},{"cpu_ms":144.243000000003,"wall_ms":152.6992020662874},{"cpu_ms":131.0849999999988,"wall_ms":137.5057501718402},{"cpu_ms":109.22800000000166,"wall_ms":114.54272293485701},{"cpu_ms":139.14199999999965,"wall_ms":145.521042868495},{"cpu_ms":74.79299999999967,"wall_ms":82.08938594907522},{"cpu_ms":83.49400000000173,"wall_ms":87.8905700519681},{"cpu_ms":102.62799999999572,"wall_ms":107.934681000188},{"cpu_ms":100.65700000000533,"wall_ms":106.04112013243139},{"cpu_ms":76.13800000000026,"wall_ms":82.45325600728393},{"cpu_ms":138.342999999999,"wall_ms":145.15682216733694},{"cpu_ms":130.02300000000133,"wall_ms":142.54065114073455},{"cpu_ms":132.38299999999725,"wall_ms":138.22182710282505},{"cpu_ms":143.0039999999977,"wall_ms":147.34966098330915},{"cpu_ms":141.11699999999416,"wall_ms":146.54073910787702},{"cpu_ms":142.0659999999998,"wall_ms":146.45475801080465},{"cpu_ms":107.79300000000092,"wall_ms":111.18983500637114},{"cpu_ms":136.85499999999706,"wall_ms":141.9845710042864},{"cpu_ms":132.80499999999762,"wall_ms":139.2048140987754},{"cpu_ms":110.47400000000351,"wall_ms":112.8138299100101},{"cpu_ms":135.85300000000444,"wall_ms":145.22863505408168},{"cpu_ms":131.9599999999994,"wall_ms":136.338867014274},{"cpu_ms":123.94600000000366,"wall_ms":131.32795901037753},{"cpu_ms":123.76800000000543,"wall_ms":132.15356203727424},{"cpu_ms":121.46800000000013,"wall_ms":125.87168905884027},{"cpu_ms":144.5319999999981,"wall_ms":152.916946914047},{"cpu_ms":144.46600000000132,"wall_ms":152.82507403753698},{"cpu_ms":144.85100000000273,"wall_ms":152.232717955485},{"cpu_ms":150.3839999999954,"wall_ms":157.83959906548262},{"cpu_ms":145.21200000000078,"wall_ms":149.58732179366052},{"cpu_ms":92.1199999999942,"wall_ms":98.46052783541381},{"cpu_ms":100.7979999999975,"wall_ms":108.89323009178042}],"before_median_cpu_ms":135.26400000000294,"after":[{"cpu_ms":128.90000000000023,"wall_ms":133.29759309999645},{"cpu_ms":72.70600000000016,"wall_ms":80.10162599384785},{"cpu_ms":133.788,"wall_ms":139.1651111189276},{"cpu_ms":138.67399999999998,"wall_ms":147.04634505324066},{"cpu_ms":137.25200000000015,"wall_ms":143.59186706133187},{"cpu_ms":78.80100000000034,"wall_ms":82.16957096010447},{"cpu_ms":126.88500000000059,"wall_ms":134.2682067770511},{"cpu_ms":137.9279999999996,"wall_ms":143.32398492842913},{"cpu_ms":134.42899999999992,"wall_ms":141.8285199906677},{"cpu_ms":128.71799999999922,"wall_ms":137.1254068799317},{"cpu_ms":130.10100000000068,"wall_ms":134.5048879738897},{"cpu_ms":136.60200000000077,"wall_ms":143.00543908029795},{"cpu_ms":133.3819999999992,"wall_ms":135.77146595343947},{"cpu_ms":126.77200000000077,"wall_ms":135.14111703261733},{"cpu_ms":132.4090000000009,"wall_ms":137.7952490001917},{"cpu_ms":139.41299999999933,"wall_ms":147.81834604218602},{"cpu_ms":138.7300000000007,"wall_ms":145.17897181212902},{"cpu_ms":116.03699999999861,"wall_ms":125.40696887299418},{"cpu_ms":141.6529999999998,"wall_ms":150.0195199623704},{"cpu_ms":138.401,"wall_ms":148.83272210136056},{"cpu_ms":139.81900000000104,"wall_ms":146.27687400206923},{"cpu_ms":142.7120000000013,"wall_ms":149.16139608249068},{"cpu_ms":141.8929999999996,"wall_ms":148.35767284967005},{"cpu_ms":144.62900000000013,"wall_ms":147.0800149254501},{"cpu_ms":137.10500000000002,"wall_ms":140.49315103329718},{"cpu_ms":142.27900000000028,"wall_ms":151.69772692024708},{"cpu_ms":136.41099999999895,"wall_ms":141.8092509265989},{"cpu_ms":143.29000000000036,"wall_ms":148.69587402790785},{"cpu_ms":148.18399999999698,"wall_ms":154.6917341183871},{"cpu_ms":144.47099999999935,"wall_ms":150.8525270037353},{"cpu_ms":133.92600000000243,"wall_ms":138.34383198991418},{"cpu_ms":144.16200000000146,"wall_ms":149.61166703142226},{"cpu_ms":143.4919999999984,"wall_ms":148.9442279562354},{"cpu_ms":107.57599999999812,"wall_ms":113.95316594280303},{"cpu_ms":145.0010000000006,"wall_ms":151.36464312672615},{"cpu_ms":138.61999999999952,"wall_ms":146.9896538183093},{"cpu_ms":78.91100000000151,"wall_ms":83.1930919084698},{"cpu_ms":144.15599999999884,"wall_ms":151.57770086079836},{"cpu_ms":135.5409999999999,"wall_ms":138.97487986832857},{"cpu_ms":144.17100000000005,"wall_ms":149.5830020867288},{"cpu_ms":141.057,"wall_ms":148.55598099529743},{"cpu_ms":142.1670000000006,"wall_ms":150.65418416634202},{"cpu_ms":140.1319999999977,"wall_ms":145.5566838849336},{"cpu_ms":144.25300000000263,"wall_ms":148.67719798348844},{"cpu_ms":140.28899999999922,"wall_ms":148.6495560966432},{"cpu_ms":141.6109999999975,"wall_ms":150.0602629967034},{"cpu_ms":135.38100000000242,"wall_ms":138.74660315923393},{"cpu_ms":108.63999999999763,"wall_ms":115.10974611155689},{"cpu_ms":141.7710000000021,"wall_ms":148.1856289319694},{"cpu_ms":138.6819999999993,"wall_ms":145.1179829891771},{"cpu_ms":142.68399999999914,"wall_ms":152.10348088294268},{"cpu_ms":144.4119999999991,"wall_ms":150.8319249842316},{"cpu_ms":111.02999999999952,"wall_ms":119.95772900991142},{"cpu_ms":137.1949999999984,"wall_ms":145.61951090581715},{"cpu_ms":126.69899999999856,"wall_ms":132.9961521551013},{"cpu_ms":119.87599999999787,"wall_ms":129.27670893259346},{"cpu_ms":100.88700000000017,"wall_ms":107.22967400215566},{"cpu_ms":123.39500000000214,"wall_ms":130.82863995805383},{"cpu_ms":119.9489999999983,"wall_ms":126.30102597177029},{"cpu_ms":127.75299999999845,"wall_ms":132.1153249591589},{"cpu_ms":109.53199999999441,"wall_ms":114.66594506055117},{"cpu_ms":143.53899999999697,"wall_ms":149.93755100294948},{"cpu_ms":134.67800000000096,"wall_ms":144.08457884564996},{"cpu_ms":140.29099999999772,"wall_ms":148.67959008552134},{"cpu_ms":138.37900000000047,"wall_ms":143.79238313995302},{"cpu_ms":123.00900000000325,"wall_ms":129.45506093092263},{"cpu_ms":135.33999999999935,"wall_ms":141.65116008371115},{"cpu_ms":135.98700000000008,"wall_ms":143.39216379448771},{"cpu_ms":124.72800000000461,"wall_ms":132.69487814977765},{"cpu_ms":136.0729999999961,"wall_ms":143.50059698335826},{"cpu_ms":118.6089999999993,"wall_ms":124.98396192677319},{"cpu_ms":142.57500000000078,"wall_ms":148.06255511939526},{"cpu_ms":132.46200000000385,"wall_ms":138.88320489786565},{"cpu_ms":119.60799999999949,"wall_ms":124.13107696920633},{"cpu_ms":107.24900000000304,"wall_ms":110.5978291016072},{"cpu_ms":85.51900000000501,"wall_ms":92.21702883951366},{"cpu_ms":105.82399999999836,"wall_ms":113.30158310011029},{"cpu_ms":81.61399999999475,"wall_ms":83.91967695206404},{"cpu_ms":111.69000000000295,"wall_ms":119.71469107083976},{"cpu_ms":94.37700000000149,"wall_ms":101.08092799782753},{"cpu_ms":114.54200000000014,"wall_ms":117.92878294363618},{"cpu_ms":142.6930000000013,"wall_ms":153.08174607343972},{"cpu_ms":140.63099999999906,"wall_ms":148.04022200405598},{"cpu_ms":139.87300000000147,"wall_ms":146.25846105627716},{"cpu_ms":116.08799999999775,"wall_ms":124.383658869192},{"cpu_ms":110.14200000000329,"wall_ms":118.47276892513037},{"cpu_ms":129.0130000000005,"wall_ms":134.3632321804762},{"cpu_ms":143.70999999999867,"wall_ms":152.11902488954365},{"cpu_ms":86.73799999999687,"wall_ms":97.1397899556905},{"cpu_ms":126.81899999999757,"wall_ms":132.2467769496143},{"cpu_ms":125.77899999999431,"wall_ms":131.14690105430782},{"cpu_ms":128.68999999999886,"wall_ms":132.04701314680278},{"cpu_ms":122.18099999999765,"wall_ms":128.5842601209879},{"cpu_ms":76.989999999995,"wall_ms":83.50345818325877},{"cpu_ms":148.7639999999999,"wall_ms":153.22720701806247},{"cpu_ms":148.03299999999808,"wall_ms":153.42226112261415},{"cpu_ms":146.24300000000545,"wall_ms":152.64366590417922},{"cpu_ms":145.87399999999917,"wall_ms":150.26072086766362},{"cpu_ms":124.4130000000041,"wall_ms":129.7921270597726},{"cpu_ms":144.8960000000028,"wall_ms":151.34043898433447}],"after_median_cpu_ms":135.764,"before_control":[{"cpu_ms":144.44100000000003,"wall_ms":150.88501200079918},{"cpu_ms":141.4249999999999,"wall_ms":146.81528299115598},{"cpu_ms":127.77500000000019,"wall_ms":150.98369494080544},{"cpu_ms":144.24100000000007,"wall_ms":146.6487820725888},{"cpu_ms":105.78600000000016,"wall_ms":113.20641310885549},{"cpu_ms":84.32299999999948,"wall_ms":89.68924917280674},{"cpu_ms":138.2519999999996,"wall_ms":146.57044503837824},{"cpu_ms":136.02800000000047,"wall_ms":145.46154206618667},{"cpu_ms":123.50800000000018,"wall_ms":135.89018885977566},{"cpu_ms":141.66699999999997,"wall_ms":145.0238530524075},{"cpu_ms":136.18400000000008,"wall_ms":144.56768007948995},{"cpu_ms":133.6970000000015,"wall_ms":140.14455117285252},{"cpu_ms":140.12000000000137,"wall_ms":145.5298289656639},{"cpu_ms":139.48599999999976,"wall_ms":145.92793211340904},{"cpu_ms":78.66999999999891,"wall_ms":87.12365198880434},{"cpu_ms":142.25899999999925,"wall_ms":146.62732905708253},{"cpu_ms":143.92299999999915,"wall_ms":149.3099839426577},{"cpu_ms":83.13300000000012,"wall_ms":88.52976001799107},{"cpu_ms":149.32199999999972,"wall_ms":151.7135079484433},{"cpu_ms":145.35300000000007,"wall_ms":153.77338812686503},{"cpu_ms":146.23500000000078,"wall_ms":151.64931816980243},{"cpu_ms":144.46399999999926,"wall_ms":147.8668658528477},{"cpu_ms":140.53300000000135,"wall_ms":146.8934561125934},{"cpu_ms":101.71700000000072,"wall_ms":113.86223696172237},{"cpu_ms":127.92800000000071,"wall_ms":137.28523394092917},{"cpu_ms":147.3709999999997,"wall_ms":152.76886010542512},{"cpu_ms":137.65199999999922,"wall_ms":143.99222191423178},{"cpu_ms":138.0739999999996,"wall_ms":142.48445304110646},{"cpu_ms":147.74000000000242,"wall_ms":153.118401998654},{"cpu_ms":141.71000000000333,"wall_ms":147.09218591451645},{"cpu_ms":131.58299999999912,"wall_ms":142.97736412845552},{"cpu_ms":142.59600000000106,"wall_ms":150.08271695114672},{"cpu_ms":143.69299999999896,"wall_ms":150.10412596166134},{"cpu_ms":122.03099999999978,"wall_ms":128.4444557968527},{"cpu_ms":140.6710000000011,"wall_ms":145.07101685740054},{"cpu_ms":135.89299999999938,"wall_ms":141.28060080111027},{"cpu_ms":78.33999999999719,"wall_ms":83.67603505030274},{"cpu_ms":141.7199999999994,"wall_ms":148.15230714157224},{"cpu_ms":151.74900000000235,"wall_ms":157.14370599016547},{"cpu_ms":144.33199999999857,"wall_ms":149.73307098262012},{"cpu_ms":143.62699999999862,"wall_ms":148.05879211053252},{"cpu_ms":144.57099999999912,"wall_ms":150.00275592319667},{"cpu_ms":143.13299999999884,"wall_ms":150.61311703175306},{"cpu_ms":139.87000000000194,"wall_ms":147.63226406648755},{"cpu_ms":138.59199999999916,"wall_ms":146.98174805380404},{"cpu_ms":140.0669999999984,"wall_ms":144.483988173306},{"cpu_ms":139.56400000000002,"wall_ms":144.98435403220356},{"cpu_ms":144.48099999999897,"wall_ms":149.91445001214743},{"cpu_ms":144.168999999998,"wall_ms":147.55678200162947},{"cpu_ms":125.97799999999992,"wall_ms":129.38314815983176},{"cpu_ms":135.90200000000152,"wall_ms":142.33150403015316},{"cpu_ms":145.0969999999998,"wall_ms":149.88491404801607},{"cpu_ms":142.67999999999859,"wall_ms":149.1541489958763},{"cpu_ms":136.29700000000255,"wall_ms":143.73028906993568},{"cpu_ms":137.20900000000213,"wall_ms":147.6238069590181},{"cpu_ms":129.72500000000053,"wall_ms":135.12558210641146},{"cpu_ms":125.64199999999914,"wall_ms":133.04346008226275},{"cpu_ms":127.01799999999963,"wall_ms":132.37726502120495},{"cpu_ms":119.01799999999696,"wall_ms":126.40955788083375},{"cpu_ms":127.59400000000198,"wall_ms":134.96950990520418},{"cpu_ms":120.32799999999355,"wall_ms":125.7159321103245},{"cpu_ms":146.07200000000375,"wall_ms":150.52059083245695},{"cpu_ms":148.4179999999995,"wall_ms":151.81541186757386},{"cpu_ms":141.6889999999995,"wall_ms":147.11128687486053},{"cpu_ms":136.49600000000106,"wall_ms":142.8864288609475},{"cpu_ms":146.12100000000083,"wall_ms":150.5492040887475},{"cpu_ms":135.75699999999813,"wall_ms":142.1821431722492},{"cpu_ms":144.25799999999356,"wall_ms":150.62813111580908},{"cpu_ms":120.277999999999,"wall_ms":128.68808605708182},{"cpu_ms":140.5499999999975,"wall_ms":147.95584604144096},{"cpu_ms":142.0010000000005,"wall_ms":146.3680670130998},{"cpu_ms":138.66399999999857,"wall_ms":146.05278801172972},{"cpu_ms":133.0430000000007,"wall_ms":140.35020698793232},{"cpu_ms":104.09800000000047,"wall_ms":112.10489692166448},{"cpu_ms":143.45099999999888,"wall_ms":148.81663280539215},{"cpu_ms":125.63699999999756,"wall_ms":132.96751584857702},{"cpu_ms":131.37599999999594,"wall_ms":135.7813961803913},{"cpu_ms":102.26399999999813,"wall_ms":108.65413607098162},{"cpu_ms":76.18399999999781,"wall_ms":84.50763602741063},{"cpu_ms":122.44700000000108,"wall_ms":131.821493152529},{"cpu_ms":95.33499999999862,"wall_ms":100.63914209604263},{"cpu_ms":142.07400000000092,"wall_ms":150.51091089844704},{"cpu_ms":144.5630000000051,"wall_ms":149.98362492769957},{"cpu_ms":130.786999999998,"wall_ms":137.51168991439044},{"cpu_ms":105.24300000000153,"wall_ms":107.57582890801132},{"cpu_ms":98.5030000000009,"wall_ms":102.78722713701427},{"cpu_ms":133.6250000000021,"wall_ms":138.004289008677},{"cpu_ms":123.95800000000179,"wall_ms":130.02011482603848},{"cpu_ms":94.97400000000056,"wall_ms":100.28713289648294},{"cpu_ms":95.25500000000164,"wall_ms":96.61011002026498},{"cpu_ms":128.57499999999789,"wall_ms":133.98743094876409},{"cpu_ms":126.62600000000168,"wall_ms":132.0370938628912},{"cpu_ms":81.01099999999661,"wall_ms":84.37819383107126},{"cpu_ms":94.69900000000564,"wall_ms":103.56854717247188},{"cpu_ms":145.81299999999686,"wall_ms":152.2489481139928},{"cpu_ms":148.56799999999737,"wall_ms":153.97940203547478},{"cpu_ms":139.33800000000218,"wall_ms":146.67453686706722},{"cpu_ms":146.6609999999946,"wall_ms":152.03561913222075},{"cpu_ms":140.11800000000108,"wall_ms":147.48657494783401},{"cpu_ms":115.87300000000056,"wall_ms":119.16941008530557}],"before_control_median_cpu_ms":138.1629999999996,"after_control":[{"cpu_ms":144.3129999999999,"wall_ms":149.7171081136912},{"cpu_ms":81.38099999999993,"wall_ms":91.9387040194124},{"cpu_ms":93.46599999999982,"wall_ms":100.8132139686495},{"cpu_ms":139.71,"wall_ms":147.11318002082407},{"cpu_ms":130.76899999999992,"wall_ms":137.1844590175897},{"cpu_ms":83.03599999999989,"wall_ms":89.34646798297763},{"cpu_ms":101.99000000000069,"wall_ms":109.21338805928826},{"cpu_ms":137.05299999999988,"wall_ms":144.514701096341},{"cpu_ms":139.35399999999998,"wall_ms":142.73587591014802},{"cpu_ms":136.19300000000044,"wall_ms":143.61678203567863},{"cpu_ms":137.41699999999923,"wall_ms":142.85283396020532},{"cpu_ms":139.8029999999988,"wall_ms":144.2365909460932},{"cpu_ms":139.60499999999954,"wall_ms":142.99374702386558},{"cpu_ms":128.13799999999986,"wall_ms":133.53514508344233},{"cpu_ms":78.61200000000146,"wall_ms":83.88315397314727},{"cpu_ms":140.9420000000008,"wall_ms":145.3755390830338},{"cpu_ms":118.21600000000032,"wall_ms":128.5893488675356},{"cpu_ms":94.4660000000006,"wall_ms":96.8196380417794},{"cpu_ms":128.7649999999996,"wall_ms":134.19329491443932},{"cpu_ms":147.50300000000038,"wall_ms":151.9519230350852},{"cpu_ms":147.72800000000075,"wall_ms":152.16046199202538},{"cpu_ms":146.49299999999954,"wall_ms":152.91178692132235},{"cpu_ms":157.58399999999995,"wall_ms":160.96488200128078},{"cpu_ms":139.29599999999988,"wall_ms":144.6954768616706},{"cpu_ms":145.91899999999924,"wall_ms":152.34565804712474},{"cpu_ms":146.77600000000092,"wall_ms":153.19053805433214},{"cpu_ms":138.92299999999835,"wall_ms":142.3157479148358},{"cpu_ms":141.6370000000029,"wall_ms":150.07040393538773},{"cpu_ms":144.41999999999666,"wall_ms":147.82038796693087},{"cpu_ms":143.9889999999977,"wall_ms":148.3859750442207},{"cpu_ms":92.9000000000002,"wall_ms":99.23261683434248},{"cpu_ms":148.20299999999875,"wall_ms":152.65334211289883},{"cpu_ms":142.10699999999932,"wall_ms":149.54402996227145},{"cpu_ms":143.79900000000134,"wall_ms":150.23922896943986},{"cpu_ms":128.12199999999763,"wall_ms":135.53133397363126},{"cpu_ms":147.5219999999986,"wall_ms":152.620930923149},{"cpu_ms":144.3310000000011,"wall_ms":150.73987492360175},{"cpu_ms":130.1520000000025,"wall_ms":136.56052784062922},{"cpu_ms":78.32699999999804,"wall_ms":80.68222785368562},{"cpu_ms":142.3450000000024,"wall_ms":151.83306694962084},{"cpu_ms":140.66700000000054,"wall_ms":146.3258219882846},{"cpu_ms":142.58399999999938,"wall_ms":150.93234297819436},{"cpu_ms":138.2879999999993,"wall_ms":143.70579412207007},{"cpu_ms":138.81199999999794,"wall_ms":146.28050685860217},{"cpu_ms":144.52500000000157,"wall_ms":149.94851383380592},{"cpu_ms":138.16400000000328,"wall_ms":147.63021003454924},{"cpu_ms":140.5979999999971,"wall_ms":144.98914289288223},{"cpu_ms":132.25200000000115,"wall_ms":134.67059284448624},{"cpu_ms":145.78200000000052,"wall_ms":151.1614010669291},{"cpu_ms":140.2549999999998,"wall_ms":146.58098807558417},{"cpu_ms":142.13599999999715,"wall_ms":149.78394494391978},{"cpu_ms":139.9620000000006,"wall_ms":146.44605503417552},{"cpu_ms":139.82599999999934,"wall_ms":147.21592492423952},{"cpu_ms":136.44700000000043,"wall_ms":140.82937501370907},{"cpu_ms":139.02899999999718,"wall_ms":147.39617193117738},{"cpu_ms":124.60199999999944,"wall_ms":129.97175799682736},{"cpu_ms":123.64600000000081,"wall_ms":130.10569801554084},{"cpu_ms":125.47800000000109,"wall_ms":133.84135183878243},{"cpu_ms":125.83899999999915,"wall_ms":131.2226669397205},{"cpu_ms":121.66700000000219,"wall_ms":127.05545593053102},{"cpu_ms":140.81000000000188,"wall_ms":148.18503498099744},{"cpu_ms":142.5959999999975,"wall_ms":148.95225898362696},{"cpu_ms":136.86900000000435,"wall_ms":142.26596988737583},{"cpu_ms":141.61,"wall_ms":149.03830899856985},{"cpu_ms":126.08799999999576,"wall_ms":132.4464629869908},{"cpu_ms":134.1460000000012,"wall_ms":138.56900902464986},{"cpu_ms":134.5840000000038,"wall_ms":143.9432348124683},{"cpu_ms":139.6960000000007,"wall_ms":148.1090420857072},{"cpu_ms":149.98299999999887,"wall_ms":154.36692000366747},{"cpu_ms":144.89500000000533,"wall_ms":147.2911830060184},{"cpu_ms":139.64699999999652,"wall_ms":145.0020840857178},{"cpu_ms":142.52499999999912,"wall_ms":148.94164819270372},{"cpu_ms":139.285000000001,"wall_ms":144.63484683074057},{"cpu_ms":129.51299999999577,"wall_ms":134.857869008556},{"cpu_ms":87.82599999999974,"wall_ms":91.1132050678134},{"cpu_ms":141.7029999999997,"wall_ms":146.13276696763933},{"cpu_ms":94.85300000000052,"wall_ms":102.22790203988552},{"cpu_ms":99.69500000000409,"wall_ms":103.04270498454571},{"cpu_ms":101.64300000000281,"wall_ms":106.9769689347595},{"cpu_ms":121.40399999999829,"wall_ms":127.75964010506868},{"cpu_ms":104.60400000000192,"wall_ms":109.98101509176195},{"cpu_ms":142.78300000000144,"wall_ms":150.17798799090087},{"cpu_ms":143.6119999999974,"wall_ms":146.9683579634875},{"cpu_ms":139.02500000000373,"wall_ms":147.49393006786704},{"cpu_ms":103.83499999999657,"wall_ms":110.12937198393047},{"cpu_ms":95.96100000000263,"wall_ms":102.24689706228673},{"cpu_ms":133.80699999999734,"wall_ms":136.20084803551435},{"cpu_ms":114.40999999999946,"wall_ms":121.81819300167263},{"cpu_ms":84.305999999998,"wall_ms":88.72320596128702},{"cpu_ms":101.52599999999978,"wall_ms":106.47867689840496},{"cpu_ms":125.94800000000106,"wall_ms":132.29922601021826},{"cpu_ms":127.60899999999964,"wall_ms":129.98595996759832},{"cpu_ms":84.6650000000011,"wall_ms":92.04078698530793},{"cpu_ms":82.62500000000017,"wall_ms":89.99042492359877},{"cpu_ms":145.96800000000343,"wall_ms":152.38396101631224},{"cpu_ms":133.51300000000066,"wall_ms":138.98328901268542},{"cpu_ms":145.83600000000274,"wall_ms":150.22887103259563},{"cpu_ms":142.4960000000013,"wall_ms":146.86761796474457},{"cpu_ms":145.56300000000277,"wall_ms":148.9548389799893},{"cpu_ms":106.65600000000097,"wall_ms":110.96646590158343}],"after_control_median_cpu_ms":138.86749999999813}, + {"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":29.90499999999985,"wall_ms":36.54199908487499},{"cpu_ms":41.038999999999824,"wall_ms":46.82116489857435},{"cpu_ms":46.959000000000195,"wall_ms":54.38564601354301},{"cpu_ms":36.81299999999999,"wall_ms":41.192003060132265},{"cpu_ms":28.802999999999912,"wall_ms":33.07094099000096},{"cpu_ms":48.39500000000019,"wall_ms":53.84576693177223},{"cpu_ms":44.924999999999656,"wall_ms":51.308484049513936},{"cpu_ms":44.69100000000026,"wall_ms":51.07035906985402},{"cpu_ms":48.09099999999988,"wall_ms":52.473129937425256},{"cpu_ms":41.60399999999997,"wall_ms":51.444286946207285},{"cpu_ms":46.799000000000035,"wall_ms":51.14537780173123},{"cpu_ms":43.885000000000396,"wall_ms":51.24260997399688},{"cpu_ms":46.64400000000013,"wall_ms":51.08815198764205},{"cpu_ms":47.43599999999981,"wall_ms":51.74558004364371},{"cpu_ms":45.74799999999968,"wall_ms":51.0776920709759},{"cpu_ms":45.410999999999646,"wall_ms":52.717798156663775},{"cpu_ms":48.40200000000028,"wall_ms":52.78164683841169},{"cpu_ms":46.58500000000032,"wall_ms":53.058134159073234},{"cpu_ms":32.98700000000032,"wall_ms":40.38516408763826},{"cpu_ms":47.721000000000124,"wall_ms":54.15929201990366},{"cpu_ms":50.37999999999965,"wall_ms":53.77044505439699},{"cpu_ms":51.71700000000001,"wall_ms":81.95440098643303},{"cpu_ms":46.58799999999985,"wall_ms":54.070675978437066},{"cpu_ms":44.946000000000375,"wall_ms":53.31929214298725},{"cpu_ms":46.89399999999999,"wall_ms":53.27213415876031},{"cpu_ms":49.736999999999476,"wall_ms":54.208173882216215},{"cpu_ms":47.53400000000063,"wall_ms":54.901108145713806},{"cpu_ms":43.50100000000001,"wall_ms":53.12235397286713},{"cpu_ms":49.49700000000057,"wall_ms":53.95124317146838},{"cpu_ms":25.69500000000069,"wall_ms":34.970011096447706},{"cpu_ms":52.16300000000018,"wall_ms":54.52441400848329},{"cpu_ms":48.23700000000031,"wall_ms":55.65211293287575},{"cpu_ms":43.14600000000013,"wall_ms":50.58133299462497},{"cpu_ms":46.52600000000007,"wall_ms":56.020219810307026},{"cpu_ms":45.56799999999939,"wall_ms":55.89657905511558},{"cpu_ms":46.155999999999864,"wall_ms":51.55832995660603},{"cpu_ms":52.22300000000146,"wall_ms":57.72013799287379},{"cpu_ms":29.196999999999917,"wall_ms":37.363856099545956},{"cpu_ms":28.12299999999901,"wall_ms":34.44280591793358},{"cpu_ms":42.08099999999959,"wall_ms":82.58368703536689},{"cpu_ms":48.57400000000034,"wall_ms":54.07391209155321},{"cpu_ms":47.49600000000065,"wall_ms":53.971300134435296},{"cpu_ms":48.84099999999947,"wall_ms":53.229093085974455},{"cpu_ms":47.19399999999929,"wall_ms":54.49950788170099},{"cpu_ms":46.469999999999345,"wall_ms":54.95094181969762},{"cpu_ms":48.41099999999976,"wall_ms":54.847488179802895},{"cpu_ms":44.650000000000745,"wall_ms":54.98021701350808},{"cpu_ms":46.30700000000054,"wall_ms":53.74339409172535},{"cpu_ms":48.811999999999856,"wall_ms":54.22337097115815},{"cpu_ms":47.301000000000926,"wall_ms":53.710712818428874},{"cpu_ms":47.0360000000003,"wall_ms":54.43556793034077},{"cpu_ms":46.188000000000784,"wall_ms":52.59252293035388},{"cpu_ms":47.03400000000002,"wall_ms":53.40793705545366},{"cpu_ms":47.15399999999903,"wall_ms":54.5970699749887},{"cpu_ms":48.37299999999978,"wall_ms":52.790933987125754},{"cpu_ms":47.563000000000244,"wall_ms":53.93976112827659},{"cpu_ms":47.996999999998735,"wall_ms":54.42422698251903},{"cpu_ms":49.295000000000755,"wall_ms":53.65313496440649},{"cpu_ms":49.611000000000516,"wall_ms":53.956082090735435},{"cpu_ms":49.602000000000146,"wall_ms":54.07743901014328},{"cpu_ms":45.90199999999989,"wall_ms":53.259229054674506},{"cpu_ms":48.5679999999995,"wall_ms":53.00517380237579},{"cpu_ms":49.1659999999996,"wall_ms":54.60539483465254},{"cpu_ms":45.77300000000051,"wall_ms":54.133894154801965},{"cpu_ms":48.009999999999664,"wall_ms":54.439465049654245},{"cpu_ms":50.178999999999974,"wall_ms":55.655909003689885},{"cpu_ms":50.773999999998765,"wall_ms":54.18458906933665},{"cpu_ms":47.413000000000594,"wall_ms":51.820999942719936},{"cpu_ms":46.357000000000426,"wall_ms":52.81298398040235},{"cpu_ms":49.39000000000071,"wall_ms":54.860451025888324},{"cpu_ms":49.35000000000045,"wall_ms":53.79369808360934},{"cpu_ms":45.06500000000102,"wall_ms":53.51097881793976},{"cpu_ms":48.057000000000016,"wall_ms":52.414149045944214},{"cpu_ms":31.08299999999886,"wall_ms":36.94380004890263},{"cpu_ms":46.66999999999888,"wall_ms":52.05444595776498},{"cpu_ms":45.47200000000018,"wall_ms":51.827712915837765},{"cpu_ms":46.317000000000164,"wall_ms":52.77436203323305},{"cpu_ms":47.48700000000028,"wall_ms":53.81459603086114},{"cpu_ms":46.56599999999855,"wall_ms":53.16507304087281},{"cpu_ms":47.931000000001944,"wall_ms":53.29938395880163},{"cpu_ms":50.04700000000284,"wall_ms":54.47680479846895},{"cpu_ms":47.52200000000073,"wall_ms":52.86293686367571},{"cpu_ms":48.02100000000209,"wall_ms":53.404449950903654},{"cpu_ms":45.70199999999858,"wall_ms":50.05880491808057},{"cpu_ms":46.897999999998774,"wall_ms":54.2846848256886},{"cpu_ms":47.93899999999951,"wall_ms":54.31574396789074},{"cpu_ms":47.74399999999801,"wall_ms":53.08023397810757},{"cpu_ms":46.70099999999877,"wall_ms":54.10625203512609},{"cpu_ms":39.1130000000004,"wall_ms":49.51851605437696},{"cpu_ms":46.52800000000212,"wall_ms":50.884669879451394},{"cpu_ms":49.13400000000223,"wall_ms":52.538221003487706},{"cpu_ms":47.84399999999778,"wall_ms":51.206231117248535},{"cpu_ms":43.33999999999705,"wall_ms":50.62836082652211},{"cpu_ms":45.673999999998216,"wall_ms":54.058081936091185},{"cpu_ms":46.65200000000169,"wall_ms":54.15412108413875},{"cpu_ms":36.315000000001874,"wall_ms":40.96746118739247},{"cpu_ms":50.02600000000257,"wall_ms":53.40494099073112},{"cpu_ms":48.763000000001,"wall_ms":53.2217079307884},{"cpu_ms":47.81799999999947,"wall_ms":54.2360742110759},{"cpu_ms":50.271000000002175,"wall_ms":54.65349508449435}],"before_median_cpu_ms":47.03500000000016,"after":[{"cpu_ms":43.77300000000006,"wall_ms":49.395508132874966},{"cpu_ms":47.57900000000026,"wall_ms":53.909535985440016},{"cpu_ms":47.592999999999996,"wall_ms":52.1007040515542},{"cpu_ms":38.92399999999974,"wall_ms":44.24985800869763},{"cpu_ms":49.44800000000038,"wall_ms":53.87080297805369},{"cpu_ms":48.938999999999844,"wall_ms":53.3830919303},{"cpu_ms":42.82500000000012,"wall_ms":50.20689405500889},{"cpu_ms":43.4009999999998,"wall_ms":47.817578073590994},{"cpu_ms":45.99799999999998,"wall_ms":52.51343594864011},{"cpu_ms":48.964999999999925,"wall_ms":52.326134871691465},{"cpu_ms":45.44499999999996,"wall_ms":53.75933414325118},{"cpu_ms":43.832999999999785,"wall_ms":51.20724393054843},{"cpu_ms":49.53299999999983,"wall_ms":52.97231418080628},{"cpu_ms":47.296999999999926,"wall_ms":52.7054239064455},{"cpu_ms":50.13100000000037,"wall_ms":54.43209200166166},{"cpu_ms":44.75799999999985,"wall_ms":50.129990093410015},{"cpu_ms":41.839999999999655,"wall_ms":50.204710103571415},{"cpu_ms":49.35199999999984,"wall_ms":53.72728384099901},{"cpu_ms":46.43700000000006,"wall_ms":53.740309085696936},{"cpu_ms":49.14499999999933,"wall_ms":53.52354398928583},{"cpu_ms":48.642000000000074,"wall_ms":55.02203502692282},{"cpu_ms":44.324999999999726,"wall_ms":54.7978759277612},{"cpu_ms":47.6769999999993,"wall_ms":54.00961195118725},{"cpu_ms":48.59400000000047,"wall_ms":54.033407010138035},{"cpu_ms":46.12700000000025,"wall_ms":53.54380211792886},{"cpu_ms":49.70400000000019,"wall_ms":54.037675028666854},{"cpu_ms":47.088999999999714,"wall_ms":52.37343395128846},{"cpu_ms":49.65499999999956,"wall_ms":54.044737946242094},{"cpu_ms":49.51099999999986,"wall_ms":55.904665030539036},{"cpu_ms":50.11999999999972,"wall_ms":54.497273871675134},{"cpu_ms":47.717999999999705,"wall_ms":77.90479995310307},{"cpu_ms":49.35799999999979,"wall_ms":55.77550292946398},{"cpu_ms":43.868999999999936,"wall_ms":49.20817608945072},{"cpu_ms":50.673000000000634,"wall_ms":66.0693550016731},{"cpu_ms":49.8340000000006,"wall_ms":56.21770001016557},{"cpu_ms":44.815000000000715,"wall_ms":50.191334914416075},{"cpu_ms":47.58999999999958,"wall_ms":53.05184097960591},{"cpu_ms":46.54500000000006,"wall_ms":55.85326789878309},{"cpu_ms":27.2660000000009,"wall_ms":33.87174685485661},{"cpu_ms":48.59300000000033,"wall_ms":54.185820976272225},{"cpu_ms":45.097999999999416,"wall_ms":56.62860698066652},{"cpu_ms":49.10300000000056,"wall_ms":54.518924094736576},{"cpu_ms":48.44500000000096,"wall_ms":55.812231032177806},{"cpu_ms":50.40400000000034,"wall_ms":55.806149961426854},{"cpu_ms":48.4650000000002,"wall_ms":54.754843935370445},{"cpu_ms":46.81199999999919,"wall_ms":55.165612837299705},{"cpu_ms":48.97899999999922,"wall_ms":55.397455813363194},{"cpu_ms":49.562999999999136,"wall_ms":55.95435691066086},{"cpu_ms":48.012999999999195,"wall_ms":55.444363970309496},{"cpu_ms":47.45299999999908,"wall_ms":55.80281396396458},{"cpu_ms":48.96799999999857,"wall_ms":55.38786784745753},{"cpu_ms":49.2120000000007,"wall_ms":54.61779795587063},{"cpu_ms":48.74299999999998,"wall_ms":55.17331697046757},{"cpu_ms":46.8869999999999,"wall_ms":55.298160994425416},{"cpu_ms":47.17699999999958,"wall_ms":54.64927805587649},{"cpu_ms":50.46199999999956,"wall_ms":53.92898106947541},{"cpu_ms":44.90300000000147,"wall_ms":55.27860391885042},{"cpu_ms":45.66999999999943,"wall_ms":51.01244896650314},{"cpu_ms":50.01299999999986,"wall_ms":54.40506082959473},{"cpu_ms":45.91199999999951,"wall_ms":55.31667498871684},{"cpu_ms":45.71799999999904,"wall_ms":54.23687701113522},{"cpu_ms":50.216999999999956,"wall_ms":54.63789589703083},{"cpu_ms":48.29699999999981,"wall_ms":55.661348858848214},{"cpu_ms":48.662000000000205,"wall_ms":56.1128631234169},{"cpu_ms":51.480999999999,"wall_ms":54.86586201004684},{"cpu_ms":47.5670000000008,"wall_ms":55.955979973077774},{"cpu_ms":49.48399999999964,"wall_ms":54.98617701232433},{"cpu_ms":29.5109999999994,"wall_ms":35.76349304057658},{"cpu_ms":49.52000000000112,"wall_ms":54.046720964834094},{"cpu_ms":49.4880000000002,"wall_ms":53.842949913814664},{"cpu_ms":48.09999999999981,"wall_ms":55.54697499610484},{"cpu_ms":43.18699999999964,"wall_ms":54.661289090290666},{"cpu_ms":48.047000000000395,"wall_ms":53.435699082911015},{"cpu_ms":45.38300000000106,"wall_ms":51.716054091230035},{"cpu_ms":46.38100000000023,"wall_ms":51.67839280329645},{"cpu_ms":42.72600000000004,"wall_ms":52.07963287830353},{"cpu_ms":46.29100000000008,"wall_ms":53.606346948072314},{"cpu_ms":48.4770000000001,"wall_ms":54.94207399897277},{"cpu_ms":47.826999999998066,"wall_ms":55.219483096152544},{"cpu_ms":48.811000000000604,"wall_ms":54.17926795780659},{"cpu_ms":38.59199999999774,"wall_ms":44.932321878150105},{"cpu_ms":48.335999999999046,"wall_ms":54.68945181928575},{"cpu_ms":47.138000000000346,"wall_ms":55.46432104893029},{"cpu_ms":48.78099999999819,"wall_ms":54.14544488303363},{"cpu_ms":48.842000000000496,"wall_ms":55.23230601102114},{"cpu_ms":49.845999999998725,"wall_ms":55.26976613327861},{"cpu_ms":48.66700000000179,"wall_ms":55.010261945426464},{"cpu_ms":47.76599999999931,"wall_ms":54.14117709733546},{"cpu_ms":49.806999999997714,"wall_ms":117.04137804917991},{"cpu_ms":47.24199999999712,"wall_ms":52.704742178320885},{"cpu_ms":43.708999999999776,"wall_ms":53.084271028637886},{"cpu_ms":46.07000000000028,"wall_ms":52.45843110606074},{"cpu_ms":48.05500000000151,"wall_ms":52.44352389127016},{"cpu_ms":38.53400000000207,"wall_ms":48.405956011265516},{"cpu_ms":48.151999999998196,"wall_ms":55.63478893600404},{"cpu_ms":48.70799999999775,"wall_ms":55.237406166270375},{"cpu_ms":50.40199999999828,"wall_ms":54.80365618132055},{"cpu_ms":48.08099999999982,"wall_ms":54.47140405885875},{"cpu_ms":52.351999999999066,"wall_ms":54.7365490347147},{"cpu_ms":47.50999999999905,"wall_ms":54.81682997196913}],"after_median_cpu_ms":48.051000000000954,"before_control":[{"cpu_ms":45.53700000000016,"wall_ms":51.90009996294975},{"cpu_ms":43.39000000000004,"wall_ms":49.656986026093364},{"cpu_ms":48.024999999999984,"wall_ms":51.40440305694938},{"cpu_ms":45.4939999999997,"wall_ms":51.848994102329016},{"cpu_ms":28.566999999999787,"wall_ms":32.813258934766054},{"cpu_ms":46.52600000000007,"wall_ms":51.92889296449721},{"cpu_ms":45.48100000000011,"wall_ms":51.920568104833364},{"cpu_ms":45.722999999999736,"wall_ms":52.11870791390538},{"cpu_ms":44.811000000000156,"wall_ms":53.18516190163791},{"cpu_ms":44.90399999999983,"wall_ms":51.251045893877745},{"cpu_ms":48.07399999999973,"wall_ms":51.46160000003874},{"cpu_ms":49.603999999999985,"wall_ms":53.021995816379786},{"cpu_ms":45.35700000000009,"wall_ms":51.802756963297725},{"cpu_ms":43.81199999999996,"wall_ms":52.2039569914341},{"cpu_ms":46.29800000000017,"wall_ms":52.61765094473958},{"cpu_ms":44.4899999999997,"wall_ms":49.825199879705906},{"cpu_ms":40.19800000000018,"wall_ms":46.52451095171273},{"cpu_ms":47.711999999999755,"wall_ms":53.02275693975389},{"cpu_ms":48.28999999999972,"wall_ms":53.80339710973203},{"cpu_ms":43.880000000000585,"wall_ms":49.46720181033015},{"cpu_ms":48.486999999999725,"wall_ms":51.91103904508054},{"cpu_ms":45.98000000000013,"wall_ms":88.36123789660633},{"cpu_ms":42.88100000000039,"wall_ms":54.249491076916456},{"cpu_ms":49.732999999999805,"wall_ms":53.061881102621555},{"cpu_ms":46.71100000000017,"wall_ms":54.26402809098363},{"cpu_ms":49.31999999999981,"wall_ms":53.75966522842646},{"cpu_ms":47.96800000000001,"wall_ms":53.35376597940922},{"cpu_ms":36.00799999999982,"wall_ms":42.15846909210086},{"cpu_ms":45.28599999999905,"wall_ms":53.71874896809459},{"cpu_ms":49.5479999999997,"wall_ms":53.921767976135015},{"cpu_ms":50.014,"wall_ms":54.455285193398595},{"cpu_ms":44.83399999999982,"wall_ms":49.21319708228111},{"cpu_ms":45.1940000000004,"wall_ms":50.61633908189833},{"cpu_ms":48.13299999999998,"wall_ms":55.56219513528049},{"cpu_ms":51.50600000000072,"wall_ms":55.961581179872155},{"cpu_ms":46.027999999999736,"wall_ms":50.46378402039409},{"cpu_ms":48.3159999999998,"wall_ms":55.64915714785457},{"cpu_ms":49.39099999999996,"wall_ms":55.73569796979427},{"cpu_ms":29.972000000000776,"wall_ms":33.26768800616264},{"cpu_ms":50.391000000001185,"wall_ms":53.73839498497546},{"cpu_ms":42.785000000000295,"wall_ms":49.13611197844148},{"cpu_ms":48.92099999999999,"wall_ms":55.35152996890247},{"cpu_ms":46.490999999999616,"wall_ms":53.804788971319795},{"cpu_ms":45.567000000000135,"wall_ms":54.9168570432812},{"cpu_ms":44.52099999999959,"wall_ms":54.876531939953566},{"cpu_ms":47.579999999999956,"wall_ms":54.941443959251046},{"cpu_ms":49.33699999999952,"wall_ms":54.78932894766331},{"cpu_ms":50.47099999999993,"wall_ms":53.88660216704011},{"cpu_ms":48.24499999999965,"wall_ms":53.7188989110291},{"cpu_ms":49.569999999999226,"wall_ms":54.94640301913023},{"cpu_ms":35.59899999999949,"wall_ms":43.687385972589254},{"cpu_ms":50.47599999999974,"wall_ms":55.88987679220736},{"cpu_ms":43.245999999999896,"wall_ms":52.68054804764688},{"cpu_ms":47.86100000000104,"wall_ms":53.24260797351599},{"cpu_ms":45.76199999999986,"wall_ms":54.11146185360849},{"cpu_ms":42.84299999999952,"wall_ms":49.295430071651936},{"cpu_ms":46.371000000000606,"wall_ms":53.64803597331047},{"cpu_ms":48.833000000000126,"wall_ms":54.254649905487895},{"cpu_ms":47.825999999998814,"wall_ms":54.23888983204961},{"cpu_ms":48.31300000000027,"wall_ms":53.707505809143186},{"cpu_ms":47.06300000000141,"wall_ms":53.44425491057336},{"cpu_ms":47.535999999999135,"wall_ms":55.091494927182794},{"cpu_ms":46.03299999999955,"wall_ms":53.42970811761916},{"cpu_ms":46.14399999999996,"wall_ms":54.59858197718859},{"cpu_ms":47.33700000000063,"wall_ms":54.61315996944904},{"cpu_ms":48.54299999999867,"wall_ms":53.90594806522131},{"cpu_ms":48.95300000000091,"wall_ms":54.33334712870419},{"cpu_ms":45.78699999999891,"wall_ms":53.18933795206249},{"cpu_ms":48.807999999999296,"wall_ms":53.308750968426466},{"cpu_ms":50.071000000000865,"wall_ms":56.53558415360749},{"cpu_ms":46.88400000000037,"wall_ms":52.216429030522704},{"cpu_ms":48.12300000000036,"wall_ms":52.80506983399391},{"cpu_ms":51.420999999999495,"wall_ms":53.841687040403485},{"cpu_ms":41.30400000000023,"wall_ms":46.749039087444544},{"cpu_ms":34.56900000000118,"wall_ms":37.87878807634115},{"cpu_ms":46.42800000000058,"wall_ms":49.92740089073777},{"cpu_ms":44.67599999999905,"wall_ms":54.055066080763936},{"cpu_ms":47.0600000000001,"wall_ms":53.45600703731179},{"cpu_ms":45.937000000002115,"wall_ms":53.32631408236921},{"cpu_ms":46.82599999999937,"wall_ms":54.25168317742646},{"cpu_ms":49.99600000000015,"wall_ms":54.36886311508715},{"cpu_ms":40.04399999999819,"wall_ms":49.48552418500185},{"cpu_ms":46.83700000000002,"wall_ms":53.188817109912634},{"cpu_ms":45.53500000000099,"wall_ms":54.87197288312018},{"cpu_ms":46.73199999999866,"wall_ms":54.13411417976022},{"cpu_ms":47.786000000002105,"wall_ms":58.37926990352571},{"cpu_ms":47.35499999999959,"wall_ms":53.80126181989908},{"cpu_ms":47.33900000000091,"wall_ms":55.65472599118948},{"cpu_ms":44.819000000000386,"wall_ms":51.20596010237932},{"cpu_ms":44.20000000000002,"wall_ms":52.449513925239444},{"cpu_ms":46.499000000000734,"wall_ms":52.98565700650215},{"cpu_ms":43.963999999999004,"wall_ms":52.39766789600253},{"cpu_ms":44.597000000003106,"wall_ms":50.933439983054996},{"cpu_ms":46.897999999998774,"wall_ms":52.304103039205074},{"cpu_ms":49.399999999998556,"wall_ms":53.86680318042636},{"cpu_ms":34.64400000000012,"wall_ms":41.12222092226148},{"cpu_ms":46.03799999999936,"wall_ms":54.4788280967623},{"cpu_ms":48.20800000000247,"wall_ms":53.504988085478544},{"cpu_ms":48.20399999999836,"wall_ms":53.62844909541309},{"cpu_ms":45.68599999999989,"wall_ms":55.02146203070879}],"before_control_median_cpu_ms":46.61850000000012,"after_control":[{"cpu_ms":42.20499999999983,"wall_ms":50.63363187946379},{"cpu_ms":40.77799999999998,"wall_ms":56.037313072010875},{"cpu_ms":48.24899999999999,"wall_ms":52.638890920206904},{"cpu_ms":31.414000000000275,"wall_ms":36.859313026070595},{"cpu_ms":43.63099999999997,"wall_ms":50.11840793304145},{"cpu_ms":51.30699999999999,"wall_ms":56.58936593681574},{"cpu_ms":49.408000000000115,"wall_ms":51.83495790697634},{"cpu_ms":47.23800000000011,"wall_ms":52.65521095134318},{"cpu_ms":47.06100000000024,"wall_ms":52.41285800002515},{"cpu_ms":45.66200000000009,"wall_ms":51.09078693203628},{"cpu_ms":45.47500000000015,"wall_ms":51.950623048469424},{"cpu_ms":48.52800000000013,"wall_ms":53.96958813071251},{"cpu_ms":47.272999999999676,"wall_ms":53.62710799090564},{"cpu_ms":48.38300000000029,"wall_ms":53.82369505241513},{"cpu_ms":45.547000000000004,"wall_ms":53.06717101484537},{"cpu_ms":45.736000000000665,"wall_ms":50.09082704782486},{"cpu_ms":33.91400000000022,"wall_ms":37.24990296177566},{"cpu_ms":40.98500000000005,"wall_ms":48.39532799087465},{"cpu_ms":46.466999999999814,"wall_ms":53.91969415359199},{"cpu_ms":45.17199999999999,"wall_ms":49.50952995568514},{"cpu_ms":47.20200000000041,"wall_ms":56.63288594223559},{"cpu_ms":49.577000000000204,"wall_ms":58.88087907806039},{"cpu_ms":52.91699999999988,"wall_ms":79.27942997775972},{"cpu_ms":48.22099999999985,"wall_ms":54.65875589288771},{"cpu_ms":48.186999999999536,"wall_ms":53.58061986044049},{"cpu_ms":50.07900000000021,"wall_ms":54.52090781182051},{"cpu_ms":47.96200000000006,"wall_ms":54.34452788904309},{"cpu_ms":46.89800000000055,"wall_ms":53.31737804226577},{"cpu_ms":49.51800000000084,"wall_ms":54.84953289851546},{"cpu_ms":49.33299999999985,"wall_ms":53.68443392217159},{"cpu_ms":48.91799999999957,"wall_ms":56.325291050598025},{"cpu_ms":47.67600000000005,"wall_ms":55.0423520617187},{"cpu_ms":46.634000000000064,"wall_ms":62.84419703297317},{"cpu_ms":49.85099999999942,"wall_ms":56.2404019292444},{"cpu_ms":49.93799999999915,"wall_ms":56.32707406766713},{"cpu_ms":43.984999999999275,"wall_ms":54.351561004295945},{"cpu_ms":51.84099999999958,"wall_ms":56.31029303185642},{"cpu_ms":51.80999999999969,"wall_ms":56.1807700432837},{"cpu_ms":30.46199999999999,"wall_ms":33.70849299244583},{"cpu_ms":50.305999999999074,"wall_ms":57.606655871495605},{"cpu_ms":49.86800000000002,"wall_ms":55.23837800137699},{"cpu_ms":49.44299999999835,"wall_ms":55.80360605381429},{"cpu_ms":48.64500000000049,"wall_ms":53.99345117621124},{"cpu_ms":51.593999999999696,"wall_ms":56.04918487370014},{"cpu_ms":47.35400000000034,"wall_ms":54.63936901651323},{"cpu_ms":49.00800000000061,"wall_ms":55.42390514165163},{"cpu_ms":47.61700000000069,"wall_ms":54.93753612972796},{"cpu_ms":48.81000000000135,"wall_ms":56.20661890134215},{"cpu_ms":48.31700000000083,"wall_ms":56.63524009287357},{"cpu_ms":48.44200000000143,"wall_ms":55.920973885804415},{"cpu_ms":45.541000000000054,"wall_ms":53.094431990757585},{"cpu_ms":51.29099999999909,"wall_ms":53.69352106936276},{"cpu_ms":50.190999999999875,"wall_ms":54.51712990179658},{"cpu_ms":47.94300000000007,"wall_ms":55.349597008898854},{"cpu_ms":47.49300000000112,"wall_ms":54.79910806752741},{"cpu_ms":44.77400000000031,"wall_ms":51.122595090419054},{"cpu_ms":51.33099999999935,"wall_ms":56.740185944363475},{"cpu_ms":49.88800000000104,"wall_ms":55.29158818535507},{"cpu_ms":46.41499999999965,"wall_ms":54.676068015396595},{"cpu_ms":46.86900000000094,"wall_ms":54.22838102094829},{"cpu_ms":47.7489999999996,"wall_ms":55.097725009545684},{"cpu_ms":48.385000000001455,"wall_ms":54.838751908391714},{"cpu_ms":46.654000000000195,"wall_ms":53.985266014933586},{"cpu_ms":54.078000000000515,"wall_ms":55.54495099931955},{"cpu_ms":48.346000000000444,"wall_ms":54.76095620542765},{"cpu_ms":48.90900000000009,"wall_ms":55.38267805241048},{"cpu_ms":50.60399999999987,"wall_ms":56.09313608147204},{"cpu_ms":48.78400000000127,"wall_ms":55.18966796807945},{"cpu_ms":47.5209999999997,"wall_ms":54.87204412929714},{"cpu_ms":48.922999999998495,"wall_ms":57.35450400970876},{"cpu_ms":50.74999999999896,"wall_ms":55.15761696733534},{"cpu_ms":47.29399999999906,"wall_ms":54.75532519631088},{"cpu_ms":39.3340000000002,"wall_ms":48.66074281744659},{"cpu_ms":42.70599999999902,"wall_ms":48.02382201887667},{"cpu_ms":46.656000000000475,"wall_ms":53.98886208422482},{"cpu_ms":42.06699999999941,"wall_ms":49.48984202928841},{"cpu_ms":47.68100000000075,"wall_ms":54.138723062351346},{"cpu_ms":50.42399999999958,"wall_ms":53.82129899226129},{"cpu_ms":48.24999999999946,"wall_ms":53.762157913297415},{"cpu_ms":50.373000000000445,"wall_ms":53.79596212878823},{"cpu_ms":48.648000000000025,"wall_ms":55.002777837216854},{"cpu_ms":48.44800000000049,"wall_ms":52.77538392692804},{"cpu_ms":49.32799999999915,"wall_ms":53.7412001285702},{"cpu_ms":48.48400000000197,"wall_ms":55.82844093441963},{"cpu_ms":47.61099999999985,"wall_ms":53.95959806628525},{"cpu_ms":49.05899999999974,"wall_ms":54.45533408783376},{"cpu_ms":48.17000000000249,"wall_ms":54.57455711439252},{"cpu_ms":50.89600000000161,"wall_ms":54.291306994855404},{"cpu_ms":45.29099999999886,"wall_ms":51.71342915855348},{"cpu_ms":48.22200000000265,"wall_ms":52.65114107169211},{"cpu_ms":45.807999999997406,"wall_ms":53.23975207284093},{"cpu_ms":47.54700000000156,"wall_ms":51.9844158552587},{"cpu_ms":44.896999999998854,"wall_ms":52.25989990867674},{"cpu_ms":47.81099999999938,"wall_ms":53.18792490288615},{"cpu_ms":49.22000000000182,"wall_ms":53.6297510843724},{"cpu_ms":47.60300000000228,"wall_ms":54.92679588496685},{"cpu_ms":50.304999999998046,"wall_ms":54.729617200791836},{"cpu_ms":48.65699999999862,"wall_ms":53.97762102074921},{"cpu_ms":49.47100000000049,"wall_ms":55.83638604730368},{"cpu_ms":50.667000000000684,"wall_ms":55.02695287577808}],"after_control_median_cpu_ms":48.23550000000132}, + {"name":"string_split_map_join","node_exit":0,"before":[{"cpu_ms":315.0149999999998,"wall_ms":322.3630338907242},{"cpu_ms":198.87199999999973,"wall_ms":205.2096389234066},{"cpu_ms":300.42300000000034,"wall_ms":305.84017699584365},{"cpu_ms":234.43599999999964,"wall_ms":239.79508900083601},{"cpu_ms":297.15700000000123,"wall_ms":302.58120503276587},{"cpu_ms":300.75699999999904,"wall_ms":308.1798190250993},{"cpu_ms":304.2739999999995,"wall_ms":310.6952409725636},{"cpu_ms":288.0869999999991,"wall_ms":295.47037999145687},{"cpu_ms":303.46899999999977,"wall_ms":312.8274050541222},{"cpu_ms":299.46799999999917,"wall_ms":302.87324800156057},{"cpu_ms":304.31700000000103,"wall_ms":311.7671918589622},{"cpu_ms":298.4139999999993,"wall_ms":303.89753193594515},{"cpu_ms":296.4199999999977,"wall_ms":303.7903611548245},{"cpu_ms":293.6540000000001,"wall_ms":302.0163639448583},{"cpu_ms":293.4570000000001,"wall_ms":303.83442202582955},{"cpu_ms":287.35499999999803,"wall_ms":294.792870990932},{"cpu_ms":294.6770000000001,"wall_ms":300.0746627803892},{"cpu_ms":302.9650000000004,"wall_ms":307.3718829546124},{"cpu_ms":299.7060000000005,"wall_ms":308.11432003974915},{"cpu_ms":294.96199999999817,"wall_ms":301.3258909340948},{"cpu_ms":167.67600000000016,"wall_ms":173.0109651107341},{"cpu_ms":171.05300000000057,"wall_ms":175.36218906752765},{"cpu_ms":172.92799999999886,"wall_ms":179.49413298629224},{"cpu_ms":302.41499999999985,"wall_ms":306.8108221050352},{"cpu_ms":312.71699999999925,"wall_ms":317.1678311191499},{"cpu_ms":303.8089999999976,"wall_ms":310.22915011271834},{"cpu_ms":208.97000000000077,"wall_ms":217.34487498179078},{"cpu_ms":188.50400000000178,"wall_ms":194.8319219518453},{"cpu_ms":167.51100000000463,"wall_ms":172.78826609253883},{"cpu_ms":198.8370000000046,"wall_ms":201.13430195488036},{"cpu_ms":185.14700000000062,"wall_ms":194.57903900183737},{"cpu_ms":253.31100000000362,"wall_ms":262.691818876192},{"cpu_ms":199.9759999999995,"wall_ms":206.3611620105803},{"cpu_ms":269.95500000000305,"wall_ms":275.35280399024487},{"cpu_ms":263.0979999999994,"wall_ms":272.5161910057068},{"cpu_ms":218.29699999999974,"wall_ms":221.5891450177878},{"cpu_ms":216.6130000000024,"wall_ms":224.04477605596185},{"cpu_ms":187.69200000000552,"wall_ms":192.0994019601494},{"cpu_ms":215.81199999999967,"wall_ms":222.07929100841284},{"cpu_ms":184.66899999999953,"wall_ms":190.37305400706828},{"cpu_ms":244.22400000000266,"wall_ms":247.62720591388643},{"cpu_ms":310.2909999999994,"wall_ms":315.7151679042727},{"cpu_ms":308.33599999999706,"wall_ms":310.7187778223306},{"cpu_ms":271.5029999999956,"wall_ms":281.4510490279645},{"cpu_ms":311.2339999999989,"wall_ms":316.5965450461954},{"cpu_ms":266.3610000000034,"wall_ms":272.74418296292424},{"cpu_ms":283.23599999999516,"wall_ms":289.662305964157},{"cpu_ms":291.0140000000041,"wall_ms":300.4233620595187},{"cpu_ms":306.8150000000003,"wall_ms":313.21032298728824},{"cpu_ms":298.6360000000019,"wall_ms":306.0375051572919},{"cpu_ms":303.21200000000204,"wall_ms":309.65338996611536},{"cpu_ms":307.38999999999805,"wall_ms":312.7936131786555},{"cpu_ms":253.7590000000023,"wall_ms":262.1548059396446},{"cpu_ms":312.973999999997,"wall_ms":319.39183408394456},{"cpu_ms":301.29699999999815,"wall_ms":307.7802690677345},{"cpu_ms":306.5649999999991,"wall_ms":311.95086520165205},{"cpu_ms":296.83700000000357,"wall_ms":301.2004499323666},{"cpu_ms":261.74300000000983,"wall_ms":268.0583100300282},{"cpu_ms":217.5500000000028,"wall_ms":223.0153949931264},{"cpu_ms":185.66300000000524,"wall_ms":190.96164894290268},{"cpu_ms":243.938,"wall_ms":250.3488261718303},{"cpu_ms":308.2640000000083,"wall_ms":317.6622479222715},{"cpu_ms":307.01799999999935,"wall_ms":313.4340450633317},{"cpu_ms":299.61099999999874,"wall_ms":308.06929687969387},{"cpu_ms":296.0680000000053,"wall_ms":306.481126928702},{"cpu_ms":226.08899999998755,"wall_ms":232.52100008539855},{"cpu_ms":306.3539999999989,"wall_ms":312.81625805422664},{"cpu_ms":231.4879999999988,"wall_ms":241.74399906769395},{"cpu_ms":306.3769999999977,"wall_ms":311.7808820679784},{"cpu_ms":208.59500000000253,"wall_ms":216.11419110558927},{"cpu_ms":293.71500000000594,"wall_ms":302.12353891693056},{"cpu_ms":211.29700000000184,"wall_ms":221.70647396706045},{"cpu_ms":296.087,"wall_ms":300.55441497825086},{"cpu_ms":227.67999999999233,"wall_ms":235.62340601347387},{"cpu_ms":234.2129999999969,"wall_ms":238.01182000897825},{"cpu_ms":252.39899999999693,"wall_ms":258.8013899512589},{"cpu_ms":244.37399999999343,"wall_ms":251.70712894760072},{"cpu_ms":297.19700000001126,"wall_ms":306.57592904753983},{"cpu_ms":297.1679999999992,"wall_ms":307.60751687921584},{"cpu_ms":306.88699999998903,"wall_ms":311.25893210992217},{"cpu_ms":301.78300000000036,"wall_ms":310.14932715334},{"cpu_ms":313.0689999999987,"wall_ms":318.54252493940294},{"cpu_ms":302.89199999999994,"wall_ms":309.3073219060898},{"cpu_ms":184.68000000000018,"wall_ms":192.06103682518005},{"cpu_ms":211.9289999999978,"wall_ms":219.31500895880163},{"cpu_ms":186.35399999999436,"wall_ms":189.6638059988618},{"cpu_ms":259.9170000000015,"wall_ms":267.3245270270854},{"cpu_ms":290.66199999999753,"wall_ms":297.0500399824232},{"cpu_ms":301.29199999998946,"wall_ms":310.74955593794584},{"cpu_ms":310.3349999999949,"wall_ms":314.7448229137808},{"cpu_ms":304.7300000000064,"wall_ms":310.1249870378524},{"cpu_ms":291.15699999999833,"wall_ms":297.5770130287856},{"cpu_ms":303.87199999999837,"wall_ms":309.27541805431247},{"cpu_ms":317.3400000000015,"wall_ms":324.7889850754291},{"cpu_ms":316.5439999999933,"wall_ms":322.001364082098},{"cpu_ms":318.79199999998775,"wall_ms":324.19681805185974},{"cpu_ms":170.57499999999948,"wall_ms":173.88617107644677},{"cpu_ms":302.8049999999922,"wall_ms":310.1797769777477},{"cpu_ms":303.1080000000088,"wall_ms":307.53991194069386},{"cpu_ms":295.7980000000049,"wall_ms":301.2213911861181}],"before_median_cpu_ms":294.81949999999915,"after":[{"cpu_ms":317.43100000000004,"wall_ms":324.8779261484742},{"cpu_ms":205.1740000000004,"wall_ms":211.89905307255685},{"cpu_ms":300.16399999999965,"wall_ms":307.58851603604853},{"cpu_ms":294.29900000000055,"wall_ms":301.68013111688197},{"cpu_ms":236.67199999999954,"wall_ms":239.0563879162073},{"cpu_ms":281.5670000000008,"wall_ms":286.98360710404813},{"cpu_ms":285.3960000000004,"wall_ms":294.85283698886633},{"cpu_ms":211.49800000000062,"wall_ms":219.43276398815215},{"cpu_ms":316.5699999999987,"wall_ms":322.9644950479269},{"cpu_ms":316.16600000000085,"wall_ms":320.55374793708324},{"cpu_ms":300.395,"wall_ms":308.77168313600123},{"cpu_ms":300.4759999999998,"wall_ms":305.8298339601606},{"cpu_ms":283.7069999999997,"wall_ms":287.0812970213592},{"cpu_ms":300.47899999999925,"wall_ms":306.88073579221964},{"cpu_ms":307.9060000000027,"wall_ms":311.31715909577906},{"cpu_ms":281.8269999999998,"wall_ms":291.2285579368472},{"cpu_ms":292.91699999999923,"wall_ms":303.3219629433006},{"cpu_ms":303.4590000000002,"wall_ms":305.8553480077535},{"cpu_ms":302.4090000000008,"wall_ms":308.818164980039},{"cpu_ms":301.06500000000125,"wall_ms":308.4574011154473},{"cpu_ms":193.4919999999991,"wall_ms":200.80465893261135},{"cpu_ms":170.52599999999885,"wall_ms":175.8321369998157},{"cpu_ms":303.28700000000094,"wall_ms":310.7331439387053},{"cpu_ms":295.5509999999997,"wall_ms":301.9958110526204},{"cpu_ms":297.773000000003,"wall_ms":306.193026015535},{"cpu_ms":304.60500000000224,"wall_ms":310.99090399220586},{"cpu_ms":169.63999999999402,"wall_ms":173.93458890728652},{"cpu_ms":182.67800000000278,"wall_ms":187.06233892589808},{"cpu_ms":199.3579999999966,"wall_ms":203.66941089741886},{"cpu_ms":234.9089999999947,"wall_ms":242.31666303239763},{"cpu_ms":286.7169999999959,"wall_ms":296.1379480548203},{"cpu_ms":262.4479999999991,"wall_ms":267.7994859404862},{"cpu_ms":296.4589999999987,"wall_ms":304.43225195631385},{"cpu_ms":229.5999999999978,"wall_ms":236.96163087151945},{"cpu_ms":203.24899999999957,"wall_ms":207.58605003356934},{"cpu_ms":231.16600000000176,"wall_ms":236.4402071107179},{"cpu_ms":224.4449999999958,"wall_ms":228.76015002839267},{"cpu_ms":288.50099999999657,"wall_ms":291.8496700003743},{"cpu_ms":199.28600000000074,"wall_ms":203.6536391824484},{"cpu_ms":277.1550000000005,"wall_ms":289.3562079407275},{"cpu_ms":238.19100000000049,"wall_ms":247.63401900418103},{"cpu_ms":290.02999999999446,"wall_ms":295.45334400609136},{"cpu_ms":306.9370000000049,"wall_ms":313.38358018547297},{"cpu_ms":304.786,"wall_ms":311.1554440110922},{"cpu_ms":317.3699999999968,"wall_ms":319.7424749378115},{"cpu_ms":280.65199999999635,"wall_ms":287.0227328967303},{"cpu_ms":278.04199999999923,"wall_ms":283.4689400624484},{"cpu_ms":276.55599999999936,"wall_ms":282.9335380811244},{"cpu_ms":313.6829999999975,"wall_ms":320.07483509369195},{"cpu_ms":312.3210000000043,"wall_ms":318.75182990916073},{"cpu_ms":315.5450000000002,"wall_ms":324.01776290498674},{"cpu_ms":273.0840000000043,"wall_ms":284.59145594388247},{"cpu_ms":307.7549999999931,"wall_ms":310.10361295193434},{"cpu_ms":303.0329999999992,"wall_ms":308.5007471963763},{"cpu_ms":304.56399999999917,"wall_ms":311.9481310714036},{"cpu_ms":238.17499999999825,"wall_ms":243.56901389546692},{"cpu_ms":258.8259999999991,"wall_ms":261.21024903841317},{"cpu_ms":228.06099999999674,"wall_ms":233.3815610036254},{"cpu_ms":215.66099999999722,"wall_ms":222.94507292099297},{"cpu_ms":187.90599999999813,"wall_ms":191.225792048499},{"cpu_ms":187.63199999999358,"wall_ms":194.70846187323332},{"cpu_ms":295.3890000000001,"wall_ms":300.725539913401},{"cpu_ms":308.5229999999939,"wall_ms":311.9273080956191},{"cpu_ms":288.25700000000154,"wall_ms":298.5949469730258},{"cpu_ms":313.21599999999705,"wall_ms":317.60354805737734},{"cpu_ms":308.4029999999984,"wall_ms":314.8157161194831},{"cpu_ms":304.77000000000487,"wall_ms":312.2118879109621},{"cpu_ms":249.94399999999928,"wall_ms":256.35327701456845},{"cpu_ms":311.4559999999926,"wall_ms":315.8182178158313},{"cpu_ms":310.9979999999979,"wall_ms":316.4178691804409},{"cpu_ms":264.7759999999977,"wall_ms":275.1771511975676},{"cpu_ms":302.85099999998977,"wall_ms":306.29613902419806},{"cpu_ms":311.0070000000036,"wall_ms":317.41635384969413},{"cpu_ms":309.9069999999955,"wall_ms":313.28694499097764},{"cpu_ms":311.0860000000031,"wall_ms":315.5442338902503},{"cpu_ms":249.03500000000633,"wall_ms":252.3857681080699},{"cpu_ms":255.57299999999827,"wall_ms":262.9531300626695},{"cpu_ms":274.7279999999961,"wall_ms":284.1841340996325},{"cpu_ms":305.66500000000474,"wall_ms":312.0968481525779},{"cpu_ms":306.01400000000467,"wall_ms":314.4507869146764},{"cpu_ms":189.65000000000032,"wall_ms":193.97209002636373},{"cpu_ms":286.50899999999524,"wall_ms":296.89280688762665},{"cpu_ms":304.6989999999994,"wall_ms":314.0915250405669},{"cpu_ms":317.5990000000013,"wall_ms":323.0148639995605},{"cpu_ms":308.06900000000326,"wall_ms":315.48547907732427},{"cpu_ms":195.5990000000014,"wall_ms":201.03945885784924},{"cpu_ms":284.3039999999917,"wall_ms":291.6952499654144},{"cpu_ms":258.36500000001195,"wall_ms":266.73473487608135},{"cpu_ms":305.73900000000265,"wall_ms":312.1302668005228},{"cpu_ms":285.7259999999968,"wall_ms":288.0989881232381},{"cpu_ms":310.6039999999979,"wall_ms":316.9670361094177},{"cpu_ms":308.8950000000068,"wall_ms":316.2928051315248},{"cpu_ms":303.43400000001,"wall_ms":308.8575878646225},{"cpu_ms":323.23000000000945,"wall_ms":325.6366499699652},{"cpu_ms":315.4010000000085,"wall_ms":321.78696407936513},{"cpu_ms":257.39200000001006,"wall_ms":265.5407900456339},{"cpu_ms":169.13099999999304,"wall_ms":174.4430330581963},{"cpu_ms":296.1790000000093,"wall_ms":302.59126308374107},{"cpu_ms":302.29199999999423,"wall_ms":309.6947888843715},{"cpu_ms":303.82500000000334,"wall_ms":314.1966238617897}],"after_median_cpu_ms":294.84400000000034,"before_control":[{"cpu_ms":316.9689999999998,"wall_ms":323.3604491688311},{"cpu_ms":235.0259999999995,"wall_ms":245.52176403813064},{"cpu_ms":225.17500000000013,"wall_ms":231.55219107866287},{"cpu_ms":297.06900000000047,"wall_ms":302.46346397325397},{"cpu_ms":263.8649999999991,"wall_ms":268.2500188238919},{"cpu_ms":304.5989999999996,"wall_ms":307.98181891441345},{"cpu_ms":208.9750000000006,"wall_ms":214.5738520193845},{"cpu_ms":271.23700000000105,"wall_ms":281.66444413363934},{"cpu_ms":300.44200000000035,"wall_ms":310.89655589312315},{"cpu_ms":299.45099999999945,"wall_ms":302.8669161722064},{"cpu_ms":299.54399999999913,"wall_ms":311.9266708381474},{"cpu_ms":171.51499999999942,"wall_ms":181.89260899089277},{"cpu_ms":308.42000000000166,"wall_ms":314.8506961297244},{"cpu_ms":284.49900000000025,"wall_ms":290.86808511056006},{"cpu_ms":294.51800000000003,"wall_ms":299.90637907758355},{"cpu_ms":276.29699999999957,"wall_ms":282.64882997609675},{"cpu_ms":298.7290000000016,"wall_ms":304.178805090487},{"cpu_ms":303.76799999999804,"wall_ms":308.16924409009516},{"cpu_ms":301.4359999999989,"wall_ms":309.83950407244265},{"cpu_ms":245.85599999999985,"wall_ms":252.22708098590374},{"cpu_ms":188.639000000002,"wall_ms":261.80507405661047},{"cpu_ms":174.94699999999952,"wall_ms":179.2473418172449},{"cpu_ms":170.458,"wall_ms":173.76895993947983},{"cpu_ms":284.8799999999976,"wall_ms":289.24554702825844},{"cpu_ms":305.3689999999989,"wall_ms":311.7785770446062},{"cpu_ms":294.73899999999986,"wall_ms":302.1420028526336},{"cpu_ms":266.52500000000146,"wall_ms":275.98392590880394},{"cpu_ms":265.2360000000016,"wall_ms":272.59775600396097},{"cpu_ms":198.65899999999925,"wall_ms":204.01501702144742},{"cpu_ms":231.57700000000148,"wall_ms":236.87646398320794},{"cpu_ms":241.22799999999955,"wall_ms":245.55079801939428},{"cpu_ms":261.3630000000029,"wall_ms":265.7624480780214},{"cpu_ms":167.46599999999745,"wall_ms":172.7318000048399},{"cpu_ms":221.935000000002,"wall_ms":227.33172099106014},{"cpu_ms":267.040999999999,"wall_ms":275.42020892724395},{"cpu_ms":200.1419999999996,"wall_ms":207.36480690538883},{"cpu_ms":207.3350000000005,"wall_ms":214.7897758986801},{"cpu_ms":228.000999999999,"wall_ms":236.398738110438},{"cpu_ms":182.2919999999968,"wall_ms":187.60757497511804},{"cpu_ms":263.0049999999997,"wall_ms":271.4045620523393},{"cpu_ms":267.0170000000027,"wall_ms":271.43505890853703},{"cpu_ms":287.10799999999637,"wall_ms":290.4614941217005},{"cpu_ms":313.74199999999774,"wall_ms":316.14801683463156},{"cpu_ms":256.88399999999945,"wall_ms":263.9962718822062},{"cpu_ms":308.02900000000477,"wall_ms":316.4398411754519},{"cpu_ms":310.9090000000023,"wall_ms":316.24412606470287},{"cpu_ms":262.4240000000029,"wall_ms":265.9150979015976},{"cpu_ms":311.36699999999706,"wall_ms":317.82523007132113},{"cpu_ms":305.1969999999997,"wall_ms":313.63874301314354},{"cpu_ms":308.59499999999684,"wall_ms":316.97420799173415},{"cpu_ms":305.91899999999583,"wall_ms":310.2753658313304},{"cpu_ms":276.76200000000506,"wall_ms":281.1492320615798},{"cpu_ms":268.5650000000024,"wall_ms":274.9427300877869},{"cpu_ms":271.5919999999983,"wall_ms":277.09199697710574},{"cpu_ms":302.8189999999995,"wall_ms":310.25860202498734},{"cpu_ms":308.14800000000275,"wall_ms":313.5142680257559},{"cpu_ms":253.64299999999673,"wall_ms":259.05643310397863},{"cpu_ms":235.84999999999923,"wall_ms":239.2672230489552},{"cpu_ms":243.16400000000726,"wall_ms":250.52580703049898},{"cpu_ms":183.19600000000946,"wall_ms":187.48394795693457},{"cpu_ms":228.02900000000648,"wall_ms":234.43232499994338},{"cpu_ms":249.60899999999242,"wall_ms":255.0293740350753},{"cpu_ms":307.4090000000069,"wall_ms":312.8031447995454},{"cpu_ms":303.69300000000976,"wall_ms":314.11667983047664},{"cpu_ms":306.92999999999415,"wall_ms":317.35842884518206},{"cpu_ms":280.3250000000048,"wall_ms":287.7341350540519},{"cpu_ms":304.553999999996,"wall_ms":310.95597008243203},{"cpu_ms":274.1100000000074,"wall_ms":280.55337094701827},{"cpu_ms":307.1040000000096,"wall_ms":313.4974099230021},{"cpu_ms":280.0540000000069,"wall_ms":286.4261781796813},{"cpu_ms":172.12600000000577,"wall_ms":177.46903491206467},{"cpu_ms":299.5820000000009,"wall_ms":306.99703888967633},{"cpu_ms":301.132999999993,"wall_ms":305.5150890722871},{"cpu_ms":300.5539999999911,"wall_ms":308.9722499717027},{"cpu_ms":302.62399999999445,"wall_ms":312.06736504100263},{"cpu_ms":235.36599999999908,"wall_ms":246.41576805152},{"cpu_ms":174.54800000000148,"wall_ms":182.93453892692924},{"cpu_ms":295.8459999999974,"wall_ms":299.2073658388108},{"cpu_ms":200.05600000000356,"wall_ms":207.89009588770568},{"cpu_ms":283.03100000000825,"wall_ms":290.3351809363812},{"cpu_ms":307.5410000000005,"wall_ms":311.9577670004219},{"cpu_ms":303.6869999999965,"wall_ms":310.0936929695308},{"cpu_ms":210.86699999999325,"wall_ms":220.26281198486686},{"cpu_ms":309.3549999999965,"wall_ms":314.74093813449144},{"cpu_ms":303.51400000000694,"wall_ms":311.95184402167797},{"cpu_ms":291.6270000000054,"wall_ms":298.008891986683},{"cpu_ms":246.1320000000029,"wall_ms":252.5352828670293},{"cpu_ms":306.7500000000081,"wall_ms":313.1713119801134},{"cpu_ms":297.84100000000535,"wall_ms":306.2644221354276},{"cpu_ms":231.53399999999635,"wall_ms":237.80319606885314},{"cpu_ms":203.05199999999957,"wall_ms":211.27003012225032},{"cpu_ms":305.4819999999978,"wall_ms":307.8840470407158},{"cpu_ms":297.9649999999907,"wall_ms":306.11689388751984},{"cpu_ms":296.244999999999,"wall_ms":302.59526101872325},{"cpu_ms":318.5990000000061,"wall_ms":325.0098479911685},{"cpu_ms":168.91100000000847,"wall_ms":173.23846113868058},{"cpu_ms":176.29700000000526,"wall_ms":184.53447497449815},{"cpu_ms":173.11199999998905,"wall_ms":178.43199893832207},{"cpu_ms":301.1399999999895,"wall_ms":308.5143130738288},{"cpu_ms":306.05799999999306,"wall_ms":311.4773619454354}],"before_control_median_cpu_ms":280.18950000000586,"after_control":[{"cpu_ms":320.4280000000006,"wall_ms":325.85006207227707},{"cpu_ms":268.02499999999975,"wall_ms":273.45403796061873},{"cpu_ms":297.7400000000001,"wall_ms":304.1634929832071},{"cpu_ms":301.49699999999945,"wall_ms":305.9028540737927},{"cpu_ms":279.9770000000006,"wall_ms":283.34881318733096},{"cpu_ms":299.1499999999991,"wall_ms":304.55892090685666},{"cpu_ms":295.64000000000055,"wall_ms":299.05020422302186},{"cpu_ms":246.6039999999996,"wall_ms":256.01774198003113},{"cpu_ms":305.613000000001,"wall_ms":310.9990779776126},{"cpu_ms":316.88399999999996,"wall_ms":321.3136082049459},{"cpu_ms":313.15200000000056,"wall_ms":314.52580890618265},{"cpu_ms":294.119000000002,"wall_ms":302.5106689892709},{"cpu_ms":288.5869999999997,"wall_ms":297.0154760405421},{"cpu_ms":292.66799999999904,"wall_ms":297.090234933421},{"cpu_ms":292.2229999999999,"wall_ms":298.6131520010531},{"cpu_ms":229.4860000000014,"wall_ms":235.90673599392176},{"cpu_ms":297.92899999999986,"wall_ms":303.7927320692688},{"cpu_ms":301.24900000000207,"wall_ms":308.7030609603971},{"cpu_ms":307.1079999999995,"wall_ms":310.45819190330803},{"cpu_ms":301.3429999999993,"wall_ms":305.737285874784},{"cpu_ms":173.29499999999953,"wall_ms":175.604262156412},{"cpu_ms":175.09599999999992,"wall_ms":176.39421904459596},{"cpu_ms":170.96599999999995,"wall_ms":175.28029601089656},{"cpu_ms":290.50400000000207,"wall_ms":298.9038110245019},{"cpu_ms":306.36600000000055,"wall_ms":311.81441410444677},{"cpu_ms":309.79999999999916,"wall_ms":318.1965039111674},{"cpu_ms":309.96500000000185,"wall_ms":317.33617512509227},{"cpu_ms":321.672999999997,"wall_ms":329.0438980329782},{"cpu_ms":170.33599999999893,"wall_ms":174.654183909297},{"cpu_ms":195.1400000000021,"wall_ms":200.47455886378884},{"cpu_ms":224.35099999999863,"wall_ms":228.71848405338824},{"cpu_ms":295.0739999999996,"wall_ms":298.44420892186463},{"cpu_ms":261.6530000000026,"wall_ms":268.02895381115377},{"cpu_ms":266.7029999999997,"wall_ms":275.0537861138582},{"cpu_ms":253.68999999999886,"wall_ms":257.0907580666244},{"cpu_ms":269.92500000000064,"wall_ms":278.0225141905248},{"cpu_ms":224.02600000000206,"wall_ms":227.44591301307082},{"cpu_ms":244.6249999999992,"wall_ms":254.01525804772973},{"cpu_ms":180.85800000000063,"wall_ms":185.18074811436236},{"cpu_ms":295.40999999999684,"wall_ms":302.76891007088125},{"cpu_ms":248.84099999999876,"wall_ms":254.24566701985896},{"cpu_ms":185.12000000000484,"wall_ms":190.54485484957695},{"cpu_ms":307.7920000000063,"wall_ms":313.1878739222884},{"cpu_ms":244.62000000000472,"wall_ms":253.03235789760947},{"cpu_ms":313.31699999999785,"wall_ms":319.6885238867253},{"cpu_ms":303.4859999999995,"wall_ms":307.8578789718449},{"cpu_ms":179.60399999999765,"wall_ms":184.94147900491953},{"cpu_ms":249.01700000000204,"wall_ms":255.62091614119709},{"cpu_ms":309.02000000000385,"wall_ms":316.4478549733758},{"cpu_ms":308.8079999999991,"wall_ms":315.1824588421732},{"cpu_ms":307.1509999999975,"wall_ms":314.47743996977806},{"cpu_ms":191.27799999999695,"wall_ms":198.63372202962637},{"cpu_ms":308.1639999999979,"wall_ms":315.5769139993936},{"cpu_ms":312.6190000000051,"wall_ms":319.01209498755634},{"cpu_ms":298.80400000000407,"wall_ms":306.1748289037496},{"cpu_ms":306.67700000000053,"wall_ms":317.0660771429539},{"cpu_ms":293.3319999999995,"wall_ms":295.7098768092692},{"cpu_ms":279.65700000000027,"wall_ms":284.08955596387386},{"cpu_ms":194.72599999998863,"wall_ms":203.59777798876166},{"cpu_ms":177.11299999999142,"wall_ms":181.41223909333348},{"cpu_ms":309.9160000000012,"wall_ms":316.3657148834318},{"cpu_ms":212.35099999999818,"wall_ms":216.73344098962843},{"cpu_ms":303.96399999999346,"wall_ms":313.3819969370961},{"cpu_ms":316.36899999999457,"wall_ms":319.79497405700386},{"cpu_ms":284.33599999999615,"wall_ms":290.7119309529662},{"cpu_ms":309.14300000000594,"wall_ms":315.55788312107325},{"cpu_ms":198.41500000001133,"wall_ms":204.8120959661901},{"cpu_ms":215.80799999999556,"wall_ms":221.18046204559505},{"cpu_ms":257.43199999999433,"wall_ms":264.8606381844729},{"cpu_ms":312.86099999999806,"wall_ms":316.2682191468775},{"cpu_ms":185.7649999999893,"wall_ms":189.06718492507935},{"cpu_ms":281.833000000006,"wall_ms":285.2005169261247},{"cpu_ms":305.4390000000069,"wall_ms":312.8155837766826},{"cpu_ms":306.47900000001016,"wall_ms":311.863083159551},{"cpu_ms":309.03400000001113,"wall_ms":314.41670609638095},{"cpu_ms":266.94100000000276,"wall_ms":272.2801840864122},{"cpu_ms":251.35500000000377,"wall_ms":255.80116105265915},{"cpu_ms":250.44499999999914,"wall_ms":253.8465978577733},{"cpu_ms":299.29899999999066,"wall_ms":304.67048683203757},{"cpu_ms":289.98599999999897,"wall_ms":297.29981906712055},{"cpu_ms":313.21599999999705,"wall_ms":316.5997238829732},{"cpu_ms":229.89200000000665,"wall_ms":236.2906509079039},{"cpu_ms":313.5170000000045,"wall_ms":319.9579899664968},{"cpu_ms":303.2189999999986,"wall_ms":308.72616497799754},{"cpu_ms":304.58699999999794,"wall_ms":311.045901151374},{"cpu_ms":214.89599999999598,"wall_ms":224.6645879931748},{"cpu_ms":220.4150000000027,"wall_ms":224.8036079108715},{"cpu_ms":391.76099999998826,"wall_ms":397.1849379595369},{"cpu_ms":301.1730000000057,"wall_ms":308.6159680970013},{"cpu_ms":186.6720000000015,"wall_ms":193.2116060052067},{"cpu_ms":304.7150000000016,"wall_ms":314.123400952667},{"cpu_ms":304.49399999999116,"wall_ms":309.89065882749856},{"cpu_ms":286.45700000001284,"wall_ms":292.886404087767},{"cpu_ms":200.9959999999893,"wall_ms":209.11371894180775},{"cpu_ms":320.0789999999927,"wall_ms":327.4912559427321},{"cpu_ms":168.87199999999325,"wall_ms":174.1348470095545},{"cpu_ms":273.9210000000014,"wall_ms":277.2868052124977},{"cpu_ms":168.8350000000014,"wall_ms":176.19634000584483},{"cpu_ms":257.35300000000905,"wall_ms":264.77713114582},{"cpu_ms":311.6669999999999,"wall_ms":315.0626530405134}],"after_control_median_cpu_ms":292.44549999999947} +] diff --git a/benchmarks/regexp-construction/current-probe-samples.json b/benchmarks/regexp-construction/current-probe-samples.json new file mode 100644 index 0000000000..646810acc0 --- /dev/null +++ b/benchmarks/regexp-construction/current-probe-samples.json @@ -0,0 +1,4 @@ +[ + {"mode":"all","scale":1,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"construct":608.595,"test-ascii":2.37,"test-emoji":3.62,"stripAnsi":14.24,"stripAnsi-match":29.267,"ignorable":2.136,"segment":50.941,"eastAsianWidth":0.901,"stringWidth":32224.868},{"construct":546.759,"test-ascii":2.192,"test-emoji":2.259,"stripAnsi":7.995,"stripAnsi-match":16.867,"ignorable":1.159,"segment":27.887,"eastAsianWidth":0.486,"stringWidth":35017.106},{"construct":493.763,"test-ascii":2.722,"test-emoji":4.107,"stripAnsi":13.502,"stripAnsi-match":24.139,"ignorable":1.708,"segment":41.391,"eastAsianWidth":0.767,"stringWidth":41567.339},{"construct":799.274,"test-ascii":2.149,"test-emoji":3.271,"stripAnsi":11.549,"stripAnsi-match":24.518,"ignorable":1.739,"segment":46.156,"eastAsianWidth":0.713,"stringWidth":42925.479},{"construct":630.991,"test-ascii":2.682,"test-emoji":3.951,"stripAnsi":15.123,"stripAnsi-match":31.287,"ignorable":2.112,"segment":51.911,"eastAsianWidth":0.995,"stringWidth":42952.387},{"construct":673.543,"test-ascii":4.035,"test-emoji":3.856,"stripAnsi":14.591,"stripAnsi-match":26.969,"ignorable":1.756,"segment":44.655,"eastAsianWidth":0.728,"stringWidth":42780.5},{"construct":644.144,"test-ascii":2.138,"test-emoji":3.352,"stripAnsi":12.177,"stripAnsi-match":24.937,"ignorable":1.691,"segment":44.75,"eastAsianWidth":0.762,"stringWidth":39872.152},{"construct":608.466,"test-ascii":2.311,"test-emoji":3.498,"stripAnsi":11.964,"stripAnsi-match":27.361,"ignorable":1.9,"segment":49.697,"eastAsianWidth":0.891,"stringWidth":42058.722},{"construct":602.261,"test-ascii":2.302,"test-emoji":3.623,"stripAnsi":14.345,"stripAnsi-match":27.376,"ignorable":1.776,"segment":47.664,"eastAsianWidth":0.758,"stringWidth":42284.778},{"construct":616.726,"test-ascii":2.229,"test-emoji":3.4,"stripAnsi":13.12,"stripAnsi-match":26.495,"ignorable":1.864,"segment":45.231,"eastAsianWidth":0.808,"stringWidth":42140.513},{"construct":586.594,"test-ascii":2.17,"test-emoji":3.313,"stripAnsi":12.217,"stripAnsi-match":26.001,"ignorable":1.782,"segment":47.212,"eastAsianWidth":0.983,"stringWidth":39023.407},{"construct":486.923,"test-ascii":1.385,"test-emoji":2.532,"stripAnsi":9.605,"stripAnsi-match":25.333,"ignorable":1.857,"segment":49.723,"eastAsianWidth":0.938,"stringWidth":38250.838}],"after":[{"construct":4.455,"test-ascii":0.885,"test-emoji":2.361,"stripAnsi":0.704,"stripAnsi-match":4.891,"ignorable":0.403,"segment":51.103,"eastAsianWidth":0.875,"stringWidth":391.771},{"construct":4.608,"test-ascii":1.041,"test-emoji":2.443,"stripAnsi":0.698,"stripAnsi-match":5.048,"ignorable":0.412,"segment":54.396,"eastAsianWidth":0.987,"stringWidth":286.802},{"construct":4.433,"test-ascii":0.923,"test-emoji":2.207,"stripAnsi":0.585,"stripAnsi-match":5.827,"ignorable":0.351,"segment":62.895,"eastAsianWidth":2.311,"stringWidth":463.596},{"construct":4.321,"test-ascii":2.348,"test-emoji":1.979,"stripAnsi":0.512,"stripAnsi-match":3.78,"ignorable":0.311,"segment":52.841,"eastAsianWidth":2.259,"stringWidth":697.725},{"construct":4.525,"test-ascii":0.832,"test-emoji":2.148,"stripAnsi":0.568,"stripAnsi-match":5.775,"ignorable":0.397,"segment":49.211,"eastAsianWidth":0.86,"stringWidth":391.543},{"construct":4.798,"test-ascii":0.879,"test-emoji":1.99,"stripAnsi":0.533,"stripAnsi-match":6.622,"ignorable":1.904,"segment":94.793,"eastAsianWidth":0.782,"stringWidth":354.285},{"construct":4.687,"test-ascii":0.897,"test-emoji":2.334,"stripAnsi":0.645,"stripAnsi-match":5.16,"ignorable":0.419,"segment":52.391,"eastAsianWidth":0.89,"stringWidth":406.584},{"construct":4.63,"test-ascii":0.869,"test-emoji":2.17,"stripAnsi":0.567,"stripAnsi-match":4.255,"ignorable":0.314,"segment":45.353,"eastAsianWidth":0.778,"stringWidth":352.433},{"construct":4.613,"test-ascii":0.879,"test-emoji":2.155,"stripAnsi":0.607,"stripAnsi-match":4.245,"ignorable":0.368,"segment":45.387,"eastAsianWidth":0.827,"stringWidth":346.445},{"construct":4.113,"test-ascii":0.873,"test-emoji":2.144,"stripAnsi":0.6,"stripAnsi-match":4.416,"ignorable":0.388,"segment":50.717,"eastAsianWidth":0.869,"stringWidth":399.697},{"construct":4.042,"test-ascii":0.781,"test-emoji":1.995,"stripAnsi":0.584,"stripAnsi-match":4.434,"ignorable":0.391,"segment":50.84,"eastAsianWidth":0.847,"stringWidth":397.03},{"construct":4.925,"test-ascii":1.06,"test-emoji":2.394,"stripAnsi":0.623,"stripAnsi-match":4.603,"ignorable":0.384,"segment":55.188,"eastAsianWidth":0.955,"stringWidth":403.117}],"bun":[{"construct":3.135,"test-ascii":0.739,"test-emoji":0.582,"stripAnsi":0.643,"stripAnsi-match":0.594,"ignorable":0.298,"segment":22.711,"eastAsianWidth":1.508,"stringWidth":53.362},{"construct":3.486,"test-ascii":0.655,"test-emoji":0.523,"stripAnsi":0.584,"stripAnsi-match":0.648,"ignorable":0.287,"segment":16.822,"eastAsianWidth":0.835,"stringWidth":46.72},{"construct":2.383,"test-ascii":0.397,"test-emoji":0.307,"stripAnsi":0.349,"stripAnsi-match":0.215,"ignorable":0.161,"segment":12.784,"eastAsianWidth":0.709,"stringWidth":26.374},{"construct":21.714,"test-ascii":0.905,"test-emoji":0.562,"stripAnsi":0.544,"stripAnsi-match":0.425,"ignorable":0.278,"segment":33.825,"eastAsianWidth":1.078,"stringWidth":40.385},{"construct":3.781,"test-ascii":0.696,"test-emoji":0.582,"stripAnsi":0.623,"stripAnsi-match":0.399,"ignorable":0.282,"segment":22.393,"eastAsianWidth":1.7,"stringWidth":54.882},{"construct":3.607,"test-ascii":0.853,"test-emoji":0.494,"stripAnsi":1,"stripAnsi-match":0.439,"ignorable":0.275,"segment":34.615,"eastAsianWidth":0.644,"stringWidth":38.697},{"construct":3.654,"test-ascii":0.676,"test-emoji":0.595,"stripAnsi":0.585,"stripAnsi-match":0.415,"ignorable":0.293,"segment":20.722,"eastAsianWidth":1.289,"stringWidth":47.281},{"construct":3.782,"test-ascii":0.702,"test-emoji":0.593,"stripAnsi":0.552,"stripAnsi-match":0.425,"ignorable":0.288,"segment":20.801,"eastAsianWidth":1.281,"stringWidth":52.241},{"construct":3.482,"test-ascii":0.624,"test-emoji":0.577,"stripAnsi":0.595,"stripAnsi-match":0.66,"ignorable":0.28,"segment":17.48,"eastAsianWidth":1.123,"stringWidth":48.937},{"construct":3.257,"test-ascii":0.608,"test-emoji":0.525,"stripAnsi":0.643,"stripAnsi-match":0.67,"ignorable":0.291,"segment":15.585,"eastAsianWidth":1.1,"stringWidth":50},{"construct":3.495,"test-ascii":0.673,"test-emoji":0.595,"stripAnsi":0.679,"stripAnsi-match":0.706,"ignorable":0.302,"segment":23.169,"eastAsianWidth":1.248,"stringWidth":52.703},{"construct":3.235,"test-ascii":0.579,"test-emoji":0.6,"stripAnsi":0.489,"stripAnsi-match":0.541,"ignorable":0.251,"segment":16.24,"eastAsianWidth":1.2,"stringWidth":50.592}]},"output":["constructed g 14116","checksum 162430"],"medians_us":{"before":{"construct":608.5305000000001,"test-ascii":2.2655000000000003,"test-emoji":3.449,"stripAnsi":12.6685,"stripAnsi-match":26.248,"ignorable":1.779,"segment":46.684,"eastAsianWidth":0.7875000000000001,"stringWidth":41813.0305},"after":{"construct":4.5665,"test-ascii":0.882,"test-emoji":2.1624999999999996,"stripAnsi":0.5925,"stripAnsi-match":4.747,"ignorable":0.3895,"segment":51.747,"eastAsianWidth":0.872,"stringWidth":394.40049999999997},"bun":{"construct":3.4905,"test-ascii":0.6745000000000001,"test-emoji":0.5794999999999999,"stripAnsi":0.59,"stripAnsi-match":0.49,"ignorable":0.2845,"segment":20.761499999999998,"eastAsianWidth":1.1615,"stringWidth":49.4685}}}, + {"mode":"test-emoji","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"test-emoji":3.046},{"test-emoji":3.264},{"test-emoji":3.187},{"test-emoji":3.045},{"test-emoji":3.554},{"test-emoji":3.459},{"test-emoji":3.251},{"test-emoji":3.105},{"test-emoji":3.227},{"test-emoji":3.504},{"test-emoji":3.536},{"test-emoji":3.534}],"after":[{"test-emoji":1.507},{"test-emoji":2.191},{"test-emoji":1.746},{"test-emoji":2.265},{"test-emoji":2.121},{"test-emoji":1.916},{"test-emoji":1.942},{"test-emoji":2.218},{"test-emoji":2.223},{"test-emoji":2.143},{"test-emoji":2.317},{"test-emoji":2.338}],"bun":[{"test-emoji":0.056},{"test-emoji":0.084},{"test-emoji":0.052},{"test-emoji":0.062},{"test-emoji":0.051},{"test-emoji":0.052},{"test-emoji":0.066},{"test-emoji":0.044},{"test-emoji":0.059},{"test-emoji":0.065},{"test-emoji":0.065},{"test-emoji":0.064}]},"output":["checksum 100000"],"medians_us":{"before":{"test-emoji":3.2575},"after":{"test-emoji":2.167},"bun":{"test-emoji":0.0605}}} +] diff --git a/benchmarks/regexp-construction/current-results.md b/benchmarks/regexp-construction/current-results.md new file mode 100644 index 0000000000..00b4cd16ec --- /dev/null +++ b/benchmarks/regexp-construction/current-results.md @@ -0,0 +1,256 @@ +# RegExp cache: verification against Perry 0.5.1557 + +Runtime source `5b42a596f34b75800c9ec31abdb2526925a4b3f5` integrates +main `64f5249ac0a29eb7ffdde58947355b8a6bd38f27` (Perex 0.1.4). +The preserved candidate build snapshot is +`c03b3348e0905d2ef7d9ffe628508f3b0f216689`. +All builds, tests, probes and profiles ran on Linux through +`GIT_DIR=/tmp/perry-regexp-lane.git ./remote.sh ''`. +Nothing was built or tested on the Mac. + +Fresh main and candidate compiler/runtime/stdlib bundles are respectively +`/tmp/regexp-main-1557-libs` and `/tmp/regexp-1557-candidate-libs`. +Their identical release package sets and explicit Rust flags built successfully +in 3m 59s and 4m 19s. This comparison freezes the main reference above; older +comparisons remain in [final-results.md](final-results.md). + +## Attribution and final implementation + +The [original real-input profiles](results.md) established the cause before +editing: the active engine was Perex with **no construction cache**. Every +constructor reparsed and rebuilt its program. The old SipHash/fallback cache +analysis described an inactive implementation, not the measured execution path. + +| Path | Original CPU attribution | +|---|---| +| 14,116-character emoji constructor | Parser cursor 32.13%, sequence parsing 18.28%, escapes 17.83%, character classes 6.73%, emission 6.27%. | +| Reused emoji test on ASCII | About 77% revalidating immutable program words, ranges and repeats. Main now supplies an in-cell validation witness. | +| Plain stripAnsi | Property/reflective reads, flag getters, closure dispatch, exception traps and runtime handles dominate. Validation is 2.12%; no-match replacement already avoided result arrays. | + +The final change adds a 512-entry, 32 MiB program/source payload LRU. A source +identity plus canonical flags hit reads no pattern bytes. Independently +allocated equal strings use a content hash plus exact comparison. Eviction +removes individual entries; lookbehind and backreferences use the same engine +and cache. Source/program cells are mutable GC roots, with the identity index +rewritten after movement. Original source strings are sealed against in-place +append. Receiver state and `lastIndex` remain independent. + +Compilation stays eager, preserving constructor syntax errors and avoiding a +deferred compile on first test. Main's immutable program witnesses avoid repeat +validation. Its newer cross-call non-ASCII cursor table is preserved, including +heap-generation invalidation. Optional scratch hints from an earlier experiment +were removed; scratch buffers remain owned and budgeted per operation. + +Canonical builtin guards avoid generic property dispatch for ordinary tests +and empty replacements. Overrides, descriptors, coercions and altered +prototypes retain their ordinary observable behavior. Numeric `lastIndex` +avoids a coercion trap. Plain no-match replacement returns the input; actual +matches build output from spans without exec arrays or capture strings. + +A diagnostic snapshot after the constructor loop reports **three compilations, +200 identity hits and 14,277 source bytes hashed**, exactly the three initial +source lengths. The 200 repeated emoji constructions add no compilation or +pattern hashing. Diagnostic attribution itself inspects pattern bytes; the +timings below were collected with diagnostics disabled. + +The prior final profiles show the remaining tier: native dispatch, search +setup, the VM, exception traps, handle management, guards and scratch allocation. +Repeated parsing and program validation no longer dominate. See +[the prior profile breakdown](final-results.md#attribution-and-implementation) +and [ownership notes](../../docs/src/internals/regexp-program-cache.md). + +## Real OpenCode dependency probe + +OpenCode v1.18.30 supplies emoji-regex 10.6.0, string-width 7.2.0, +strip-ansi 7.1.2 and get-east-asian-width 1.6.0. The pattern is the real +14,116-character source. Perry reports 10 modules; both arms compile with +`--no-auto-optimize --debug-symbols`. Bun is 1.3.14. + +Twelve balanced rounds on CPU 15 cover all six before/after/Bun orders. +Native builds use one executable pathname/inode, copied outside timing. +All checksums and non-timing output agree. The original `all 1` counts are +200 constructions, 2,000 tests/replacements, 200 segmentations and 90 width +calls, checksum 162430. Medians below are wall microseconds per operation, +including the first operation. + +| Operation | Main | Candidate | Bun | +|---|---:|---:|---:| +| Emoji construction | 608.531 | 4.566 | 3.490 | +| Reused emoji test, ASCII | 2.266 | 0.882 | 0.675 | +| Reused emoji test, rocket | 3.449 | 2.162 | 0.579 | +| stripAnsi, plain help line | 12.668 | 0.593 | 0.590 | +| stripAnsi, ANSI-colored text | 26.248 | 4.747 | 0.490 | +| Default_Ignorable test | 1.779 | 0.390 | 0.284 | +| Intl.Segmenter | 46.684 | 51.747 | 20.761 | +| eastAsianWidth | 0.788 | 0.872 | 1.161 | +| stringWidth | 41813.031 | 394.400 | 49.468 | + +Construction improves **133×**, plain stripAnsi **21×**, and stringWidth +**106×**. Construction, the issue's reused ASCII test, and plain stripAnsi +meet their 20/2/1 µs targets. The rocket test is **2.163 µs** in this loaded +run and **2.167 µs** at 200,000 calls (main 3.258, Bun 0.061). +It exceeds the 2 µs target here; the prior integration measured 1.269 µs. +Actual ANSI matches remain above 1 µs. These are recorded limits, not omitted +samples. [Raw probe samples](current-probe-samples.json). + +This shared host had another large native OpenCode compilation running. +Absolute times and identical-binary controls vary substantially. Our own +correctness jobs started only after these timing runs. No full OpenCode binary +was rebuilt or timed for this lane; dependency measurements do not establish +the issue's under-one-second full startup expectation. + +## Existing benchmark controls + +All 12 existing `benchmarks/app-patterns/kernels/*.ts` compile against each +matching bundle and match Node 26.5.1. Twelve balanced four-arm rounds follow +one warmup: two identical-copy labels per build, 24 samples/build. Values are +pooled median child user CPU milliseconds. A/A compares the two identical-copy +label medians within each build. Wall samples are retained in +[the raw artifact](current-app-samples.json). + +| Kernel | Main ms | Candidate ms | Change | Main A/A | Candidate A/A | +|---|---:|---:|---:|---:|---:| +| batch | 47.604 | 48.038 | 0.91% | 4.29% | 5.42% | +| buffer_transcode | 73.163 | 71.779 | -1.89% | 1.38% | 4.78% | +| date_format_parse | 46.109 | 46.189 | 0.17% | -0.25% | -0.87% | +| json_parse_1mb | 81.657 | 79.575 | -2.55% | -0.38% | 5.48% | +| json_stringify_1mb | 95.008 | 94.004 | -1.06% | -10.41% | -33.91% | +| map_1m | 229.503 | 229.816 | 0.14% | -0.09% | -4.15% | +| object_deep_clone | 29.527 | 28.119 | -4.77% | -8.86% | -0.52% | +| promise_all_chains | 157.143 | 157.506 | 0.23% | -14.39% | -8.06% | +| regex_replace | 3412.720 | 2889.472 | -15.33% | -7.21% | 5.05% | +| string_concat_csv | 47.117 | 47.617 | 1.06% | 1.68% | 2.28% | +| string_split_map_join | 280.455 | 299.048 | 6.63% | -11.69% | -1.71% | +| string_template_interp | 74.776 | 76.552 | 2.37% | -2.45% | 5.80% | + +The existing regex replacement kernel improves **15.33%**. Large A/A spreads, +including 33.91% in JSON stringify, prevent interpreting every small sweep +delta as a code effect. The 100-round JSON parse control (200 samples/build) +is **86.078 → 85.172 ms, -1.05%**; the previous integration's loaded JSON +slowdown does not reproduce against this main/build pair. +[Raw JSON control](current-json-control-samples.json). + +Longer controls use 100 balanced four-arm rounds, 200 samples/build. The +intervals resample balanced four-round blocks 5,000 times, with seed 10179. + +| Kernel | Main ms | Candidate ms | Change | 95% block-bootstrap interval | +|---|---:|---:|---:|---| +| json_parse_1mb | 86.077 | 85.172 | -1.05% | -1.72% to 0.43% | +| json_stringify_1mb | 136.675 | 137.079 | 0.30% | -2.62% to 1.85% | +| string_concat_csv | 46.861 | 48.161 | 2.78% | 1.56% to 3.58% | +| string_split_map_join | 291.086 | 293.124 | 0.70% | -1.63% to 7.98% | + +JSON stringify and split/join do not show a resolved slowdown beyond these +controls. CSV concatenation retains **+2.78%**, with the interval above zero. +The strict no-regression requirement is **not met**, so the PR stays in draft. +The earlier JSON issue has not simply been dismissed as noise, nor does its +absence in the current comparison establish that every other benchmark is +unaffected. No padding, unrelated string optimization or benchmark alteration +was added to conceal this result. +[Other raw controls](current-other-control-samples.json). + +## Correctness + +- `cargo test -p perry-runtime --lib regex`: **103 passed** (9.78s). +- Upstream cursor-position slice: **three passed** (0.82s). +- Full runtime lib: **3,746 passed, four ignored, one failed** (208.04s). + `native_stack::tests::stack_top_respects_custom_thread_stack_sizes` fails + identically on freshly built main; seven other native-stack tests pass there. +- Regex parity against Node 26.5.1: **25 passed**, zero differences, compile + failures, crashes or skips (5m 53s including the HTTP extension rebuild). +- Full pinned RegExp/Annex B Test262: **1,786 passed, 129 runtime failures, + zero output differences, zero compile failures, seven skipped**. There are + 1,915 judged cases and 210 negative agreements. All 129 failure-name/category + pairs match fresh main exactly, with none added or removed. + [Comparison artifact](current-test262-comparison.json). +- Formatting, file-size cap, registration (298 files/four registries), GC-root + inventory (1,420 holders/155 scanners) and Node-version consistency: pass. +- `pre-tag-check.sh --quick`: only public benchmark evidence freshness fails. + Fresh main reproduces that failure with exit 2; the candidate quick suite + returns exit 1 and lists that check alone. + +Inspection with `rg -n regex .github/workflows/test.yml` finds the reduced-feature +comment, not a separately named regex/Test262 job. The runtime/parity coverage +and full RegExp plus Annex B corpus above are therefore run explicitly. +Test262 is pinned at `4249661388e5d3f92a85186213da140a6481490f`, and Node +26.5.1 matches the repository oracle. The benchmark regression and unmeasured +full startup remain explicit acceptance gaps. + +## Exact host commands + +All commands below were invoked through the remote wrapper; `taskset` places +timings on CPU 15. Each release bundle contains the compiler and both static +archives from one build. Baseline and candidate builds ran sequentially in the +same lane, checking out the named source before each build. + +```sh +export RUSTFLAGS="-C force-unwind-tables=yes -C force-frame-pointers=yes" +export PATH=/tmp/regexp-oracle/node-v26.5.1-linux-x64/bin:$PATH +# For main 64f5249ac0 and candidate snapshot c03b3348e0: +cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static +# Copy perry, libperry_runtime.a and libperry_stdlib.a from the target release +# directory into /tmp/regexp-main-1557-libs or /tmp/regexp-1557-candidate-libs. + +python3 benchmarks/regexp-construction/prepare.py /tmp/regexp-probe-1557.ts +PERRY_RUNTIME_DIR=/tmp/regexp-main-1557-libs /tmp/regexp-main-1557-libs/perry \ + compile /tmp/regexp-probe-1557.ts -o /tmp/regexp-probe-main-1557 \ + --no-auto-optimize --debug-symbols +PERRY_RUNTIME_DIR=/tmp/regexp-1557-candidate-libs /tmp/regexp-1557-candidate-libs/perry \ + compile /tmp/regexp-probe-1557.ts -o /tmp/regexp-probe-candidate-1557 \ + --no-auto-optimize --debug-symbols +taskset -c 15 python3 benchmarks/regexp-construction/measure.py \ + --before /tmp/regexp-probe-main-1557 --after /tmp/regexp-probe-candidate-1557 \ + --source /tmp/regexp-probe-1557.ts --bun /root/claude-opencode/bun/bin/bun \ + --output /tmp/regexp-1557-probe.json --runs 12 \ + --case all:1 --case test-emoji:100 +taskset -c 15 python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-main-1557-libs --after /tmp/regexp-1557-candidate-libs \ + --output /tmp/regexp-1557-apps --runs 12 --paired-controls +taskset -c 15 python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-main-1557-libs --after /tmp/regexp-1557-candidate-libs \ + --output /tmp/regexp-1557-json-control --runs 100 --paired-controls \ + --filter json_parse_1mb +for kernel in json_stringify_1mb string_concat_csv string_split_map_join; do + taskset -c 15 python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-main-1557-libs --after /tmp/regexp-1557-candidate-libs \ + --output "/tmp/regexp-1557-control-$kernel" --runs 100 --paired-controls \ + --filter "$kernel" +done + +export RUST_TEST_THREADS=1 CARGO_BUILD_JOBS=6 +taskset -c 9-14 cargo test -p perry-runtime --lib regex +taskset -c 9-14 cargo test -p perry-runtime --lib perex_position_hint +taskset -c 9-14 cargo test -p perry-runtime --lib +# In main 64f5249ac0: +taskset -c 9-14 cargo test -p perry-runtime --lib native_stack +python3 benchmarks/ci_public_baseline_check.py + +# Match the candidate compiler's build stamp before building its HTTP extension: +git checkout --detach c03b3348e0905d2ef7d9ffe628508f3b0f216689 +PERRY_SKIP_BUILD=1 PERRY_BIN=/tmp/regexp-1557-candidate-libs/perry \ + PERRY_RUNTIME_DIR=/tmp/regexp-1557-candidate-libs taskset -c 9-14 \ + ./run_parity_tests.sh --filter regex +PERRY_RUNTIME_DIR=/tmp/regexp-main-1557-libs taskset -c 0-3 \ + python3 scripts/test262_subset.py --root /tmp/regexp-test262 \ + --dir built-ins/RegExp annexB/built-ins/RegExp --all-features --jobs 4 \ + --perry-bin /tmp/regexp-main-1557-libs/perry \ + --report /tmp/regexp-test262-main-1557.json --sample-cap 100000 +PERRY_RUNTIME_DIR=/tmp/regexp-1557-candidate-libs taskset -c 4-6,8 \ + python3 scripts/test262_subset.py --root /tmp/regexp-test262 \ + --dir built-ins/RegExp annexB/built-ins/RegExp --all-features --jobs 4 \ + --perry-bin /tmp/regexp-1557-candidate-libs/perry \ + --report /tmp/regexp-test262-candidate-1557.json --sample-cap 100000 +# Restore the final source snapshot before lint. +cargo fmt --all -- --check +./scripts/check_file_size.sh +python3 scripts/check_test_registration.py +python3 scripts/gc_runtime_root_holders.py +python3 scripts/check_node_version_consistency.py --list +./scripts/pre-tag-check.sh --quick +PERRY_REGEX_DIAG=1 taskset -c 15 /tmp/regexp-probe-candidate-1557 all 1 +``` + +The original profiling commands and their pre-edit attribution are retained in +[results.md](results.md#reproduction-commands). The benchmark drivers now default to 12 +rounds, which is divisible by both schedule lengths. Explicit `--runs 12` +and `--runs 100 --paired-controls` were used for the recorded measurements. diff --git a/benchmarks/regexp-construction/current-test262-comparison.json b/benchmarks/regexp-construction/current-test262-comparison.json new file mode 100644 index 0000000000..c120aee162 --- /dev/null +++ b/benchmarks/regexp-construction/current-test262-comparison.json @@ -0,0 +1,162 @@ +{ + "baseline": { + "source": "64f5249ac0a29eb7ffdde58947355b8a6bd38f27", + "generated_at": "2026-09-13T16:27:06Z", + "totals": { + "pass": 1786, + "diff": 0, + "runtime-fail": 129, + "compile-fail": 0, + "skip": 7 + } + }, + "candidate": { + "source": "5b42a596f34b75800c9ec31abdb2526925a4b3f5", + "build_snapshot": "c03b3348e0905d2ef7d9ffe628508f3b0f216689", + "generated_at": "2026-09-13T16:26:51Z", + "totals": { + "pass": 1786, + "diff": 0, + "runtime-fail": 129, + "compile-fail": 0, + "skip": 7 + } + }, + "test262_pinned": "4249661388e5d3f92a85186213da140a6481490f", + "node_runtime": "v26.5.1", + "judged": 1915, + "negative_agreements": 210, + "identical_failure_pairs": [ + "annexB/built-ins/RegExp/legacy-accessors/input/prop-desc.js|runtime-fail", + "annexB/built-ins/RegExp/prototype/Symbol.split/Symbol.match-getter-recompiles-source.js|runtime-fail", + "built-ins/RegExp/S15.10.2.8_A3_T15.js|runtime-fail", + "built-ins/RegExp/S15.10.2.8_A3_T16.js|runtime-fail", + "built-ins/RegExp/Symbol.species/length.js|runtime-fail", + "built-ins/RegExp/Symbol.species/return-value.js|runtime-fail", + "built-ins/RegExp/Symbol.species/symbol-species-name.js|runtime-fail", + "built-ins/RegExp/Symbol.species/symbol-species.js|runtime-fail", + "built-ins/RegExp/escape/escaped-surrogates.js|runtime-fail", + "built-ins/RegExp/escape/is-function.js|runtime-fail", + "built-ins/RegExp/escape/length.js|runtime-fail", + "built-ins/RegExp/escape/name.js|runtime-fail", + "built-ins/RegExp/escape/non-string-inputs.js|runtime-fail", + "built-ins/RegExp/escape/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/escape/prop-desc.js|runtime-fail", + "built-ins/RegExp/from-regexp-like-get-ctor-err.js|runtime-fail", + "built-ins/RegExp/named-groups/non-unicode-property-names-invalid.js|runtime-fail", + "built-ins/RegExp/named-groups/unicode-property-names-invalid.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/Basic_Emoji.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/Emoji_Keycap_Sequence.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji_Flag_Sequence.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji_Modifier_Sequence.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji_Tag_Sequence.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji_ZWJ_Sequence.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.match/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.matchAll/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.replace/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.search/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.split/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/quantifier-integer-limit.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-13.1.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-14.0.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-15.0.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-15.1.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-16.0.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-17.0.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-string-literal.js|runtime-fail" + ], + "added": [], + "removed": [] +} diff --git a/benchmarks/regexp-construction/final-app-samples.json b/benchmarks/regexp-construction/final-app-samples.json new file mode 100644 index 0000000000..cf6a7beb28 --- /dev/null +++ b/benchmarks/regexp-construction/final-app-samples.json @@ -0,0 +1,14 @@ +[ +{"name":"batch","node_exit":0,"before":[{"cpu_ms":48.69800000000013,"wall_ms":55.06442906334996},{"cpu_ms":43.90300000000025,"wall_ms":100.7130581419915},{"cpu_ms":44.37200000000008,"wall_ms":58.725327951833606},{"cpu_ms":39.998000000000204,"wall_ms":46.014321967959404},{"cpu_ms":46.5829999999996,"wall_ms":62.716193962842226},{"cpu_ms":42.21200000000014,"wall_ms":56.73370696604252},{"cpu_ms":43.879000000000005,"wall_ms":80.53331892006099},{"cpu_ms":43.528999999999925,"wall_ms":48.492321046069264},{"cpu_ms":42.67099999999991,"wall_ms":74.59882204420865},{"cpu_ms":43.226999999999904,"wall_ms":48.98911900818348},{"cpu_ms":38.64300000000043,"wall_ms":49.542363034561276},{"cpu_ms":42.54300000000022,"wall_ms":69.8052269872278}],"before_median_cpu_ms":43.377999999999915,"after":[{"cpu_ms":52.48200000000014,"wall_ms":73.5161101911217},{"cpu_ms":39.638999999999754,"wall_ms":46.7265120241791},{"cpu_ms":42.00299999999979,"wall_ms":64.47388790547848},{"cpu_ms":42.42800000000013,"wall_ms":53.49569790996611},{"cpu_ms":39.63500000000009,"wall_ms":59.4290429726243},{"cpu_ms":42.767999999999695,"wall_ms":50.438005942851305},{"cpu_ms":43.736,"wall_ms":143.71634484268725},{"cpu_ms":43.63299999999981,"wall_ms":48.15032286569476},{"cpu_ms":41.354999999999805,"wall_ms":50.2818338572979},{"cpu_ms":42.21500000000012,"wall_ms":47.3287480417639},{"cpu_ms":42.02999999999957,"wall_ms":46.460274839773774},{"cpu_ms":43.00000000000015,"wall_ms":48.47281496040523}],"after_median_cpu_ms":42.32150000000013,"before_control":[{"cpu_ms":45.64199999999996,"wall_ms":57.02138505876064},{"cpu_ms":47.04799999999976,"wall_ms":64.7078650072217},{"cpu_ms":40.34499999999986,"wall_ms":55.98562001250684},{"cpu_ms":45.64799999999991,"wall_ms":51.31152784451842},{"cpu_ms":41.78500000000041,"wall_ms":66.32005609571934},{"cpu_ms":43.75499999999999,"wall_ms":48.19756094366312},{"cpu_ms":40.612000000000315,"wall_ms":81.34963409975171},{"cpu_ms":42.780000000000044,"wall_ms":48.89869107864797},{"cpu_ms":40.0520000000002,"wall_ms":51.67082883417606},{"cpu_ms":42.20400000000035,"wall_ms":49.67695381492376},{"cpu_ms":40.77099999999989,"wall_ms":48.301203874871135},{"cpu_ms":40.918000000000006,"wall_ms":112.39820811897516}],"before_control_median_cpu_ms":41.99450000000038,"after_control":[{"cpu_ms":43.13000000000011,"wall_ms":53.51781006902456},{"cpu_ms":41.15700000000011,"wall_ms":48.24781487695873},{"cpu_ms":42.40200000000005,"wall_ms":90.79241496510804},{"cpu_ms":39.201999999999956,"wall_ms":45.40649591945112},{"cpu_ms":40.166999999999845,"wall_ms":51.07239191420376},{"cpu_ms":44.25200000000018,"wall_ms":50.651033874601126},{"cpu_ms":45.9839999999998,"wall_ms":53.46133396960795},{"cpu_ms":42.99200000000036,"wall_ms":48.13885106705129},{"cpu_ms":35.040999999999656,"wall_ms":42.54196514375508},{"cpu_ms":41.92899999999966,"wall_ms":47.69502207636833},{"cpu_ms":50.221000000000075,"wall_ms":97.73587598465383},{"cpu_ms":47.47699999999977,"wall_ms":76.6198979690671}],"after_control_median_cpu_ms":42.6970000000002}, +{"name":"buffer_transcode","node_exit":0,"before":[{"cpu_ms":74.2560000000001,"wall_ms":108.04525599814951},{"cpu_ms":66.43899999999991,"wall_ms":92.78019913472235},{"cpu_ms":67.22900000000021,"wall_ms":87.07953803241253},{"cpu_ms":69.48199999999983,"wall_ms":297.81673988327384},{"cpu_ms":65.08599999999998,"wall_ms":88.84153980761766},{"cpu_ms":61.668000000000056,"wall_ms":99.43003114312887},{"cpu_ms":75.68399999999986,"wall_ms":117.76933097280562},{"cpu_ms":79.5199999999987,"wall_ms":117.59040807373822},{"cpu_ms":67.91499999999928,"wall_ms":99.10069592297077},{"cpu_ms":66.69400000000003,"wall_ms":122.1543641295284},{"cpu_ms":75.17700000000005,"wall_ms":119.98031288385391},{"cpu_ms":74.69899999999896,"wall_ms":92.74560399353504}],"before_median_cpu_ms":68.69849999999956,"after":[{"cpu_ms":69.44300000000058,"wall_ms":150.13468591496348},{"cpu_ms":68.2589999999994,"wall_ms":87.27432321757078},{"cpu_ms":71.82200000000006,"wall_ms":126.72434095293283},{"cpu_ms":67.91999999999999,"wall_ms":98.09432597830892},{"cpu_ms":67.3629999999994,"wall_ms":137.56275898776948},{"cpu_ms":61.346999999999596,"wall_ms":101.3040819671005},{"cpu_ms":71.26800000000077,"wall_ms":246.80438009090722},{"cpu_ms":71.47000000000148,"wall_ms":92.67179598100483},{"cpu_ms":71.43300000000075,"wall_ms":99.12434103898704},{"cpu_ms":71.30300000000034,"wall_ms":89.55579507164657},{"cpu_ms":68.90800000000041,"wall_ms":154.63153715245426},{"cpu_ms":68.3290000000003,"wall_ms":124.24118304625154}],"after_median_cpu_ms":69.1755000000005,"before_control":[{"cpu_ms":73.02599999999958,"wall_ms":97.39740379154682},{"cpu_ms":63.73000000000051,"wall_ms":95.10242589749396},{"cpu_ms":68.95099999999931,"wall_ms":87.14273711666465},{"cpu_ms":75.58500000000024,"wall_ms":213.01556704565883},{"cpu_ms":68.99000000000032,"wall_ms":88.42331892810762},{"cpu_ms":66.09599999999993,"wall_ms":88.22836400941014},{"cpu_ms":68.26299999999996,"wall_ms":212.66730688512325},{"cpu_ms":66.4750000000005,"wall_ms":98.05872896686196},{"cpu_ms":63.30800000000103,"wall_ms":201.3608948327601},{"cpu_ms":68.32099999999919,"wall_ms":146.99700404889882},{"cpu_ms":62.97499999999978,"wall_ms":261.3726060371846},{"cpu_ms":70.14000000000031,"wall_ms":167.41447895765305}],"before_control_median_cpu_ms":68.29199999999958,"after_control":[{"cpu_ms":71.52599999999953,"wall_ms":128.79572086967528},{"cpu_ms":69.75200000000027,"wall_ms":88.63907190971076},{"cpu_ms":60.24300000000071,"wall_ms":91.42739116214216},{"cpu_ms":72.46500000000023,"wall_ms":112.63677407987416},{"cpu_ms":67.74300000000011,"wall_ms":86.04775089770555},{"cpu_ms":64.42000000000014,"wall_ms":85.02979995682836},{"cpu_ms":71.44800000000018,"wall_ms":160.82926490344107},{"cpu_ms":71.25799999999849,"wall_ms":96.78983897902071},{"cpu_ms":67.13799999999992,"wall_ms":118.62756591290236},{"cpu_ms":66.45400000000024,"wall_ms":91.33458812721074},{"cpu_ms":61.83599999999956,"wall_ms":177.44271201081574},{"cpu_ms":68.15500000000085,"wall_ms":178.08351013809443}],"after_control_median_cpu_ms":67.94900000000048}, +{"name":"date_format_parse","node_exit":0,"before":[{"cpu_ms":44.653000000000276,"wall_ms":117.9784620180726},{"cpu_ms":47.116000000000824,"wall_ms":94.81661207973957},{"cpu_ms":43.94299999999873,"wall_ms":91.00692509673536},{"cpu_ms":43.64900000000027,"wall_ms":102.7269409969449},{"cpu_ms":49.10500000000084,"wall_ms":70.10435592383146},{"cpu_ms":48.12000000000083,"wall_ms":60.8421431388706},{"cpu_ms":45.125000000000526,"wall_ms":76.51072507724166},{"cpu_ms":48.066000000000386,"wall_ms":55.1501598674804},{"cpu_ms":42.52900000000004,"wall_ms":80.70221496745944},{"cpu_ms":46.780000000000044,"wall_ms":68.09090496972203},{"cpu_ms":48.42999999999975,"wall_ms":74.0553680807352},{"cpu_ms":43.67000000000054,"wall_ms":77.79772905632854}],"before_median_cpu_ms":45.952500000000285,"after":[{"cpu_ms":47.443000000001234,"wall_ms":92.74171595461667},{"cpu_ms":45.26199999999925,"wall_ms":101.74618801102042},{"cpu_ms":45.01000000000133,"wall_ms":65.35150716081262},{"cpu_ms":48.024999999999096,"wall_ms":109.61525910533965},{"cpu_ms":43.91700000000043,"wall_ms":76.51427085511386},{"cpu_ms":46.64799999999936,"wall_ms":61.145800864323974},{"cpu_ms":44.50399999999988,"wall_ms":102.38638403825462},{"cpu_ms":43.62599999999972,"wall_ms":67.15833302587271},{"cpu_ms":45.849999999999724,"wall_ms":70.7691281568259},{"cpu_ms":44.781000000000404,"wall_ms":70.32224396243691},{"cpu_ms":44.38799999999965,"wall_ms":102.09326702170074},{"cpu_ms":47.71700000000045,"wall_ms":65.92622096650302}],"after_median_cpu_ms":45.13600000000029,"before_control":[{"cpu_ms":43.12299999999958,"wall_ms":49.33365317992866},{"cpu_ms":45.25699999999944,"wall_ms":82.7414549421519},{"cpu_ms":42.942000000000036,"wall_ms":69.06405300833285},{"cpu_ms":43.36699999999993,"wall_ms":75.63081989064813},{"cpu_ms":41.76799999999936,"wall_ms":73.99598602205515},{"cpu_ms":47.52400000000101,"wall_ms":137.6083940267563},{"cpu_ms":47.19500000000032,"wall_ms":113.64831402897835},{"cpu_ms":45.27899999999896,"wall_ms":70.68121386691928},{"cpu_ms":43.732000000000326,"wall_ms":104.71454402431846},{"cpu_ms":46.922999999999604,"wall_ms":77.24146894179285},{"cpu_ms":49.250999999999934,"wall_ms":63.245573081076145},{"cpu_ms":44.620000000000104,"wall_ms":80.26585006155074}],"before_control_median_cpu_ms":44.93849999999977,"after_control":[{"cpu_ms":46.59499999999994,"wall_ms":62.61199899017811},{"cpu_ms":44.213000000000946,"wall_ms":102.79333498328924},{"cpu_ms":45.02399999999973,"wall_ms":76.80567516945302},{"cpu_ms":44.26499999999933,"wall_ms":59.92502998560667},{"cpu_ms":42.54899999999928,"wall_ms":88.24448520317674},{"cpu_ms":45.48600000000036,"wall_ms":66.49629608727992},{"cpu_ms":47.00499999999863,"wall_ms":107.71220410242677},{"cpu_ms":46.55400000000043,"wall_ms":88.35505205206573},{"cpu_ms":46.7240000000011,"wall_ms":86.6523808799684},{"cpu_ms":47.05199999999898,"wall_ms":62.48863902874291},{"cpu_ms":47.401999999999944,"wall_ms":55.65072409808636},{"cpu_ms":45.5849999999991,"wall_ms":69.49053006246686}],"after_control_median_cpu_ms":46.06949999999976}, +{"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":92.80500000000025,"wall_ms":421.45633907057345},{"cpu_ms":84.71199999999968,"wall_ms":176.70855089090765},{"cpu_ms":83.58800000000244,"wall_ms":118.80337493494153},{"cpu_ms":78.6309999999979,"wall_ms":176.15713994018734},{"cpu_ms":79.07499999999956,"wall_ms":146.1401937995106},{"cpu_ms":87.12600000000137,"wall_ms":129.14425320923328},{"cpu_ms":88.53300000000175,"wall_ms":187.535067088902},{"cpu_ms":84.49499999999688,"wall_ms":147.58628490380943},{"cpu_ms":81.74500000000151,"wall_ms":100.52301408722997},{"cpu_ms":61.804999999999666,"wall_ms":73.4341770876199},{"cpu_ms":81.07000000000042,"wall_ms":90.79774492420256},{"cpu_ms":75.85899999999768,"wall_ms":84.66994692571461}],"before_median_cpu_ms":82.66650000000197,"after":[{"cpu_ms":89.27199999999935,"wall_ms":400.7927260827273},{"cpu_ms":89.39499999999967,"wall_ms":207.21098384819925},{"cpu_ms":82.99699999999888,"wall_ms":209.95346712879837},{"cpu_ms":83.32400000000106,"wall_ms":234.9920340348035},{"cpu_ms":81.29200000000125,"wall_ms":405.1033300347626},{"cpu_ms":87.8869999999985,"wall_ms":173.20509511046112},{"cpu_ms":85.4910000000011,"wall_ms":128.70823917910457},{"cpu_ms":87.06200000000308,"wall_ms":93.59753504395485},{"cpu_ms":85.38399999999768,"wall_ms":121.27181608229876},{"cpu_ms":84.38400000000001,"wall_ms":92.89262816309929},{"cpu_ms":78.8650000000004,"wall_ms":94.97976815328002},{"cpu_ms":79.89600000000152,"wall_ms":90.53348191082478}],"after_median_cpu_ms":84.88399999999885,"before_control":[{"cpu_ms":81.70099999999891,"wall_ms":164.50787684880197},{"cpu_ms":83.41700000000074,"wall_ms":130.759950960055},{"cpu_ms":81.6599999999994,"wall_ms":91.57455596141517},{"cpu_ms":85.11800000000136,"wall_ms":204.95604118332267},{"cpu_ms":91.58000000000044,"wall_ms":171.5491919312626},{"cpu_ms":86.24700000000018,"wall_ms":159.2794801108539},{"cpu_ms":83.38399999999879,"wall_ms":222.54700516350567},{"cpu_ms":82.55100000000226,"wall_ms":121.02390313521028},{"cpu_ms":88.35499999999996,"wall_ms":228.12756011262536},{"cpu_ms":80.73200000000114,"wall_ms":83.46090908162296},{"cpu_ms":75.34199999999913,"wall_ms":84.90917412564158},{"cpu_ms":82.63799999999932,"wall_ms":184.02158305980265}],"before_control_median_cpu_ms":83.01099999999906,"after_control":[{"cpu_ms":78.49700000000226,"wall_ms":127.14903592132032},{"cpu_ms":89.28399999999925,"wall_ms":170.29832513071597},{"cpu_ms":84.8639999999996,"wall_ms":108.60989079810679},{"cpu_ms":81.87099999999958,"wall_ms":221.3274659588933},{"cpu_ms":90.75099999999736,"wall_ms":171.99201020412147},{"cpu_ms":83.4210000000013,"wall_ms":149.05157196335495},{"cpu_ms":87.54599999999968,"wall_ms":125.87661785073578},{"cpu_ms":78.81699999999725,"wall_ms":117.42362705990672},{"cpu_ms":91.80100000000024,"wall_ms":271.51419408619404},{"cpu_ms":84.06399999999792,"wall_ms":123.2945742085576},{"cpu_ms":84.10900000000154,"wall_ms":89.1464608721435},{"cpu_ms":80.47200000000032,"wall_ms":164.8095219861716}],"after_control_median_cpu_ms":84.08649999999973}, +{"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":133.9269999999999,"wall_ms":142.25803199224174},{"cpu_ms":138.8229999999986,"wall_ms":351.8482109066099},{"cpu_ms":127.41099999999861,"wall_ms":141.65177894756198},{"cpu_ms":123.45499999999987,"wall_ms":129.87057003192604},{"cpu_ms":130.79199999999958,"wall_ms":248.57851583510637},{"cpu_ms":154.6929999999982,"wall_ms":160.79184599220753},{"cpu_ms":131.3949999999977,"wall_ms":338.1883429829031},{"cpu_ms":114.34299999999809,"wall_ms":228.59401116147637},{"cpu_ms":136.8620000000007,"wall_ms":151.06815099716187},{"cpu_ms":135.37200000000027,"wall_ms":239.7588100284338},{"cpu_ms":138.86400000000165,"wall_ms":296.32163792848587},{"cpu_ms":137.10100000000125,"wall_ms":143.53370503522456}],"before_median_cpu_ms":134.6495000000001,"after":[{"cpu_ms":131.06699999999805,"wall_ms":140.66712907515466},{"cpu_ms":136.78600000000074,"wall_ms":271.9927881844342},{"cpu_ms":126.70500000000118,"wall_ms":145.1861320529133},{"cpu_ms":128.0399999999986,"wall_ms":137.58258800953627},{"cpu_ms":131.98899999999725,"wall_ms":153.4406328573823},{"cpu_ms":157.3360000000008,"wall_ms":175.5240480415523},{"cpu_ms":115.6630000000014,"wall_ms":171.6351229697466},{"cpu_ms":127.44600000000261,"wall_ms":142.18488498590887},{"cpu_ms":131.2029999999993,"wall_ms":138.01089697517455},{"cpu_ms":138.67899999999977,"wall_ms":290.7294009346515},{"cpu_ms":148.31599999999767,"wall_ms":315.7686679624021},{"cpu_ms":129.65499999999963,"wall_ms":163.75633399002254}],"after_median_cpu_ms":131.13499999999868,"before_control":[{"cpu_ms":125.30500000000089,"wall_ms":133.76681902445853},{"cpu_ms":134.94000000000028,"wall_ms":264.12641210481524},{"cpu_ms":128.0619999999999,"wall_ms":235.5237880256027},{"cpu_ms":103.75400000000212,"wall_ms":255.12081803753972},{"cpu_ms":130.72500000000176,"wall_ms":204.73079197108746},{"cpu_ms":129.94799999999884,"wall_ms":138.7112159281969},{"cpu_ms":129.79699999999994,"wall_ms":268.11793888919055},{"cpu_ms":136.19500000000073,"wall_ms":168.69748500175774},{"cpu_ms":106.28200000000021,"wall_ms":142.26555614732206},{"cpu_ms":123.95500000000226,"wall_ms":243.30692808143795},{"cpu_ms":153.64500000000092,"wall_ms":336.2137528602034},{"cpu_ms":139.3519999999988,"wall_ms":259.9984109401703}],"before_control_median_cpu_ms":129.87249999999938,"after_control":[{"cpu_ms":130.3289999999997,"wall_ms":141.19950495660305},{"cpu_ms":93.37000000000018,"wall_ms":135.45592478476465},{"cpu_ms":138.8580000000026,"wall_ms":144.99959396198392},{"cpu_ms":126.55200000000022,"wall_ms":134.55383898690343},{"cpu_ms":133.4850000000003,"wall_ms":173.09374804608524},{"cpu_ms":128.31800000000015,"wall_ms":252.07647099159658},{"cpu_ms":147.1940000000025,"wall_ms":185.70632999762893},{"cpu_ms":141.0809999999998,"wall_ms":143.6030939221382},{"cpu_ms":126.52499999999733,"wall_ms":276.187774958089},{"cpu_ms":107.61099999999857,"wall_ms":382.4280898552388},{"cpu_ms":146.2849999999989,"wall_ms":150.19617206417024},{"cpu_ms":131.84299999999993,"wall_ms":176.42764607444406}],"after_control_median_cpu_ms":131.0859999999998}, +{"name":"map_1m","node_exit":0,"before":[{"cpu_ms":324.9190000000013,"wall_ms":351.92148899659514},{"cpu_ms":335.954000000001,"wall_ms":369.06243092380464},{"cpu_ms":326.4569999999978,"wall_ms":342.57889608852565},{"cpu_ms":348.0610000000013,"wall_ms":364.8679149337113},{"cpu_ms":288.69000000000256,"wall_ms":296.6634270269424},{"cpu_ms":242.87199999999842,"wall_ms":257.9674259759486},{"cpu_ms":274.34800000000337,"wall_ms":286.4301900845021},{"cpu_ms":318.06500000000426,"wall_ms":326.52916200459003},{"cpu_ms":297.6190000000045,"wall_ms":311.3943978678435},{"cpu_ms":310.86400000000225,"wall_ms":320.2645489946008},{"cpu_ms":342.8520000000006,"wall_ms":354.1207481175661},{"cpu_ms":316.358000000001,"wall_ms":404.90747615695}],"before_median_cpu_ms":317.2115000000026,"after":[{"cpu_ms":339.8109999999974,"wall_ms":349.50778912752867},{"cpu_ms":346.8260000000001,"wall_ms":653.4595228731632},{"cpu_ms":331.116999999999,"wall_ms":344.9996670242399},{"cpu_ms":339.10900000000055,"wall_ms":754.065542947501},{"cpu_ms":294.6529999999967,"wall_ms":306.84527894482017},{"cpu_ms":248.57500000000243,"wall_ms":261.38242706656456},{"cpu_ms":290.56999999999533,"wall_ms":299.1406659130007},{"cpu_ms":293.2709999999972,"wall_ms":299.60618494078517},{"cpu_ms":285.4079999999968,"wall_ms":295.92161206528544},{"cpu_ms":327.58299999999707,"wall_ms":346.639301860705},{"cpu_ms":354.00299999999874,"wall_ms":428.7839410826564},{"cpu_ms":352.73200000000315,"wall_ms":373.9179321564734}],"after_median_cpu_ms":329.34999999999803,"before_control":[{"cpu_ms":310.45599999999496,"wall_ms":321.361216949299},{"cpu_ms":335.7120000000009,"wall_ms":355.3306881804019},{"cpu_ms":339.27599999999813,"wall_ms":351.17965284734964},{"cpu_ms":309.46300000000093,"wall_ms":345.44808603823185},{"cpu_ms":302.01600000000184,"wall_ms":311.64874299429357},{"cpu_ms":231.88999999999993,"wall_ms":241.22826498933136},{"cpu_ms":307.81000000000347,"wall_ms":319.55640600062907},{"cpu_ms":298.32300000000345,"wall_ms":308.08665906079113},{"cpu_ms":292.6399999999987,"wall_ms":305.6161729618907},{"cpu_ms":293.2570000000041,"wall_ms":306.0227329842746},{"cpu_ms":341.7340000000024,"wall_ms":352.77892113663256},{"cpu_ms":340.7899999999984,"wall_ms":375.22594607435167}],"before_control_median_cpu_ms":308.63650000000223,"after_control":[{"cpu_ms":319.8630000000051,"wall_ms":363.2797980681062},{"cpu_ms":345.19399999999933,"wall_ms":369.2299339454621},{"cpu_ms":355.38199999999875,"wall_ms":531.4897729549557},{"cpu_ms":345.81500000000176,"wall_ms":478.17141585983336},{"cpu_ms":294.38899999999535,"wall_ms":302.4677310604602},{"cpu_ms":276.60000000000196,"wall_ms":287.4897599685937},{"cpu_ms":281.3079999999957,"wall_ms":289.6473901346326},{"cpu_ms":298.6009999999979,"wall_ms":309.3848340213299},{"cpu_ms":294.62199999999683,"wall_ms":301.27084511332214},{"cpu_ms":328.25499999999863,"wall_ms":337.2407129500061},{"cpu_ms":331.3709999999972,"wall_ms":343.4041179716587},{"cpu_ms":332.9689999999985,"wall_ms":466.3785349112004}],"after_control_median_cpu_ms":324.0590000000019}, +{"name":"object_deep_clone","node_exit":0,"before":[{"cpu_ms":16.606999999993377,"wall_ms":19.84529383480549},{"cpu_ms":29.690000000002215,"wall_ms":35.81412695348263},{"cpu_ms":32.60500000000377,"wall_ms":38.70211215689778},{"cpu_ms":32.26700000000449,"wall_ms":36.86849609948695},{"cpu_ms":28.267999999997073,"wall_ms":175.125814974308},{"cpu_ms":28.027999999999054,"wall_ms":48.251181142404675},{"cpu_ms":24.414000000000158,"wall_ms":31.138672027736902},{"cpu_ms":28.334999999998445,"wall_ms":38.42440294101834},{"cpu_ms":16.27200000000073,"wall_ms":19.617098150774837},{"cpu_ms":21.211999999998454,"wall_ms":25.657232152298093},{"cpu_ms":29.499000000001274,"wall_ms":68.81646090187132},{"cpu_ms":27.860000000003993,"wall_ms":93.60481007024646}],"before_median_cpu_ms":28.147999999998063,"after":[{"cpu_ms":24.604000000003623,"wall_ms":28.92281301319599},{"cpu_ms":30.496999999996888,"wall_ms":37.458069156855345},{"cpu_ms":33.04099999999721,"wall_ms":37.420317996293306},{"cpu_ms":30.853000000000463,"wall_ms":38.2916450034827},{"cpu_ms":27.291000000005283,"wall_ms":30.789771117269993},{"cpu_ms":28.93900000000116,"wall_ms":119.293631054461},{"cpu_ms":24.60699999999605,"wall_ms":80.82703896798193},{"cpu_ms":26.562000000005526,"wall_ms":31.27974597737193},{"cpu_ms":28.995999999999356,"wall_ms":66.59809686243534},{"cpu_ms":21.719000000004485,"wall_ms":31.49351617321372},{"cpu_ms":30.96999999999639,"wall_ms":173.78301708959043},{"cpu_ms":29.888999999997168,"wall_ms":61.05265696533024}],"after_median_cpu_ms":28.967500000000257,"before_control":[{"cpu_ms":33.51400000000382,"wall_ms":36.999150877818465},{"cpu_ms":34.10900000000083,"wall_ms":37.0240667834878},{"cpu_ms":31.200999999995815,"wall_ms":36.992848850786686},{"cpu_ms":16.240999999993733,"wall_ms":21.68341795913875},{"cpu_ms":17.90499999999895,"wall_ms":23.285130970180035},{"cpu_ms":23.759000000005415,"wall_ms":141.7661840096116},{"cpu_ms":28.53600000000256,"wall_ms":37.71888604387641},{"cpu_ms":27.20899999999915,"wall_ms":32.52251795493066},{"cpu_ms":38.25899999999649,"wall_ms":79.94299801066518},{"cpu_ms":29.40399999999954,"wall_ms":32.06134610809386},{"cpu_ms":27.124999999998067,"wall_ms":118.79170290194452},{"cpu_ms":28.507000000004723,"wall_ms":49.366435036063194}],"before_control_median_cpu_ms":28.52150000000364,"after_control":[{"cpu_ms":34.06999999999982,"wall_ms":36.503985058516264},{"cpu_ms":32.099999999999795,"wall_ms":37.57919487543404},{"cpu_ms":34.65200000000124,"wall_ms":38.69292512536049},{"cpu_ms":18.064999999999998,"wall_ms":66.65780814364552},{"cpu_ms":22.975999999999885,"wall_ms":30.021836049854755},{"cpu_ms":24.929999999997676,"wall_ms":143.013282911852},{"cpu_ms":30.042999999999154,"wall_ms":33.351866994053125},{"cpu_ms":28.517999999998267,"wall_ms":46.20825499296188},{"cpu_ms":24.278000000002464,"wall_ms":31.653273850679398},{"cpu_ms":26.032999999998196,"wall_ms":34.14508909918368},{"cpu_ms":25.26900000000154,"wall_ms":34.84354284591973},{"cpu_ms":31.753999999999394,"wall_ms":189.54671686515212}],"after_control_median_cpu_ms":27.27549999999823}, +{"name":"promise_all_chains","node_exit":0,"before":[{"cpu_ms":108.69799999999685,"wall_ms":114.40494819544256},{"cpu_ms":190.55100000000635,"wall_ms":198.01231217570603},{"cpu_ms":188.18199999999763,"wall_ms":196.77457911893725},{"cpu_ms":143.9190000000039,"wall_ms":171.5214909054339},{"cpu_ms":196.8300000000056,"wall_ms":352.754978928715},{"cpu_ms":122.46999999999986,"wall_ms":140.84098604507744},{"cpu_ms":121.9469999999987,"wall_ms":282.4467702303082},{"cpu_ms":165.50900000000013,"wall_ms":218.99289707653224},{"cpu_ms":105.73099999999869,"wall_ms":146.30505396053195},{"cpu_ms":186.26600000000337,"wall_ms":358.8613560423255},{"cpu_ms":137.58699999999635,"wall_ms":215.7529650721699},{"cpu_ms":166.35500000000292,"wall_ms":345.1529871672392}],"before_median_cpu_ms":154.71400000000202,"after":[{"cpu_ms":152.94600000000003,"wall_ms":268.0589209776372},{"cpu_ms":191.7009999999948,"wall_ms":196.29001384600997},{"cpu_ms":144.3739999999991,"wall_ms":193.2912520132959},{"cpu_ms":117.58099999999416,"wall_ms":167.2382508404553},{"cpu_ms":139.14799999999872,"wall_ms":332.85996108315885},{"cpu_ms":133.03099999999546,"wall_ms":209.4366850797087},{"cpu_ms":140.01300000000327,"wall_ms":165.3591599315405},{"cpu_ms":106.11399999999804,"wall_ms":154.57738796249032},{"cpu_ms":159.3730000000022,"wall_ms":405.13322106562555},{"cpu_ms":108.83499999999913,"wall_ms":113.18262503482401},{"cpu_ms":112.66100000000279,"wall_ms":213.845751946792},{"cpu_ms":138.86300000000062,"wall_ms":169.87546696327627}],"after_median_cpu_ms":139.00549999999967,"before_control":[{"cpu_ms":103.549000000001,"wall_ms":105.98211293108761},{"cpu_ms":181.22799999999728,"wall_ms":185.98804785870016},{"cpu_ms":146.83899999999994,"wall_ms":155.01945419237018},{"cpu_ms":212.6650000000012,"wall_ms":226.97645099833608},{"cpu_ms":181.03799999999381,"wall_ms":202.39834487438202},{"cpu_ms":107.77800000000326,"wall_ms":163.31505007110536},{"cpu_ms":103.85600000000039,"wall_ms":197.07187497988343},{"cpu_ms":111.73300000000097,"wall_ms":197.23203405737877},{"cpu_ms":115.9759999999963,"wall_ms":246.35031586512923},{"cpu_ms":138.15199999999805,"wall_ms":219.7046261280775},{"cpu_ms":114.4779999999983,"wall_ms":186.53597100637853},{"cpu_ms":156.03699999999776,"wall_ms":290.81766912713647}],"before_control_median_cpu_ms":127.06399999999718,"after_control":[{"cpu_ms":122.53900000000328,"wall_ms":131.90718600526452},{"cpu_ms":100.79600000000255,"wall_ms":107.13202087208629},{"cpu_ms":196.03899999999896,"wall_ms":205.47626493498683},{"cpu_ms":196.86000000000092,"wall_ms":215.33323009498417},{"cpu_ms":100.11300000000034,"wall_ms":169.59006409160793},{"cpu_ms":103.58500000000248,"wall_ms":140.8574259839952},{"cpu_ms":122.3150000000004,"wall_ms":184.9642558954656},{"cpu_ms":110.44199999999904,"wall_ms":179.93237380869687},{"cpu_ms":114.56100000000191,"wall_ms":194.6067400276661},{"cpu_ms":115.7790000000034,"wall_ms":163.350097136572},{"cpu_ms":199.80999999999938,"wall_ms":237.35548090189695},{"cpu_ms":106.93200000000047,"wall_ms":193.3144151698798}],"after_control_median_cpu_ms":115.17000000000266}, +{"name":"regex_replace","node_exit":0,"before":[{"cpu_ms":2715.0099999999925,"wall_ms":2981.6673651803285},{"cpu_ms":2836.4839999999986,"wall_ms":3648.683869978413},{"cpu_ms":2862.7560000000044,"wall_ms":3410.835091024637},{"cpu_ms":3005.9200000000033,"wall_ms":3777.660029940307},{"cpu_ms":3128.5179999999996,"wall_ms":3762.125907931477},{"cpu_ms":3584.0100000000066,"wall_ms":4645.093580009416},{"cpu_ms":3394.252999999992,"wall_ms":4284.902160987258},{"cpu_ms":3760.5499999999947,"wall_ms":4664.13295106031},{"cpu_ms":3351.0969999999816,"wall_ms":4189.383047865704},{"cpu_ms":2951.3100000000068,"wall_ms":3008.6598058696836},{"cpu_ms":3501.9860000000162,"wall_ms":3556.1294998042285},{"cpu_ms":2842.3509999999796,"wall_ms":3060.3681220673025}],"before_median_cpu_ms":3067.2190000000014,"after":[{"cpu_ms":2575.770000000006,"wall_ms":2881.8209229502827},{"cpu_ms":2493.183000000002,"wall_ms":3128.491133917123},{"cpu_ms":2504.07899999999,"wall_ms":3043.25486603193},{"cpu_ms":2841.3869999999974,"wall_ms":3433.0295601394027},{"cpu_ms":2734.1540000000036,"wall_ms":3378.9771541487426},{"cpu_ms":3428.9500000000144,"wall_ms":4830.63555508852},{"cpu_ms":2887.3230000000094,"wall_ms":3644.446399062872},{"cpu_ms":3227.2649999999885,"wall_ms":4210.344889899716},{"cpu_ms":3289.0650000000223,"wall_ms":4059.1391739435494},{"cpu_ms":2786.9660000000067,"wall_ms":2845.2868529129773},{"cpu_ms":2792.8440000000023,"wall_ms":2941.092671826482},{"cpu_ms":2589.922999999999,"wall_ms":2828.480469994247}],"after_median_cpu_ms":2789.9050000000043,"before_control":[{"cpu_ms":2911.7940000000003,"wall_ms":3450.2325910143554},{"cpu_ms":3015.268000000006,"wall_ms":3501.4886278659105},{"cpu_ms":2793.097000000003,"wall_ms":3486.97407101281},{"cpu_ms":2855.795999999998,"wall_ms":3454.888358945027},{"cpu_ms":3158.005000000003,"wall_ms":3922.648196807131},{"cpu_ms":2973.8030000000035,"wall_ms":3591.0167740657926},{"cpu_ms":3381.8270000000157,"wall_ms":4327.077842084691},{"cpu_ms":3767.1399999999835,"wall_ms":4525.548022938892},{"cpu_ms":3283.290999999991,"wall_ms":3821.8199210241437},{"cpu_ms":3347.5489999999863,"wall_ms":4125.091755064204},{"cpu_ms":3054.1529999999852,"wall_ms":3257.0549689698964},{"cpu_ms":3775.2360000000067,"wall_ms":4114.003716036677}],"before_control_median_cpu_ms":3106.0789999999943,"after_control":[{"cpu_ms":2516.5020000000027,"wall_ms":3035.6221348047256},{"cpu_ms":2379.466999999991,"wall_ms":2808.3863200154155},{"cpu_ms":2403.2780000000002,"wall_ms":2924.198993947357},{"cpu_ms":2610.212000000004,"wall_ms":3098.4980538487434},{"cpu_ms":2445.9909999999923,"wall_ms":3147.3278761841357},{"cpu_ms":2766.7999999999893,"wall_ms":3496.481562964618},{"cpu_ms":3583.007999999978,"wall_ms":5396.150608081371},{"cpu_ms":3016.8110000000183,"wall_ms":3827.430394012481},{"cpu_ms":2946.9040000000177,"wall_ms":3686.550661921501},{"cpu_ms":2876.0090000000105,"wall_ms":2892.1738651115447},{"cpu_ms":2583.6099999999933,"wall_ms":2724.945372901857},{"cpu_ms":2786.4700000000084,"wall_ms":3114.5133471582085}],"after_control_median_cpu_ms":2688.5059999999967}, +{"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":44.53900000001454,"wall_ms":200.09065908379853},{"cpu_ms":43.88900000000717,"wall_ms":80.63357905484736},{"cpu_ms":47.83700000001545,"wall_ms":57.34539311379194},{"cpu_ms":42.72599999998761,"wall_ms":157.733216881752},{"cpu_ms":41.8640000000039,"wall_ms":166.33599903434515},{"cpu_ms":52.86599999999453,"wall_ms":140.54646901786327},{"cpu_ms":52.44500000000585,"wall_ms":69.01094620116055},{"cpu_ms":44.86600000001317,"wall_ms":241.27586209215224},{"cpu_ms":53.07200000001444,"wall_ms":146.3772018905729},{"cpu_ms":54.53299999999217,"wall_ms":189.61610086262226},{"cpu_ms":28.71599999997443,"wall_ms":36.459784023463726},{"cpu_ms":29.921999999999116,"wall_ms":34.445981960743666}],"before_median_cpu_ms":44.702500000013856,"after":[{"cpu_ms":46.723999999983334,"wall_ms":140.5580211430788},{"cpu_ms":43.23999999999728,"wall_ms":97.25894895382226},{"cpu_ms":48.67899999999281,"wall_ms":181.65583000518382},{"cpu_ms":53.466000000014446,"wall_ms":203.30818998627365},{"cpu_ms":48.586999999997715,"wall_ms":90.14428709633648},{"cpu_ms":55.137999999999465,"wall_ms":132.73966405540705},{"cpu_ms":49.86299999998778,"wall_ms":92.73088001646101},{"cpu_ms":44.77999999997451,"wall_ms":171.18706181645393},{"cpu_ms":33.26299999997673,"wall_ms":243.37261891923845},{"cpu_ms":28.939000000008264,"wall_ms":146.37395506724715},{"cpu_ms":29.38700000001404,"wall_ms":36.22607700526714},{"cpu_ms":38.17000000000803,"wall_ms":48.090853029862046}],"after_median_cpu_ms":45.75199999997892,"before_control":[{"cpu_ms":47.75999999998248,"wall_ms":83.00677314400673},{"cpu_ms":43.82699999999318,"wall_ms":51.872878801077604},{"cpu_ms":50.47700000000077,"wall_ms":63.085647067055106},{"cpu_ms":47.04599999999459,"wall_ms":152.950783027336},{"cpu_ms":37.966999999980544,"wall_ms":162.45887498371303},{"cpu_ms":49.19500000002586,"wall_ms":139.4572239369154},{"cpu_ms":49.34299999999325,"wall_ms":56.186688132584095},{"cpu_ms":32.95100000002549,"wall_ms":87.37463410943747},{"cpu_ms":49.65999999998871,"wall_ms":118.47720993682742},{"cpu_ms":44.18300000000386,"wall_ms":178.79187897779047},{"cpu_ms":27.75000000002592,"wall_ms":36.182866897433996},{"cpu_ms":48.24800000000096,"wall_ms":50.85429595783353}],"before_control_median_cpu_ms":47.40299999998854,"after_control":[{"cpu_ms":44.247000000012804,"wall_ms":49.10837416537106},{"cpu_ms":41.427999999996246,"wall_ms":94.36953091062605},{"cpu_ms":42.69899999999893,"wall_ms":104.74438313394785},{"cpu_ms":53.04100000000744,"wall_ms":137.71981792524457},{"cpu_ms":51.8859999999961,"wall_ms":216.46842593327165},{"cpu_ms":54.175999999984015,"wall_ms":157.9908279236406},{"cpu_ms":46.75400000002128,"wall_ms":62.14144313707948},{"cpu_ms":42.8449999999998,"wall_ms":57.28703388012946},{"cpu_ms":44.59800000000769,"wall_ms":153.3288990613073},{"cpu_ms":32.03099999998926,"wall_ms":161.1711699515581},{"cpu_ms":39.16599999999448,"wall_ms":49.904981860890985},{"cpu_ms":30.539000000004535,"wall_ms":37.17196499928832}],"after_control_median_cpu_ms":43.5460000000063}, +{"name":"string_split_map_join","node_exit":0,"before":[{"cpu_ms":288.0819999999744,"wall_ms":419.224055018276},{"cpu_ms":280.4689999999823,"wall_ms":337.3917159624398},{"cpu_ms":285.68899999999076,"wall_ms":387.2068098280579},{"cpu_ms":267.5190000000214,"wall_ms":407.4177979491651},{"cpu_ms":272.026000000011,"wall_ms":391.50859601795673},{"cpu_ms":303.9489999999887,"wall_ms":442.08180787973106},{"cpu_ms":260.058000000015,"wall_ms":427.5956340134144},{"cpu_ms":236.28600000000688,"wall_ms":379.44106198847294},{"cpu_ms":176.07100000000742,"wall_ms":323.2156219892204},{"cpu_ms":282.40399999998544,"wall_ms":470.2573758549988},{"cpu_ms":273.08299999998553,"wall_ms":399.1296850144863},{"cpu_ms":273.8990000000001,"wall_ms":471.54602291993797}],"before_median_cpu_ms":273.4909999999928,"after":[{"cpu_ms":204.74300000000767,"wall_ms":308.306529186666},{"cpu_ms":285.5869999999925,"wall_ms":464.5433910191059},{"cpu_ms":274.26099999999565,"wall_ms":431.17804802022874},{"cpu_ms":258.7039999999945,"wall_ms":417.89920907467604},{"cpu_ms":269.76700000000164,"wall_ms":300.9398758877069},{"cpu_ms":316.268000000008,"wall_ms":466.70122095383704},{"cpu_ms":235.7629999999915,"wall_ms":373.242812929675},{"cpu_ms":186.396000000002,"wall_ms":283.85404706932604},{"cpu_ms":291.5140000000065,"wall_ms":354.7506150789559},{"cpu_ms":258.8790000000074,"wall_ms":445.2110040001571},{"cpu_ms":271.9590000000096,"wall_ms":458.9890439528972},{"cpu_ms":227.70399999998858,"wall_ms":469.9503821320832}],"after_median_cpu_ms":264.3230000000045,"before_control":[{"cpu_ms":278.663000000023,"wall_ms":399.25764314830303},{"cpu_ms":257.788000000005,"wall_ms":413.60517777502537},{"cpu_ms":288.7609999999938,"wall_ms":395.4168688505888},{"cpu_ms":279.16799999999853,"wall_ms":463.7973168864846},{"cpu_ms":284.37900000000127,"wall_ms":498.98806982673705},{"cpu_ms":254.1530000000023,"wall_ms":440.22307521663606},{"cpu_ms":289.59699999998634,"wall_ms":424.59588102065027},{"cpu_ms":276.06599999998593,"wall_ms":454.861783888191},{"cpu_ms":177.93599999998833,"wall_ms":287.3337168712169},{"cpu_ms":217.12700000000495,"wall_ms":394.59105604328215},{"cpu_ms":246.74799999999664,"wall_ms":402.3829728830606},{"cpu_ms":233.5960000000057,"wall_ms":534.8063469864428}],"before_control_median_cpu_ms":266.9269999999955,"after_control":[{"cpu_ms":246.04499999998097,"wall_ms":345.12065490707755},{"cpu_ms":224.49600000001624,"wall_ms":239.2687019892037},{"cpu_ms":229.79500000002417,"wall_ms":340.08698002435267},{"cpu_ms":274.00699999998324,"wall_ms":385.643889894709},{"cpu_ms":254.85000000000468,"wall_ms":376.988589996472},{"cpu_ms":207.06099999998173,"wall_ms":373.8945599179715},{"cpu_ms":214.34300000001372,"wall_ms":368.8388648442924},{"cpu_ms":248.9879999999971,"wall_ms":382.9772789031267},{"cpu_ms":214.09700000000953,"wall_ms":396.1592959240079},{"cpu_ms":268.67999999998915,"wall_ms":406.5009159967303},{"cpu_ms":238.72200000002408,"wall_ms":411.76266595721245},{"cpu_ms":273.11299999999505,"wall_ms":505.1557628903538}],"after_control_median_cpu_ms":242.38350000000253}, +{"name":"string_template_interp","node_exit":0,"before":[{"cpu_ms":72.22600000000057,"wall_ms":360.6115549337119},{"cpu_ms":60.0509999999872,"wall_ms":241.56539305113256},{"cpu_ms":63.89400000000478,"wall_ms":239.33299188502133},{"cpu_ms":67.18400000002589,"wall_ms":182.99576314166188},{"cpu_ms":76.0400000000061,"wall_ms":230.61724985018373},{"cpu_ms":52.98799999999915,"wall_ms":230.30076804570854},{"cpu_ms":59.75300000000061,"wall_ms":203.14446580596268},{"cpu_ms":76.0089999999991,"wall_ms":278.64130795933306},{"cpu_ms":74.1599999999778,"wall_ms":327.20642792992294},{"cpu_ms":58.929000000006226,"wall_ms":349.0823570173234},{"cpu_ms":73.86900000000196,"wall_ms":184.0707720257342},{"cpu_ms":53.9049999999861,"wall_ms":259.1457769740373}],"before_median_cpu_ms":65.53900000001533,"after":[{"cpu_ms":74.79399999999714,"wall_ms":205.1246939226985},{"cpu_ms":53.290000000004056,"wall_ms":181.68509495444596},{"cpu_ms":62.93599999997923,"wall_ms":276.0471620131284},{"cpu_ms":74.34499999999389,"wall_ms":189.44079405628145},{"cpu_ms":81.43100000000913,"wall_ms":210.12947405688465},{"cpu_ms":58.483999999992875,"wall_ms":286.67003801092505},{"cpu_ms":83.35199999999077,"wall_ms":278.2507089432329},{"cpu_ms":69.28000000002044,"wall_ms":317.8048951085657},{"cpu_ms":58.00800000000095,"wall_ms":133.61581088975072},{"cpu_ms":56.06399999999212,"wall_ms":229.205691954121},{"cpu_ms":58.59399999999937,"wall_ms":345.00460885465145},{"cpu_ms":62.293000000011034,"wall_ms":154.31708586402237}],"after_median_cpu_ms":62.61449999999513,"before_control":[{"cpu_ms":75.6640000000175,"wall_ms":273.2403881382197},{"cpu_ms":60.265000000015334,"wall_ms":161.58135700970888},{"cpu_ms":64.5150000000001,"wall_ms":255.90431108139455},{"cpu_ms":80.24999999997817,"wall_ms":206.51246700435877},{"cpu_ms":76.93299999999681,"wall_ms":199.65766184031963},{"cpu_ms":72.79599999998254,"wall_ms":202.91210105642676},{"cpu_ms":81.84299999999212,"wall_ms":281.02821786887944},{"cpu_ms":57.65400000001364,"wall_ms":229.01000804267824},{"cpu_ms":85.72200000000407,"wall_ms":233.98838797584176},{"cpu_ms":44.3429999999978,"wall_ms":87.05081883817911},{"cpu_ms":63.06200000000217,"wall_ms":233.55670203454792},{"cpu_ms":61.308999999994285,"wall_ms":289.9446659721434}],"before_control_median_cpu_ms":68.65549999999132,"after_control":[{"cpu_ms":75.82099999999059,"wall_ms":171.0179760120809},{"cpu_ms":49.70399999999131,"wall_ms":239.82637492008507},{"cpu_ms":46.20800000000713,"wall_ms":91.76761098206043},{"cpu_ms":66.24800000000164,"wall_ms":201.3244149275124},{"cpu_ms":77.58000000001175,"wall_ms":209.15264007635415},{"cpu_ms":64.4190000000151,"wall_ms":239.80790004134178},{"cpu_ms":75.09300000000962,"wall_ms":227.43529616855085},{"cpu_ms":56.685999999984915,"wall_ms":267.3260089941323},{"cpu_ms":76.88000000001693,"wall_ms":162.95570391230285},{"cpu_ms":61.97299999999473,"wall_ms":213.93417194485664},{"cpu_ms":62.85900000000311,"wall_ms":219.61681894026697},{"cpu_ms":72.11799999998902,"wall_ms":175.04679295234382}],"after_control_median_cpu_ms":65.33350000000837} +] diff --git a/benchmarks/regexp-construction/final-json-control-samples.json b/benchmarks/regexp-construction/final-json-control-samples.json new file mode 100644 index 0000000000..06c827ea72 --- /dev/null +++ b/benchmarks/regexp-construction/final-json-control-samples.json @@ -0,0 +1,3 @@ +[ +{"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":59.74499999999993,"wall_ms":65.16051688231528},{"cpu_ms":47.82099999999989,"wall_ms":56.74491496756673},{"cpu_ms":45.96799999999979,"wall_ms":53.29167516902089},{"cpu_ms":48.344000000000165,"wall_ms":53.713371977210045},{"cpu_ms":45.44900000000007,"wall_ms":52.80478508211672},{"cpu_ms":47.90099999999997,"wall_ms":54.19985088519752},{"cpu_ms":49.582999999999714,"wall_ms":54.887165082618594},{"cpu_ms":51.9940000000001,"wall_ms":55.29214208945632},{"cpu_ms":48.50399999999988,"wall_ms":52.83768707886338},{"cpu_ms":46.216999999999956,"wall_ms":53.389868000522256},{"cpu_ms":47.62999999999984,"wall_ms":54.910538950935006},{"cpu_ms":46.30899999999993,"wall_ms":53.49215003661811},{"cpu_ms":50.679999999999836,"wall_ms":54.02428307570517},{"cpu_ms":45.52900000000015,"wall_ms":51.884206011891365},{"cpu_ms":45.270000000000366,"wall_ms":53.43055399134755},{"cpu_ms":45.50400000000021,"wall_ms":52.86865495145321},{"cpu_ms":47.49299999999934,"wall_ms":52.85551003180444},{"cpu_ms":49.49500000000029,"wall_ms":52.74649593047798},{"cpu_ms":43.699999999999406,"wall_ms":53.94777096807957},{"cpu_ms":48.12100000000008,"wall_ms":53.2780890353024},{"cpu_ms":46.929999999999694,"wall_ms":54.20727585442364},{"cpu_ms":47.731999999999886,"wall_ms":54.009777028113604},{"cpu_ms":50.74299999999976,"wall_ms":57.1842968929559},{"cpu_ms":45.75599999999991,"wall_ms":54.046174976974726},{"cpu_ms":43.667999999999374,"wall_ms":51.88347492367029},{"cpu_ms":49.05299999999979,"wall_ms":53.320067934691906},{"cpu_ms":48.61599999999999,"wall_ms":52.944896975532174},{"cpu_ms":49.0750000000002,"wall_ms":53.271897835657},{"cpu_ms":50.341999999999665,"wall_ms":52.64215194620192},{"cpu_ms":49.47699999999955,"wall_ms":52.78038000687957},{"cpu_ms":47.259000000000384,"wall_ms":54.47217985056341},{"cpu_ms":50.56899999999942,"wall_ms":52.864937111735344},{"cpu_ms":49.46300000000026,"wall_ms":52.749651949852705},{"cpu_ms":45.738000000000056,"wall_ms":62.60696705430746},{"cpu_ms":48.88900000000085,"wall_ms":53.18814120255411},{"cpu_ms":70.94999999999985,"wall_ms":80.17234085127711},{"cpu_ms":79.26699999999975,"wall_ms":124.94688504375517},{"cpu_ms":84.17600000000114,"wall_ms":105.89603590779006},{"cpu_ms":81.98099999999897,"wall_ms":105.34977586939931},{"cpu_ms":81.8329999999996,"wall_ms":114.38500392250717},{"cpu_ms":83.6030000000001,"wall_ms":118.48357180133462},{"cpu_ms":75.2019999999991,"wall_ms":93.36453606374562},{"cpu_ms":86.03899999999953,"wall_ms":146.88333496451378},{"cpu_ms":83.38100000000104,"wall_ms":122.47662106528878},{"cpu_ms":82.55299999999899,"wall_ms":86.9154289830476},{"cpu_ms":85.85300000000018,"wall_ms":91.26342087984085},{"cpu_ms":53.23699999999931,"wall_ms":55.56267709471285},{"cpu_ms":76.86499999999974,"wall_ms":83.58193095773458},{"cpu_ms":73.84200000000085,"wall_ms":81.74912701360881},{"cpu_ms":55.69199999999874,"wall_ms":61.576161067932844},{"cpu_ms":67.48000000000154,"wall_ms":98.54551497846842},{"cpu_ms":72.51800000000053,"wall_ms":88.38931191712618},{"cpu_ms":79.20700000000025,"wall_ms":86.59416809678078},{"cpu_ms":48.327999999999705,"wall_ms":54.67104213312268},{"cpu_ms":50.45699999999975,"wall_ms":53.77960694022477},{"cpu_ms":47.32700000000101,"wall_ms":54.51031192205846},{"cpu_ms":77.91399999999982,"wall_ms":86.31182089447975},{"cpu_ms":47.64099999999871,"wall_ms":55.05952797830105},{"cpu_ms":47.34900000000053,"wall_ms":54.71298121847212},{"cpu_ms":54.34300000000114,"wall_ms":56.657182052731514},{"cpu_ms":80.28399999999891,"wall_ms":87.71478198468685},{"cpu_ms":78.94899999999971,"wall_ms":89.36890191398561},{"cpu_ms":81.81499999999886,"wall_ms":86.25660790130496},{"cpu_ms":72.57900000000106,"wall_ms":79.91665415465832},{"cpu_ms":81.89799999999892,"wall_ms":88.34905712865293},{"cpu_ms":48.84000000000199,"wall_ms":57.92536190710962},{"cpu_ms":50.015999999999394,"wall_ms":55.32276909798384},{"cpu_ms":56.49700000000024,"wall_ms":58.795507065951824},{"cpu_ms":64.1840000000009,"wall_ms":73.60061397776008},{"cpu_ms":49.17199999999866,"wall_ms":54.42566308192909},{"cpu_ms":48.98600000000286,"wall_ms":53.27456281520426},{"cpu_ms":49.10500000000084,"wall_ms":53.407391998916864},{"cpu_ms":47.471000000001595,"wall_ms":52.93256416916847},{"cpu_ms":46.03299999999777,"wall_ms":53.337911842390895},{"cpu_ms":49.226999999998355,"wall_ms":53.53786493651569},{"cpu_ms":49.72099999999813,"wall_ms":53.98189416155219},{"cpu_ms":46.994000000001535,"wall_ms":53.29001182690263},{"cpu_ms":48.940999999999235,"wall_ms":53.2283668871969},{"cpu_ms":48.34200000000166,"wall_ms":53.60590200871229},{"cpu_ms":49.58699999999894,"wall_ms":53.8548871409148},{"cpu_ms":46.886999999998125,"wall_ms":63.15074302256107},{"cpu_ms":49.6149999999993,"wall_ms":52.947652991861105},{"cpu_ms":42.71200000000164,"wall_ms":52.92781605385244},{"cpu_ms":49.38899999999791,"wall_ms":54.71576587297022},{"cpu_ms":46.752999999998934,"wall_ms":53.01702185533941},{"cpu_ms":46.37400000000014,"wall_ms":52.58590611629188},{"cpu_ms":47.98500000000061,"wall_ms":53.27271996065974},{"cpu_ms":47.90399999999906,"wall_ms":54.173473035916686},{"cpu_ms":45.970999999997986,"wall_ms":53.24962711893022},{"cpu_ms":50.76100000000139,"wall_ms":55.041212821379304},{"cpu_ms":86.11900000000006,"wall_ms":89.52536410652101},{"cpu_ms":74.06399999999991,"wall_ms":89.70426791347563},{"cpu_ms":71.10600000000034,"wall_ms":77.86868792027235},{"cpu_ms":84.13799999999938,"wall_ms":93.51546806283295},{"cpu_ms":74.09399999999877,"wall_ms":83.48128292709589},{"cpu_ms":71.22399999999729,"wall_ms":78.6244401242584},{"cpu_ms":77.62599999999864,"wall_ms":84.96978413313627},{"cpu_ms":81.36400000000066,"wall_ms":86.82275516912341},{"cpu_ms":75.61700000000116,"wall_ms":93.05382496677339},{"cpu_ms":71.96300000000022,"wall_ms":80.70246200077236}],"before_median_cpu_ms":49.539,"after":[{"cpu_ms":62.49400000000005,"wall_ms":66.812934121117},{"cpu_ms":44.023000000000145,"wall_ms":56.13517598249018},{"cpu_ms":49.37600000000009,"wall_ms":54.6787572093308},{"cpu_ms":53.54400000000004,"wall_ms":58.788493974134326},{"cpu_ms":47.46300000000004,"wall_ms":52.80169006437063},{"cpu_ms":48.11200000000015,"wall_ms":53.418411873281},{"cpu_ms":49.95400000000005,"wall_ms":54.24934392794967},{"cpu_ms":49.463999999999956,"wall_ms":53.80107695236802},{"cpu_ms":48.461000000000084,"wall_ms":52.796589909121394},{"cpu_ms":47.02099999999998,"wall_ms":53.30738378688693},{"cpu_ms":50.63100000000009,"wall_ms":53.955655079334974},{"cpu_ms":49.97400000000019,"wall_ms":53.261638851836324},{"cpu_ms":46.70200000000024,"wall_ms":52.96173878014088},{"cpu_ms":49.088999999999494,"wall_ms":52.37750709056854},{"cpu_ms":50.784999999999414,"wall_ms":54.056954104453325},{"cpu_ms":45.105000000000395,"wall_ms":52.403034875169396},{"cpu_ms":47.46200000000034,"wall_ms":52.800687961280346},{"cpu_ms":47.407999999999895,"wall_ms":52.84902919083834},{"cpu_ms":46.25500000000038,"wall_ms":54.425833048298955},{"cpu_ms":47.50199999999971,"wall_ms":52.95752012170851},{"cpu_ms":48.63900000000054,"wall_ms":53.98907791823149},{"cpu_ms":55.441000000000074,"wall_ms":59.867429081350565},{"cpu_ms":50.63100000000009,"wall_ms":53.97123494185507},{"cpu_ms":50.346000000000224,"wall_ms":52.63362592086196},{"cpu_ms":46.7840000000006,"wall_ms":56.06836196966469},{"cpu_ms":50.4920000000002,"wall_ms":52.783614955842495},{"cpu_ms":48.475999999999964,"wall_ms":51.80372507311404},{"cpu_ms":50.40599999999973,"wall_ms":52.68866801634431},{"cpu_ms":48.942999999999515,"wall_ms":53.2103730365634},{"cpu_ms":50.699000000000716,"wall_ms":54.00194204412401},{"cpu_ms":47.864999999999824,"wall_ms":52.12004599161446},{"cpu_ms":48.57000000000067,"wall_ms":52.80991503968835},{"cpu_ms":47.118000000000215,"wall_ms":52.38406010903418},{"cpu_ms":48.06999999999917,"wall_ms":55.340813007205725},{"cpu_ms":49.09400000000019,"wall_ms":52.377837942913175},{"cpu_ms":83.1129999999991,"wall_ms":118.64762799814343},{"cpu_ms":82.66799999999996,"wall_ms":110.06957292556763},{"cpu_ms":81.08799999999938,"wall_ms":102.44590090587735},{"cpu_ms":86.07500000000101,"wall_ms":118.16775100305676},{"cpu_ms":82.34700000000039,"wall_ms":108.43247501179576},{"cpu_ms":81.74999999999955,"wall_ms":155.27625405229628},{"cpu_ms":83.64100000000008,"wall_ms":105.04500614479184},{"cpu_ms":85.4689999999998,"wall_ms":163.52554503828287},{"cpu_ms":84.72999999999864,"wall_ms":102.63651702553034},{"cpu_ms":58.30900000000128,"wall_ms":66.94619380868971},{"cpu_ms":48.30300000000065,"wall_ms":58.6499348282814},{"cpu_ms":49.756000000000355,"wall_ms":58.11873287893832},{"cpu_ms":51.144999999999996,"wall_ms":60.96841604448855},{"cpu_ms":73.10499999999998,"wall_ms":76.48744690231979},{"cpu_ms":84.30300000000024,"wall_ms":89.7358269430697},{"cpu_ms":83.53999999999928,"wall_ms":87.98586903139949},{"cpu_ms":54.11999999999928,"wall_ms":58.47139097750187},{"cpu_ms":82.62999999999998,"wall_ms":91.06930787675083},{"cpu_ms":49.27300000000123,"wall_ms":53.50666609592736},{"cpu_ms":47.64699999999955,"wall_ms":53.90225606970489},{"cpu_ms":51.96499999999915,"wall_ms":56.24069506302476},{"cpu_ms":61.977000000000615,"wall_ms":67.32266582548618},{"cpu_ms":49.81900000000117,"wall_ms":55.11460010893643},{"cpu_ms":52.42699999999978,"wall_ms":54.753256030380726},{"cpu_ms":46.944999999999126,"wall_ms":55.246306816115975},{"cpu_ms":81.91099999999985,"wall_ms":89.2859569285065},{"cpu_ms":48.99999999999949,"wall_ms":55.36075099371374},{"cpu_ms":84.28699999999978,"wall_ms":88.69434194639325},{"cpu_ms":49.44599999999966,"wall_ms":55.75373489409685},{"cpu_ms":85.09300000000053,"wall_ms":94.05528684146702},{"cpu_ms":46.47599999999841,"wall_ms":54.70942403189838},{"cpu_ms":51.42799999999781,"wall_ms":54.75432798266411},{"cpu_ms":63.788999999999874,"wall_ms":70.06033090874553},{"cpu_ms":68.2779999999994,"wall_ms":74.63109004311264},{"cpu_ms":47.59200000000163,"wall_ms":53.824240108951926},{"cpu_ms":51.71499999999796,"wall_ms":54.08127000555396},{"cpu_ms":48.10200000000009,"wall_ms":53.400918841362},{"cpu_ms":48.84500000000003,"wall_ms":53.20464284159243},{"cpu_ms":47.916000000000736,"wall_ms":53.18996496498585},{"cpu_ms":46.043000000000944,"wall_ms":54.35510189272463},{"cpu_ms":48.46900000000076,"wall_ms":55.731833912432194},{"cpu_ms":46.98199999999986,"wall_ms":53.27305989339948},{"cpu_ms":46.0690000000028,"wall_ms":53.38537087664008},{"cpu_ms":47.51800000000017,"wall_ms":53.76133299432695},{"cpu_ms":49.49099999999973,"wall_ms":53.74601390212774},{"cpu_ms":48.00100000000285,"wall_ms":53.29624400474131},{"cpu_ms":49.68600000000123,"wall_ms":53.01748286001384},{"cpu_ms":48.821000000000225,"wall_ms":53.0935050919652},{"cpu_ms":45.20800000000236,"wall_ms":53.53631195612252},{"cpu_ms":50.62099999999958,"wall_ms":52.93651204556227},{"cpu_ms":50.01400000000089,"wall_ms":52.30148509144783},{"cpu_ms":53.212000000002035,"wall_ms":55.5180839728564},{"cpu_ms":46.95900000000108,"wall_ms":53.239366970956326},{"cpu_ms":47.96500000000137,"wall_ms":53.24769299477339},{"cpu_ms":58.28699999999998,"wall_ms":62.61691590771079},{"cpu_ms":81.14600000000038,"wall_ms":89.82157800346613},{"cpu_ms":84.02500000000046,"wall_ms":91.07196284458041},{"cpu_ms":79.8729999999992,"wall_ms":89.25107191316783},{"cpu_ms":88.7670000000007,"wall_ms":109.70028303563595},{"cpu_ms":72.35900000000228,"wall_ms":75.76809404417872},{"cpu_ms":67.58100000000056,"wall_ms":72.40525097586215},{"cpu_ms":55.66699999999969,"wall_ms":62.3742719180882},{"cpu_ms":65.65399999999855,"wall_ms":78.0257920268923},{"cpu_ms":80.76900000000009,"wall_ms":86.41628595069051},{"cpu_ms":77.22199999999901,"wall_ms":82.63084501959383}],"after_median_cpu_ms":49.99400000000054,"before_control":[{"cpu_ms":46.337999999999994,"wall_ms":53.712219931185246},{"cpu_ms":57.166999999999746,"wall_ms":64.53426694497466},{"cpu_ms":58.536999999999836,"wall_ms":66.846125992015},{"cpu_ms":49.96800000000023,"wall_ms":53.25259198434651},{"cpu_ms":49.9609999999997,"wall_ms":53.19109698757529},{"cpu_ms":46.927999999999855,"wall_ms":53.20449196733534},{"cpu_ms":46.537000000000276,"wall_ms":53.82175510749221},{"cpu_ms":46.54800000000003,"wall_ms":52.91982111521065},{"cpu_ms":45.47999999999996,"wall_ms":52.84320702776313},{"cpu_ms":46.1339999999999,"wall_ms":53.516865940764546},{"cpu_ms":48.39599999999989,"wall_ms":53.6630789283663},{"cpu_ms":50.85600000000001,"wall_ms":54.15293388068676},{"cpu_ms":50.530000000000186,"wall_ms":53.8272550329566},{"cpu_ms":47.18,"wall_ms":53.375912830233574},{"cpu_ms":47.91599999999985,"wall_ms":53.190164966508746},{"cpu_ms":47.80099999999976,"wall_ms":53.06708603166044},{"cpu_ms":45.93800000000048,"wall_ms":52.21363087184727},{"cpu_ms":48.91800000000046,"wall_ms":53.1971980817616},{"cpu_ms":49.08499999999982,"wall_ms":53.379358956590295},{"cpu_ms":49.578999999999596,"wall_ms":52.89892083965242},{"cpu_ms":50.22299999999991,"wall_ms":53.52634307928383},{"cpu_ms":49.913000000000096,"wall_ms":54.24016714096069},{"cpu_ms":48.32700000000045,"wall_ms":52.65853204764426},{"cpu_ms":51.4070000000002,"wall_ms":55.74166192673147},{"cpu_ms":47.74399999999979,"wall_ms":51.996375899761915},{"cpu_ms":48.17400000000038,"wall_ms":53.46378684043884},{"cpu_ms":48.41600000000046,"wall_ms":52.72399494424462},{"cpu_ms":42.87700000000072,"wall_ms":52.122700959444046},{"cpu_ms":50.27700000000035,"wall_ms":52.57946508936584},{"cpu_ms":49.76599999999998,"wall_ms":53.024034947156906},{"cpu_ms":46.87000000000019,"wall_ms":54.18801889754832},{"cpu_ms":48.52399999999957,"wall_ms":53.790426114574075},{"cpu_ms":47.35099999999992,"wall_ms":53.610019851475954},{"cpu_ms":46.340999999999966,"wall_ms":53.66839887574315},{"cpu_ms":48.89099999999935,"wall_ms":52.15433007106185},{"cpu_ms":86.28700000000045,"wall_ms":124.30630787275732},{"cpu_ms":83.98799999999973,"wall_ms":100.64132907427847},{"cpu_ms":82.37500000000075,"wall_ms":132.74935097433627},{"cpu_ms":83.6629999999996,"wall_ms":101.42589500173926},{"cpu_ms":82.51899999999956,"wall_ms":132.7436009887606},{"cpu_ms":86.43900000000038,"wall_ms":164.22809707000852},{"cpu_ms":84.11200000000107,"wall_ms":156.79766586981714},{"cpu_ms":77.23600000000097,"wall_ms":119.56975003704429},{"cpu_ms":82.65399999999978,"wall_ms":89.03637994080782},{"cpu_ms":70.74200000000097,"wall_ms":77.08793878555298},{"cpu_ms":73.16600000000051,"wall_ms":75.51435986533761},{"cpu_ms":50.12100000000075,"wall_ms":55.375819094479084},{"cpu_ms":59.65499999999935,"wall_ms":67.0054939109832},{"cpu_ms":53.136000000000294,"wall_ms":63.07550217024982},{"cpu_ms":83.94599999999919,"wall_ms":90.13869892805815},{"cpu_ms":67.99399999999878,"wall_ms":74.35745908878744},{"cpu_ms":70.56200000000068,"wall_ms":77.88554998114705},{"cpu_ms":57.97399999999975,"wall_ms":69.54041006974876},{"cpu_ms":46.298999999999424,"wall_ms":53.48837305791676},{"cpu_ms":55.61300000000102,"wall_ms":59.99158206395805},{"cpu_ms":50.9409999999999,"wall_ms":54.230378940701485},{"cpu_ms":70.88699999999903,"wall_ms":79.15766583755612},{"cpu_ms":48.57200000000006,"wall_ms":53.94454509951174},{"cpu_ms":49.60799999999921,"wall_ms":55.86928082630038},{"cpu_ms":81.09199999999994,"wall_ms":87.50794501975179},{"cpu_ms":82.6980000000006,"wall_ms":88.07602594606578},{"cpu_ms":63.57200000000063,"wall_ms":104.51687895692885},{"cpu_ms":82.74600000000021,"wall_ms":87.11739582940936},{"cpu_ms":79.4979999999974,"wall_ms":85.905501851812},{"cpu_ms":79.78000000000307,"wall_ms":87.88657281547785},{"cpu_ms":54.884000000001265,"wall_ms":60.23539719171822},{"cpu_ms":52.08900000000227,"wall_ms":56.3923679292202},{"cpu_ms":65.84499999999949,"wall_ms":71.15525589324534},{"cpu_ms":64.13099999999972,"wall_ms":70.06147294305265},{"cpu_ms":48.138999999999044,"wall_ms":55.52527797408402},{"cpu_ms":49.9609999999997,"wall_ms":54.23505790531635},{"cpu_ms":50.64399999999836,"wall_ms":52.95156012289226},{"cpu_ms":49.830999999997516,"wall_ms":53.117399802431464},{"cpu_ms":51.130000000000564,"wall_ms":53.44649404287338},{"cpu_ms":48.484999999999445,"wall_ms":53.714223904535174},{"cpu_ms":49.721999999999156,"wall_ms":54.03563496656716},{"cpu_ms":47.70199999999747,"wall_ms":52.96807992272079},{"cpu_ms":48.0620000000016,"wall_ms":53.4181019756943},{"cpu_ms":48.632999999998816,"wall_ms":53.915479918941855},{"cpu_ms":47.47799999999813,"wall_ms":53.836493054404855},{"cpu_ms":46.78400000000238,"wall_ms":54.03635697439313},{"cpu_ms":44.22299999999879,"wall_ms":53.552351891994476},{"cpu_ms":44.751999999999015,"wall_ms":52.99349804408848},{"cpu_ms":51.719999999999544,"wall_ms":59.1632837895304},{"cpu_ms":48.138999999999044,"wall_ms":52.43072705343366},{"cpu_ms":45.34800000000061,"wall_ms":52.713494980707765},{"cpu_ms":48.300999999998595,"wall_ms":52.63130203820765},{"cpu_ms":47.71299999999812,"wall_ms":53.09040914289653},{"cpu_ms":50.03900000000172,"wall_ms":53.4014708828181},{"cpu_ms":47.963000000002864,"wall_ms":53.25907398946583},{"cpu_ms":75.87999999999795,"wall_ms":86.67644206434488},{"cpu_ms":79.98800000000017,"wall_ms":94.96436500921845},{"cpu_ms":64.95400000000018,"wall_ms":78.09973903931677},{"cpu_ms":81.8879999999993,"wall_ms":86.6833960171789},{"cpu_ms":76.00300000000004,"wall_ms":83.47937907092273},{"cpu_ms":69.05200000000278,"wall_ms":75.45763393864036},{"cpu_ms":79.89200000000096,"wall_ms":85.26756009086967},{"cpu_ms":79.59800000000072,"wall_ms":84.9644539412111},{"cpu_ms":77.81699999999958,"wall_ms":90.84846521727741},{"cpu_ms":65.56600000000046,"wall_ms":67.94592086225748}],"before_control_median_cpu_ms":50.172000000000324,"after_control":[{"cpu_ms":51.26000000000008,"wall_ms":61.65898498147726},{"cpu_ms":54.39499999999997,"wall_ms":61.667040921747684},{"cpu_ms":50.36300000000038,"wall_ms":55.74025912210345},{"cpu_ms":43.03199999999974,"wall_ms":54.40743896178901},{"cpu_ms":45.31600000000013,"wall_ms":52.65570594929159},{"cpu_ms":43.95199999999999,"wall_ms":53.24211297556758},{"cpu_ms":44.44399999999993,"wall_ms":52.841283148154616},{"cpu_ms":47.918999999999826,"wall_ms":53.311151918023825},{"cpu_ms":48.386000000000266,"wall_ms":53.62080899067223},{"cpu_ms":47.44700000000002,"wall_ms":52.68297786824405},{"cpu_ms":48.042000000000144,"wall_ms":53.32865403033793},{"cpu_ms":47.22099999999996,"wall_ms":53.578080143779516},{"cpu_ms":49.86099999999993,"wall_ms":53.15097188577056},{"cpu_ms":51.18299999999998,"wall_ms":53.484323900192976},{"cpu_ms":43.82000000000019,"wall_ms":53.06331790052354},{"cpu_ms":47.78299999999991,"wall_ms":53.180136950686574},{"cpu_ms":48.59999999999953,"wall_ms":52.84998007118702},{"cpu_ms":49.546999999999564,"wall_ms":53.945014951750636},{"cpu_ms":48.89400000000066,"wall_ms":54.348648991435766},{"cpu_ms":48.75700000000016,"wall_ms":53.02673997357488},{"cpu_ms":48.07000000000006,"wall_ms":57.42103885859251},{"cpu_ms":47.34800000000039,"wall_ms":53.708984050899744},{"cpu_ms":48.61199999999943,"wall_ms":52.86038015037775},{"cpu_ms":46.66199999999954,"wall_ms":52.8939520008862},{"cpu_ms":49.722000000000044,"wall_ms":53.96798811852932},{"cpu_ms":47.896999999999856,"wall_ms":53.14373900182545},{"cpu_ms":49.3509999999997,"wall_ms":54.55662799067795},{"cpu_ms":49.00799999999972,"wall_ms":53.325287997722626},{"cpu_ms":48.365000000000435,"wall_ms":53.68069210089743},{"cpu_ms":45.90399999999928,"wall_ms":53.15916799008846},{"cpu_ms":48.3159999999998,"wall_ms":52.6176851708442},{"cpu_ms":47.884999999999955,"wall_ms":52.1440408192575},{"cpu_ms":44.86699999999999,"wall_ms":54.11660601384938},{"cpu_ms":47.37100000000005,"wall_ms":52.68381000496447},{"cpu_ms":48.96500000000081,"wall_ms":53.218268090859056},{"cpu_ms":76.4680000000002,"wall_ms":95.69279500283301},{"cpu_ms":80.79099999999961,"wall_ms":108.01918199285865},{"cpu_ms":86.64100000000019,"wall_ms":100.85318493656814},{"cpu_ms":85.1980000000001,"wall_ms":145.14135080389678},{"cpu_ms":87.35099999999996,"wall_ms":110.65259180031717},{"cpu_ms":82.30499999999985,"wall_ms":116.33337405510247},{"cpu_ms":81.50599999999919,"wall_ms":164.34662812389433},{"cpu_ms":86.33000000000024,"wall_ms":123.9925129339099},{"cpu_ms":78.56400000000008,"wall_ms":89.54365784302354},{"cpu_ms":46.94299999999885,"wall_ms":55.48233701847494},{"cpu_ms":75.43399999999956,"wall_ms":84.85709293745458},{"cpu_ms":67.35999999999898,"wall_ms":86.70608699321747},{"cpu_ms":77.95800000000064,"wall_ms":83.33896682597697},{"cpu_ms":54.96400000000001,"wall_ms":59.29645290598273},{"cpu_ms":84.42800000000084,"wall_ms":89.84227594919503},{"cpu_ms":81.67200000000108,"wall_ms":88.11657200567424},{"cpu_ms":56.47399999999969,"wall_ms":74.66535409912467},{"cpu_ms":50.42500000000061,"wall_ms":57.67946201376617},{"cpu_ms":48.38599999999893,"wall_ms":53.6356579978019},{"cpu_ms":50.801999999999126,"wall_ms":55.10519305244088},{"cpu_ms":51.35800000000046,"wall_ms":55.70479296147823},{"cpu_ms":48.62800000000078,"wall_ms":53.89770702458918},{"cpu_ms":48.73699999999914,"wall_ms":54.015266010537744},{"cpu_ms":47.66000000000048,"wall_ms":54.93015586398542},{"cpu_ms":51.322000000000756,"wall_ms":55.637697922065854},{"cpu_ms":81.9939999999999,"wall_ms":88.37126707658172},{"cpu_ms":59.35500000000005,"wall_ms":65.75682200491428},{"cpu_ms":83.03500000000064,"wall_ms":89.41540913656354},{"cpu_ms":81.69000000000182,"wall_ms":88.10766600072384},{"cpu_ms":60.026999999998054,"wall_ms":84.46903782896698},{"cpu_ms":50.69400000000002,"wall_ms":55.06802396848798},{"cpu_ms":49.69999999999786,"wall_ms":58.05048602633178},{"cpu_ms":64.3790000000024,"wall_ms":71.71620498411357},{"cpu_ms":59.11700000000053,"wall_ms":66.46796013228595},{"cpu_ms":49.1899999999994,"wall_ms":53.491679020226},{"cpu_ms":50.55499999999924,"wall_ms":53.81859000772238},{"cpu_ms":49.97900000000044,"wall_ms":53.25769097544253},{"cpu_ms":49.81400000000136,"wall_ms":53.09585016220808},{"cpu_ms":46.07599999999934,"wall_ms":53.37918899022043},{"cpu_ms":48.893000000003184,"wall_ms":54.168593138456345},{"cpu_ms":48.82200000000125,"wall_ms":53.10005694627762},{"cpu_ms":47.33200000000082,"wall_ms":53.53851709514856},{"cpu_ms":50.06999999999806,"wall_ms":53.386643063277006},{"cpu_ms":46.82599999999937,"wall_ms":53.08805499225855},{"cpu_ms":51.72100000000057,"wall_ms":55.06735318340361},{"cpu_ms":46.86299999999832,"wall_ms":53.147034952417016},{"cpu_ms":47.595000000001164,"wall_ms":52.96267103403807},{"cpu_ms":46.84999999999917,"wall_ms":53.12423198483884},{"cpu_ms":48.29300000000103,"wall_ms":53.62888611853123},{"cpu_ms":45.127000000000805,"wall_ms":52.43562604300678},{"cpu_ms":48.905999999998784,"wall_ms":52.1873920224607},{"cpu_ms":47.83600000000021,"wall_ms":53.10786212794483},{"cpu_ms":45.536999999999495,"wall_ms":52.762386854738},{"cpu_ms":46.96999999999818,"wall_ms":53.24968695640564},{"cpu_ms":48.95099999999886,"wall_ms":53.231884026899934},{"cpu_ms":57.947999999999666,"wall_ms":68.62381706014276},{"cpu_ms":84.10699999999949,"wall_ms":91.25102800317109},{"cpu_ms":78.39600000000146,"wall_ms":87.07332378253341},{"cpu_ms":86.60499999999871,"wall_ms":153.43251009471714},{"cpu_ms":79.37400000000139,"wall_ms":85.76915692538023},{"cpu_ms":67.56599999999935,"wall_ms":74.01653216220438},{"cpu_ms":80.08799999999994,"wall_ms":86.42184711061418},{"cpu_ms":84.03799999999961,"wall_ms":87.51376601867378},{"cpu_ms":81.44499999999866,"wall_ms":86.22633083723485},{"cpu_ms":65.99500000000091,"wall_ms":69.34383301995695}],"after_control_median_cpu_ms":49.623499999998714} +] diff --git a/benchmarks/regexp-construction/final-loaded-control-samples.json b/benchmarks/regexp-construction/final-loaded-control-samples.json new file mode 100644 index 0000000000..f85ea7d682 --- /dev/null +++ b/benchmarks/regexp-construction/final-loaded-control-samples.json @@ -0,0 +1,4 @@ +[ +{"name":"map_1m","node_exit":0,"before":[{"cpu_ms":193.18299999999942,"wall_ms":204.53866897150874},{"cpu_ms":200.91400000000004,"wall_ms":207.2437540628016},{"cpu_ms":193.92299999999983,"wall_ms":201.2276710011065},{"cpu_ms":191.7749999999998,"wall_ms":198.08036810718477},{"cpu_ms":178.87500000000057,"wall_ms":192.174619063735},{"cpu_ms":186.47100000000006,"wall_ms":193.81133886054158},{"cpu_ms":202.89000000000001,"wall_ms":211.20985690504313},{"cpu_ms":185.02699999999982,"wall_ms":192.3206818755716},{"cpu_ms":183.04999999999973,"wall_ms":191.3583839777857},{"cpu_ms":187.79099999999892,"wall_ms":198.08400399051607},{"cpu_ms":181.40200000000027,"wall_ms":191.72381609678268},{"cpu_ms":191.96600000000075,"wall_ms":200.31345007009804},{"cpu_ms":197.52499999999884,"wall_ms":204.86308401450515},{"cpu_ms":184.78999999999957,"wall_ms":193.08939785696566},{"cpu_ms":194.76300000000003,"wall_ms":206.08904398977757},{"cpu_ms":203.18299999999923,"wall_ms":209.52767110429704},{"cpu_ms":231.07800000000012,"wall_ms":237.5130350701511},{"cpu_ms":208.596,"wall_ms":216.93770005367696},{"cpu_ms":213.75799999999856,"wall_ms":223.07961899787188},{"cpu_ms":215.8869999999986,"wall_ms":223.22681290097535},{"cpu_ms":229.4780000000003,"wall_ms":233.86769113130867},{"cpu_ms":310.57500000000005,"wall_ms":320.0655151158571},{"cpu_ms":234.05199999999837,"wall_ms":242.39831394515932},{"cpu_ms":261.57500000000056,"wall_ms":269.98522388748825},{"cpu_ms":286.7939999999969,"wall_ms":294.1234379541129},{"cpu_ms":309.27300000000105,"wall_ms":317.66561791300774},{"cpu_ms":290.1810000000005,"wall_ms":318.747861078009},{"cpu_ms":332.7649999999984,"wall_ms":352.0220499485731},{"cpu_ms":284.01099999999957,"wall_ms":297.43210109882057},{"cpu_ms":290.2740000000001,"wall_ms":303.274940000847},{"cpu_ms":283.0849999999998,"wall_ms":292.97233396209776},{"cpu_ms":272.673000000001,"wall_ms":282.0445701945573},{"cpu_ms":292.60099999999767,"wall_ms":303.48394997417927},{"cpu_ms":275.3719999999973,"wall_ms":285.033144056797},{"cpu_ms":277.96599999999927,"wall_ms":285.87943594902754},{"cpu_ms":283.88900000000206,"wall_ms":291.3266259711236},{"cpu_ms":275.0479999999982,"wall_ms":284.3932881951332},{"cpu_ms":273.52099999999524,"wall_ms":285.88054701685905},{"cpu_ms":272.42999999999995,"wall_ms":283.88033783994615},{"cpu_ms":267.21799999999973,"wall_ms":295.99817586131394},{"cpu_ms":270.17399999999725,"wall_ms":287.8410399425775},{"cpu_ms":288.66899999999873,"wall_ms":294.55981589853764},{"cpu_ms":291.13900000000115,"wall_ms":299.5828939601779},{"cpu_ms":278.08100000000024,"wall_ms":301.2732130009681},{"cpu_ms":281.55399999999986,"wall_ms":286.9530578609556},{"cpu_ms":288.7020000000007,"wall_ms":303.13793709501624},{"cpu_ms":277.3459999999943,"wall_ms":288.9042340684682},{"cpu_ms":291.5440000000018,"wall_ms":302.1147339604795},{"cpu_ms":298.3430000000027,"wall_ms":327.9960248619318},{"cpu_ms":284.09100000000365,"wall_ms":295.9493000525981},{"cpu_ms":290.3480000000016,"wall_ms":311.7631049826741},{"cpu_ms":286.98999999999586,"wall_ms":304.5488109346479},{"cpu_ms":296.3979999999964,"wall_ms":309.30051184259355},{"cpu_ms":287.3299999999972,"wall_ms":418.6063199304044},{"cpu_ms":269.77899999999977,"wall_ms":284.3071089591831},{"cpu_ms":268.70300000000213,"wall_ms":286.63770388811827},{"cpu_ms":271.19499999999874,"wall_ms":347.8902520146221},{"cpu_ms":283.83699999999834,"wall_ms":301.56529298983514},{"cpu_ms":296.65899999999823,"wall_ms":817.082640947774},{"cpu_ms":286.8229999999983,"wall_ms":354.5227968133986},{"cpu_ms":298.5790000000037,"wall_ms":307.0776229724288},{"cpu_ms":281.9410000000033,"wall_ms":291.32768721319735},{"cpu_ms":299.83400000000415,"wall_ms":308.7230978999287},{"cpu_ms":319.6140000000014,"wall_ms":422.4830688908696},{"cpu_ms":325.49000000000206,"wall_ms":463.69111305102706},{"cpu_ms":337.1190000000013,"wall_ms":451.16892899386585},{"cpu_ms":305.3900000000027,"wall_ms":315.0434677954763},{"cpu_ms":325.25400000000104,"wall_ms":436.9061409961432},{"cpu_ms":307.89899999999193,"wall_ms":378.24491620995104},{"cpu_ms":326.469000000003,"wall_ms":356.06745816767216},{"cpu_ms":246.7929999999967,"wall_ms":258.7204820010811},{"cpu_ms":304.66599999999744,"wall_ms":312.16795090585947},{"cpu_ms":302.05999999999733,"wall_ms":308.8154250290245},{"cpu_ms":294.825000000003,"wall_ms":307.23348818719387},{"cpu_ms":286.34700000000635,"wall_ms":296.2737940251827},{"cpu_ms":309.6440000000058,"wall_ms":322.3325770813972},{"cpu_ms":302.91400000000124,"wall_ms":313.37513495236635},{"cpu_ms":295.0479999999942,"wall_ms":307.9886380583048},{"cpu_ms":291.6850000000011,"wall_ms":308.79818089306355},{"cpu_ms":298.73800000000017,"wall_ms":309.22551918774843},{"cpu_ms":298.86199999999974,"wall_ms":312.22798908129334},{"cpu_ms":294.8389999999961,"wall_ms":303.2423658296466},{"cpu_ms":267.5300000000078,"wall_ms":279.8951519653201},{"cpu_ms":237.28900000000408,"wall_ms":248.6161938868463},{"cpu_ms":265.19900000000973,"wall_ms":269.6222821250558},{"cpu_ms":269.55300000000193,"wall_ms":275.20825108513236},{"cpu_ms":218.6749999999904,"wall_ms":228.01182512193918},{"cpu_ms":228.30500000000598,"wall_ms":233.65383897908032},{"cpu_ms":229.62400000000116,"wall_ms":240.02814199775457},{"cpu_ms":291.1890000000028,"wall_ms":299.6281881351024},{"cpu_ms":290.82300000000316,"wall_ms":298.2601011171937},{"cpu_ms":294.0329999999989,"wall_ms":302.45506088249385},{"cpu_ms":294.49800000000437,"wall_ms":301.96040705777705},{"cpu_ms":309.4999999999999,"wall_ms":325.89965616352856},{"cpu_ms":291.049000000001,"wall_ms":301.65183800272644},{"cpu_ms":239.29699999999343,"wall_ms":246.26479600556195},{"cpu_ms":306.8629999999928,"wall_ms":313.80191491916776},{"cpu_ms":305.70400000000575,"wall_ms":316.0965330898762},{"cpu_ms":299.62000000000444,"wall_ms":309.0298061724752},{"cpu_ms":286.49899999999207,"wall_ms":297.8686459828168}],"before_median_cpu_ms":283.95000000000084,"after":[{"cpu_ms":195.12700000000027,"wall_ms":202.44501600973308},{"cpu_ms":196.58499999999978,"wall_ms":201.92700787447393},{"cpu_ms":189.98800000000048,"wall_ms":198.32026702351868},{"cpu_ms":192.99399999999966,"wall_ms":199.30925406515598},{"cpu_ms":187.7419999999992,"wall_ms":193.0384838487953},{"cpu_ms":184.70399999999998,"wall_ms":192.00111599639058},{"cpu_ms":189.80200000000025,"wall_ms":197.11580406874418},{"cpu_ms":188.34100000000032,"wall_ms":195.67944994196296},{"cpu_ms":181.43900000000102,"wall_ms":191.7756930924952},{"cpu_ms":186.73700000000082,"wall_ms":195.10419410653412},{"cpu_ms":186.51799999999952,"wall_ms":193.815384991467},{"cpu_ms":184.25899999999905,"wall_ms":194.525663042441},{"cpu_ms":187.85500000000076,"wall_ms":197.14413699693978},{"cpu_ms":189.09900000000056,"wall_ms":197.36539013683796},{"cpu_ms":207.63400000000053,"wall_ms":212.95265317894518},{"cpu_ms":205.25400000000005,"wall_ms":213.5927288327366},{"cpu_ms":212.87200000000084,"wall_ms":222.2089220304042},{"cpu_ms":235.04199999999997,"wall_ms":243.37655398994684},{"cpu_ms":223.94399999999948,"wall_ms":236.8928559590131},{"cpu_ms":208.05600000000268,"wall_ms":219.50360503979027},{"cpu_ms":231.2359999999991,"wall_ms":238.59693994745612},{"cpu_ms":243.6460000000018,"wall_ms":249.98136796057224},{"cpu_ms":305.53300000000047,"wall_ms":314.8477729409933},{"cpu_ms":297.7209999999992,"wall_ms":308.0415010917932},{"cpu_ms":301.0800000000024,"wall_ms":310.417041182518},{"cpu_ms":301.03799999999836,"wall_ms":312.59320699609816},{"cpu_ms":300.146999999999,"wall_ms":321.6190468519926},{"cpu_ms":295.42900000000213,"wall_ms":313.5105010587722},{"cpu_ms":290.5869999999986,"wall_ms":304.0357318241149},{"cpu_ms":288.6260000000007,"wall_ms":301.31281283684075},{"cpu_ms":280.504999999998,"wall_ms":294.3208741489798},{"cpu_ms":281.77700000000175,"wall_ms":289.7599400021136},{"cpu_ms":313.51899999999944,"wall_ms":318.8966359011829},{"cpu_ms":274.57600000000326,"wall_ms":284.97751988470554},{"cpu_ms":278.54500000000115,"wall_ms":288.49339205771685},{"cpu_ms":293.7999999999974,"wall_ms":313.487394945696},{"cpu_ms":286.4460000000051,"wall_ms":293.8071119133383},{"cpu_ms":280.142000000005,"wall_ms":290.51690199412405},{"cpu_ms":269.1039999999987,"wall_ms":281.5184339415282},{"cpu_ms":272.02199999999976,"wall_ms":288.07072900235653},{"cpu_ms":278.534999999998,"wall_ms":287.5255299732089},{"cpu_ms":285.5440000000016,"wall_ms":295.4418750014156},{"cpu_ms":293.96500000000003,"wall_ms":304.40158979035914},{"cpu_ms":282.24300000000113,"wall_ms":293.6591128818691},{"cpu_ms":276.4489999999995,"wall_ms":290.1713140308857},{"cpu_ms":283.10900000000316,"wall_ms":296.51622218079865},{"cpu_ms":276.0800000000003,"wall_ms":291.54902789741755},{"cpu_ms":281.8070000000006,"wall_ms":290.58803082443774},{"cpu_ms":291.1499999999947,"wall_ms":299.99440000392497},{"cpu_ms":282.902,"wall_ms":288.51834195666015},{"cpu_ms":281.8159999999992,"wall_ms":301.78508604876697},{"cpu_ms":294.4469999999981,"wall_ms":469.59305298514664},{"cpu_ms":306.16300000000507,"wall_ms":320.0727850198746},{"cpu_ms":280.5930000000032,"wall_ms":302.3620240855962},{"cpu_ms":279.99899999999656,"wall_ms":294.9609609786421},{"cpu_ms":274.2319999999978,"wall_ms":280.65226785838604},{"cpu_ms":269.207999999999,"wall_ms":287.6038099639118},{"cpu_ms":285.854999999998,"wall_ms":300.8354580961168},{"cpu_ms":308.6410000000015,"wall_ms":363.7851779349148},{"cpu_ms":297.80600000000135,"wall_ms":338.23304204270244},{"cpu_ms":284.1499999999968,"wall_ms":293.62228605896235},{"cpu_ms":285.90400000000216,"wall_ms":302.30372003279626},{"cpu_ms":318.6269999999922,"wall_ms":328.03909899666905},{"cpu_ms":328.98099999999886,"wall_ms":430.7291798759252},{"cpu_ms":322.6449999999943,"wall_ms":745.9604199975729},{"cpu_ms":307.2689999999909,"wall_ms":331.6585209686309},{"cpu_ms":306.82899999999336,"wall_ms":407.52299688756466},{"cpu_ms":325.1690000000025,"wall_ms":371.617240132764},{"cpu_ms":302.20400000000325,"wall_ms":313.33514489233494},{"cpu_ms":302.7019999999965,"wall_ms":308.6654460057616},{"cpu_ms":304.9440000000061,"wall_ms":312.3719429131597},{"cpu_ms":300.74399999999457,"wall_ms":309.8289498593658},{"cpu_ms":291.5310000000062,"wall_ms":299.9672591686249},{"cpu_ms":312.7330000000086,"wall_ms":323.6725020688027},{"cpu_ms":296.6119999999961,"wall_ms":305.0812149886042},{"cpu_ms":289.78099999999074,"wall_ms":330.88737493380904},{"cpu_ms":305.8110000000056,"wall_ms":314.81269211508334},{"cpu_ms":302.8410000000008,"wall_ms":312.55077407695353},{"cpu_ms":296.1209999999994,"wall_ms":303.48432902246714},{"cpu_ms":298.6879999999985,"wall_ms":308.1921979319304},{"cpu_ms":305.66400000000726,"wall_ms":315.0956789031625},{"cpu_ms":295.51100000000474,"wall_ms":304.9514200538397},{"cpu_ms":272.1289999999925,"wall_ms":280.477138934657},{"cpu_ms":218.7359999999927,"wall_ms":226.07577685266733},{"cpu_ms":294.7329999999937,"wall_ms":301.2484088540077},{"cpu_ms":285.8680000000078,"wall_ms":292.3130688723177},{"cpu_ms":218.67000000000303,"wall_ms":227.00535389594734},{"cpu_ms":218.02799999998967,"wall_ms":224.37489707954228},{"cpu_ms":224.50199999998688,"wall_ms":232.8982970211655},{"cpu_ms":292.9230000000018,"wall_ms":297.36188310198486},{"cpu_ms":291.53800000000274,"wall_ms":297.98782197758555},{"cpu_ms":286.3540000000029,"wall_ms":296.7544668354094},{"cpu_ms":297.8859999999912,"wall_ms":308.3258019760251},{"cpu_ms":288.369000000003,"wall_ms":303.8236480206251},{"cpu_ms":313.8759999999934,"wall_ms":345.2410828322172},{"cpu_ms":307.15400000001125,"wall_ms":316.614612005651},{"cpu_ms":310.6550000000112,"wall_ms":321.64148706942797},{"cpu_ms":302.89100000000246,"wall_ms":314.34665294364095},{"cpu_ms":300.83199999999977,"wall_ms":308.23715403676033},{"cpu_ms":301.0620000000017,"wall_ms":310.4917269665748}],"after_median_cpu_ms":285.88600000000497,"before_control":[{"cpu_ms":187.6420000000003,"wall_ms":198.94932303577662},{"cpu_ms":188.2640000000002,"wall_ms":198.5680900979787},{"cpu_ms":186.1480000000002,"wall_ms":193.48281482234597},{"cpu_ms":190.00200000000066,"wall_ms":196.32548792287707},{"cpu_ms":187.51799999999986,"wall_ms":194.8083220049739},{"cpu_ms":202.84800000000035,"wall_ms":208.1738628912717},{"cpu_ms":197.82600000000005,"wall_ms":207.13264704681933},{"cpu_ms":181.10399999999947,"wall_ms":192.41886492818594},{"cpu_ms":185.49299999999926,"wall_ms":194.78131015785038},{"cpu_ms":203.57599999999996,"wall_ms":212.92174607515335},{"cpu_ms":182.048,"wall_ms":191.35361397638917},{"cpu_ms":182.96899999999994,"wall_ms":192.26108002476394},{"cpu_ms":192.71700000000004,"wall_ms":201.01267704740167},{"cpu_ms":187.53799999999998,"wall_ms":195.8804950118065},{"cpu_ms":200.1589999999993,"wall_ms":211.4727960433811},{"cpu_ms":210.45200000000008,"wall_ms":216.80136490613222},{"cpu_ms":283.1060000000001,"wall_ms":292.5062489230186},{"cpu_ms":269.3159999999999,"wall_ms":280.81709216348827},{"cpu_ms":203.94300000000243,"wall_ms":209.276189096272},{"cpu_ms":227.00200000000237,"wall_ms":235.34140619449317},{"cpu_ms":229.5999999999978,"wall_ms":237.94589401222765},{"cpu_ms":263.3119999999991,"wall_ms":271.70484885573387},{"cpu_ms":232.58900000000082,"wall_ms":246.02456204593182},{"cpu_ms":294.96899999999823,"wall_ms":306.40034400857985},{"cpu_ms":297.75300000000016,"wall_ms":308.55168495327234},{"cpu_ms":316.3489999999989,"wall_ms":322.6894380059093},{"cpu_ms":297.29400000000084,"wall_ms":306.70385085977614},{"cpu_ms":302.9180000000018,"wall_ms":336.39786788262427},{"cpu_ms":288.0520000000004,"wall_ms":296.6855049598962},{"cpu_ms":296.12300000000147,"wall_ms":310.88694697245955},{"cpu_ms":286.8790000000025,"wall_ms":300.50063389353454},{"cpu_ms":291.12500000000097,"wall_ms":298.10545803047717},{"cpu_ms":286.4410000000035,"wall_ms":297.85816511139274},{"cpu_ms":281.25299999999953,"wall_ms":297.10873402655125},{"cpu_ms":275.43099999999754,"wall_ms":286.26586799509823},{"cpu_ms":280.28499999999923,"wall_ms":293.1100989226252},{"cpu_ms":273.41599999999744,"wall_ms":285.3333039674908},{"cpu_ms":283.6810000000014,"wall_ms":292.0641840901226},{"cpu_ms":272.2619999999978,"wall_ms":283.17238483577967},{"cpu_ms":288.62500000000324,"wall_ms":296.30973702296615},{"cpu_ms":270.64200000000227,"wall_ms":287.0719418860972},{"cpu_ms":287.5100000000046,"wall_ms":293.4330010320991},{"cpu_ms":289.0519999999981,"wall_ms":295.4272071365267},{"cpu_ms":293.76299999999844,"wall_ms":304.8629420809448},{"cpu_ms":287.4359999999996,"wall_ms":295.3041070140898},{"cpu_ms":251.31300000000323,"wall_ms":258.70309909805655},{"cpu_ms":304.9870000000041,"wall_ms":310.42586802504957},{"cpu_ms":289.05300000000267,"wall_ms":297.55272693000734},{"cpu_ms":314.2429999999976,"wall_ms":460.267576854676},{"cpu_ms":285.0559999999973,"wall_ms":291.6725669056177},{"cpu_ms":291.64300000000054,"wall_ms":397.8075501509011},{"cpu_ms":307.1989999999971,"wall_ms":527.5901579298079},{"cpu_ms":315.4180000000011,"wall_ms":325.2846251707524},{"cpu_ms":285.48400000000385,"wall_ms":309.07102208584547},{"cpu_ms":277.5269999999992,"wall_ms":337.9134370479733},{"cpu_ms":274.5270000000062,"wall_ms":379.8534609377384},{"cpu_ms":291.21699999999606,"wall_ms":321.4114150032401},{"cpu_ms":293.6019999999999,"wall_ms":378.42190405353904},{"cpu_ms":295.14700000000005,"wall_ms":318.5974941588938},{"cpu_ms":306.044,"wall_ms":342.96566690318286},{"cpu_ms":302.52799999999525,"wall_ms":316.20420096442103},{"cpu_ms":284.2469999999935,"wall_ms":297.79420187696815},{"cpu_ms":287.49799999999937,"wall_ms":299.16294990107417},{"cpu_ms":295.2279999999945,"wall_ms":304.87616709433496},{"cpu_ms":319.58500000000356,"wall_ms":407.76291699148715},{"cpu_ms":311.82699999999386,"wall_ms":331.731298007071},{"cpu_ms":308.67800000000045,"wall_ms":317.0667390804738},{"cpu_ms":297.300000000007,"wall_ms":313.730153022334},{"cpu_ms":304.0759999999949,"wall_ms":392.81225204467773},{"cpu_ms":306.1139999999938,"wall_ms":462.17210101895034},{"cpu_ms":292.56200000000376,"wall_ms":302.9514269437641},{"cpu_ms":293.01299999998776,"wall_ms":307.44815105572343},{"cpu_ms":305.80500000000654,"wall_ms":314.1870948020369},{"cpu_ms":298.2939999999985,"wall_ms":311.3772040233016},{"cpu_ms":289.29399999999816,"wall_ms":317.2998409718275},{"cpu_ms":306.69399999999314,"wall_ms":318.64605587907135},{"cpu_ms":291.569999999993,"wall_ms":300.9853099938482},{"cpu_ms":302.81200000000297,"wall_ms":315.5786329880357},{"cpu_ms":289.70999999999947,"wall_ms":302.12963791564107},{"cpu_ms":298.2189999999889,"wall_ms":309.6249441150576},{"cpu_ms":315.9880000000044,"wall_ms":324.40622011199594},{"cpu_ms":294.9950000000001,"wall_ms":303.4611348994076},{"cpu_ms":270.7929999999976,"wall_ms":281.73861699178815},{"cpu_ms":276.59200000000794,"wall_ms":286.04460414499044},{"cpu_ms":279.28499999998735,"wall_ms":290.6348519027233},{"cpu_ms":280.52100000000735,"wall_ms":285.909088794142},{"cpu_ms":225.0670000000099,"wall_ms":233.42675506137311},{"cpu_ms":242.72700000000214,"wall_ms":251.63028482347727},{"cpu_ms":269.09200000000055,"wall_ms":277.2995389532298},{"cpu_ms":284.9860000000035,"wall_ms":296.41255899332464},{"cpu_ms":298.451,"wall_ms":308.75412398017943},{"cpu_ms":293.5380000000123,"wall_ms":304.99122105538845},{"cpu_ms":286.0589999999945,"wall_ms":300.48548011109233},{"cpu_ms":288.32199999999375,"wall_ms":305.7644960936159},{"cpu_ms":302.75500000000477,"wall_ms":419.3557759281248},{"cpu_ms":308.337999999992,"wall_ms":319.8248220141977},{"cpu_ms":294.8350000000062,"wall_ms":305.22924405522645},{"cpu_ms":302.8070000000014,"wall_ms":314.2348339315504},{"cpu_ms":311.4699999999999,"wall_ms":317.89070600643754},{"cpu_ms":294.64799999999514,"wall_ms":305.0941601395607}],"before_control_median_cpu_ms":287.7810000000025,"after_control":[{"cpu_ms":191.86799999999994,"wall_ms":200.21696179173887},{"cpu_ms":192.24299999999994,"wall_ms":200.5202581640333},{"cpu_ms":189.8869999999997,"wall_ms":200.2115109935403},{"cpu_ms":188.75200000000004,"wall_ms":197.06025207415223},{"cpu_ms":190.69700000000012,"wall_ms":197.03623605892062},{"cpu_ms":183.8619999999995,"wall_ms":191.17389898747206},{"cpu_ms":184.666,"wall_ms":193.02129209972918},{"cpu_ms":185.61799999999982,"wall_ms":193.95595812238753},{"cpu_ms":186.01500000000115,"wall_ms":196.35676592588425},{"cpu_ms":189.4929999999988,"wall_ms":197.8523510042578},{"cpu_ms":187.9640000000009,"wall_ms":196.26680691726506},{"cpu_ms":189.7929999999999,"wall_ms":195.09131996892393},{"cpu_ms":187.67900000000105,"wall_ms":196.02632708847523},{"cpu_ms":187.55099999999914,"wall_ms":191.8520350009203},{"cpu_ms":184.47500000000082,"wall_ms":192.80660920776427},{"cpu_ms":202.928,"wall_ms":212.24846597760916},{"cpu_ms":293.9959999999999,"wall_ms":302.3713470902294},{"cpu_ms":269.53799999999893,"wall_ms":279.93823890574276},{"cpu_ms":198.33799999999968,"wall_ms":206.625065067783},{"cpu_ms":215.5719999999981,"wall_ms":226.94153618067503},{"cpu_ms":233.21200000000175,"wall_ms":245.5650728661567},{"cpu_ms":220.3090000000003,"wall_ms":231.68392991647124},{"cpu_ms":280.5269999999993,"wall_ms":290.87189212441444},{"cpu_ms":300.90200000000067,"wall_ms":310.3425819426775},{"cpu_ms":298.4640000000027,"wall_ms":306.77365115843713},{"cpu_ms":298.7310000000001,"wall_ms":311.6175450850278},{"cpu_ms":316.89599999999984,"wall_ms":328.31171504221857},{"cpu_ms":315.4779999999988,"wall_ms":334.7430150024593},{"cpu_ms":292.8800000000002,"wall_ms":304.51346607878804},{"cpu_ms":289.0289999999993,"wall_ms":300.69926590658724},{"cpu_ms":297.03399999999647,"wall_ms":310.4971081484109},{"cpu_ms":277.041999999998,"wall_ms":285.4053608607501},{"cpu_ms":287.51100000000207,"wall_ms":296.47300601936877},{"cpu_ms":280.7679999999948,"wall_ms":292.8043690044433},{"cpu_ms":271.1280000000045,"wall_ms":284.930121852085},{"cpu_ms":274.0709999999993,"wall_ms":289.41655601374805},{"cpu_ms":282.7249999999992,"wall_ms":289.0996530186385},{"cpu_ms":284.6589999999978,"wall_ms":306.96842982433736},{"cpu_ms":272.3830000000049,"wall_ms":284.22597399912775},{"cpu_ms":275.1429999999999,"wall_ms":283.98134699091315},{"cpu_ms":266.3379999999975,"wall_ms":281.20458615012467},{"cpu_ms":283.19799999999873,"wall_ms":292.04313294030726},{"cpu_ms":286.9150000000005,"wall_ms":296.3133140001446},{"cpu_ms":284.1510000000014,"wall_ms":293.53920789435506},{"cpu_ms":293.73999999999967,"wall_ms":300.1024939585477},{"cpu_ms":276.02599999999455,"wall_ms":285.3989130817354},{"cpu_ms":309.86999999999654,"wall_ms":340.33471695147455},{"cpu_ms":269.6690000000004,"wall_ms":281.8860069382936},{"cpu_ms":300.67900000000236,"wall_ms":405.967861879617},{"cpu_ms":284.05599999999964,"wall_ms":291.43925104290247},{"cpu_ms":290.71400000000125,"wall_ms":297.977760899812},{"cpu_ms":318.4870000000046,"wall_ms":601.334183011204},{"cpu_ms":308.45399999999756,"wall_ms":319.07275482080877},{"cpu_ms":291.1009999999976,"wall_ms":301.6120020765811},{"cpu_ms":268.7620000000024,"wall_ms":296.7006720136851},{"cpu_ms":286.014999999999,"wall_ms":296.2156350258738},{"cpu_ms":297.66500000000207,"wall_ms":315.6020059250295},{"cpu_ms":289.06200000000126,"wall_ms":316.4477879181504},{"cpu_ms":287.068000000005,"wall_ms":303.95097099244595},{"cpu_ms":311.14900000000034,"wall_ms":319.3434982094914},{"cpu_ms":294.31100000000754,"wall_ms":312.12278199382126},{"cpu_ms":296.45299999999963,"wall_ms":310.9716009348631},{"cpu_ms":296.9269999999966,"wall_ms":308.3546489942819},{"cpu_ms":333.6320000000086,"wall_ms":382.57384998723865},{"cpu_ms":327.57900000000006,"wall_ms":355.4964030627161},{"cpu_ms":310.35100000001137,"wall_ms":321.7308970633894},{"cpu_ms":328.1520000000029,"wall_ms":534.6197828184813},{"cpu_ms":300.25899999999695,"wall_ms":313.4419941343367},{"cpu_ms":329.0280000000081,"wall_ms":474.3598389904946},{"cpu_ms":310.89900000000625,"wall_ms":444.56286006607115},{"cpu_ms":301.1979999999994,"wall_ms":311.77125102840364},{"cpu_ms":298.49900000000673,"wall_ms":314.7593850735575},{"cpu_ms":314.2479999999921,"wall_ms":331.01937314495444},{"cpu_ms":296.3629999999995,"wall_ms":309.44366892799735},{"cpu_ms":286.70599999999524,"wall_ms":299.09050883725286},{"cpu_ms":309.3730000000079,"wall_ms":334.4002100639045},{"cpu_ms":296.84699999999964,"wall_ms":308.3218408282846},{"cpu_ms":293.74099999999714,"wall_ms":307.1537578944117},{"cpu_ms":294.5150000000041,"wall_ms":300.9942560456693},{"cpu_ms":306.744000000009,"wall_ms":314.1787259373814},{"cpu_ms":302.02799999999286,"wall_ms":310.36839401349425},{"cpu_ms":295.2980000000025,"wall_ms":306.6552239470184},{"cpu_ms":287.66099999999994,"wall_ms":300.0285499729216},{"cpu_ms":298.44599999999843,"wall_ms":307.8693130519241},{"cpu_ms":288.61600000000465,"wall_ms":298.02597500383854},{"cpu_ms":274.98399999998924,"wall_ms":284.35287112370133},{"cpu_ms":249.46899999999061,"wall_ms":260.85876300930977},{"cpu_ms":231.64100000001042,"wall_ms":239.96054590679705},{"cpu_ms":298.8320000000044,"wall_ms":306.26230686903},{"cpu_ms":287.74899999999093,"wall_ms":301.2178197968751},{"cpu_ms":288.1,"wall_ms":300.5466649774462},{"cpu_ms":291.98299999998767,"wall_ms":296.56555387191474},{"cpu_ms":298.4610000000032,"wall_ms":309.7202889621258},{"cpu_ms":287.54200000000196,"wall_ms":294.9452761095017},{"cpu_ms":286.02200000000266,"wall_ms":298.4850301872939},{"cpu_ms":299.8369999999966,"wall_ms":308.2953249104321},{"cpu_ms":294.01299999999253,"wall_ms":307.92752699926496},{"cpu_ms":294.2070000000001,"wall_ms":308.5487480275333},{"cpu_ms":301.3909999999953,"wall_ms":309.8227281589061},{"cpu_ms":296.22799999999927,"wall_ms":313.6694170534611}],"after_control_median_cpu_ms":288.35800000000233}, +{"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":79.46600000000004,"wall_ms":88.77624408341944},{"cpu_ms":82.52099999999984,"wall_ms":89.95988592505455},{"cpu_ms":84.26400000000012,"wall_ms":88.67698791436851},{"cpu_ms":83.06999999999975,"wall_ms":89.46322719566524},{"cpu_ms":76.99100000000047,"wall_ms":88.44152791425586},{"cpu_ms":52.23800000000001,"wall_ms":57.68605903722346},{"cpu_ms":72.29900000000012,"wall_ms":78.64116202108562},{"cpu_ms":74.66599999999968,"wall_ms":79.1010910179466},{"cpu_ms":74.3520000000002,"wall_ms":79.59853112697601},{"cpu_ms":74.27500000000009,"wall_ms":78.66253214888275},{"cpu_ms":75.2499999999996,"wall_ms":78.64957791753113},{"cpu_ms":74.19499999999957,"wall_ms":78.56268505565822},{"cpu_ms":76.79999999999953,"wall_ms":80.23105305619538},{"cpu_ms":70.01600000000074,"wall_ms":78.39295687153935},{"cpu_ms":72.79000000000036,"wall_ms":80.13187791220844},{"cpu_ms":75.291,"wall_ms":78.68498307652771},{"cpu_ms":74.75599999999983,"wall_ms":79.14439309388399},{"cpu_ms":71.79300000000043,"wall_ms":79.13658698089421},{"cpu_ms":71.09599999999983,"wall_ms":78.4788690507412},{"cpu_ms":76.79299999999944,"wall_ms":80.17458789981902},{"cpu_ms":71.82199999999916,"wall_ms":78.25079187750816},{"cpu_ms":71.71199999999978,"wall_ms":79.16641305200756},{"cpu_ms":76.0670000000001,"wall_ms":80.47006884589791},{"cpu_ms":72.01900000000094,"wall_ms":78.38899013586342},{"cpu_ms":71.08399999999904,"wall_ms":82.0341119542718},{"cpu_ms":74.22899999999899,"wall_ms":78.61653505824506},{"cpu_ms":70.34600000000069,"wall_ms":78.8166499696672},{"cpu_ms":69.22899999999998,"wall_ms":75.51912707276642},{"cpu_ms":77.38200000000006,"wall_ms":82.7775620855391},{"cpu_ms":75.3970000000006,"wall_ms":81.8900631275028},{"cpu_ms":76.90800000000131,"wall_ms":83.27373908832669},{"cpu_ms":70.06099999999904,"wall_ms":76.42473885789514},{"cpu_ms":75.25300000000001,"wall_ms":81.74316887743771},{"cpu_ms":76.90400000000075,"wall_ms":86.28539694473147},{"cpu_ms":86.24799999999944,"wall_ms":92.56521402858198},{"cpu_ms":88.44200000000058,"wall_ms":92.83760306425393},{"cpu_ms":87.99800000000069,"wall_ms":93.40737690217793},{"cpu_ms":84.93999999999957,"wall_ms":92.21477806568146},{"cpu_ms":85.3420000000007,"wall_ms":91.64964291267097},{"cpu_ms":90.1689999999995,"wall_ms":93.57653395272791},{"cpu_ms":87.74400000000071,"wall_ms":93.13825401477516},{"cpu_ms":87.85099999999879,"wall_ms":93.81397720426321},{"cpu_ms":86.1850000000004,"wall_ms":92.52904495224357},{"cpu_ms":89.20100000000097,"wall_ms":92.61666890233755},{"cpu_ms":85.38000000000068,"wall_ms":92.74310618638992},{"cpu_ms":85.6989999999982,"wall_ms":93.1180261541158},{"cpu_ms":86.85000000000187,"wall_ms":92.30659902095795},{"cpu_ms":85.94799999999836,"wall_ms":91.34433208964765},{"cpu_ms":87.74499999999819,"wall_ms":93.18265714682639},{"cpu_ms":87.35199999999921,"wall_ms":91.77976497448981},{"cpu_ms":84.92999999999995,"wall_ms":91.45432687364519},{"cpu_ms":90.13099999999952,"wall_ms":95.20712099038064},{"cpu_ms":87.39299999999872,"wall_ms":92.87287713959813},{"cpu_ms":50.170999999998855,"wall_ms":55.594047997146845},{"cpu_ms":49.18199999999828,"wall_ms":55.52622117102146},{"cpu_ms":50.46700000000115,"wall_ms":56.73861689865589},{"cpu_ms":88.80800000000022,"wall_ms":94.27255392074585},{"cpu_ms":86.76100000000275,"wall_ms":92.20643201842904},{"cpu_ms":85.90099999999978,"wall_ms":92.30640903115273},{"cpu_ms":85.80000000000254,"wall_ms":91.28374792635441},{"cpu_ms":85.05999999999858,"wall_ms":91.46639914251864},{"cpu_ms":86.28100000000316,"wall_ms":92.67706098034978},{"cpu_ms":87.7139999999983,"wall_ms":97.10868401452899},{"cpu_ms":86.36000000000266,"wall_ms":92.90463593788445},{"cpu_ms":85.55300000000088,"wall_ms":92.93563500978053},{"cpu_ms":86.93500000000043,"wall_ms":92.33568399213254},{"cpu_ms":85.3699999999975,"wall_ms":93.84204889647663},{"cpu_ms":89.30400000000205,"wall_ms":93.72668294236064},{"cpu_ms":84.55600000000274,"wall_ms":93.07025582529604},{"cpu_ms":88.19799999999844,"wall_ms":93.89046905562282},{"cpu_ms":89.35800000000071,"wall_ms":93.83219992741942},{"cpu_ms":89.64600000000189,"wall_ms":94.08438112586737},{"cpu_ms":87.92100000000147,"wall_ms":92.33305812813342},{"cpu_ms":87.49999999999858,"wall_ms":92.94101409614086},{"cpu_ms":84.12999999999826,"wall_ms":92.54652704112232},{"cpu_ms":87.1840000000006,"wall_ms":92.60168997570872},{"cpu_ms":89.29799999999943,"wall_ms":92.79043297283351},{"cpu_ms":87.46799999999766,"wall_ms":92.9431680124253},{"cpu_ms":86.2090000000002,"wall_ms":92.57099195383489},{"cpu_ms":89.26299999999898,"wall_ms":93.64960785023868},{"cpu_ms":84.23300000000111,"wall_ms":93.71150401420891},{"cpu_ms":84.16499999999871,"wall_ms":92.59241283871233},{"cpu_ms":84.4370000000012,"wall_ms":91.7964051477611},{"cpu_ms":84.03900000000064,"wall_ms":92.47169690206647},{"cpu_ms":86.24100000000112,"wall_ms":92.63202594593167},{"cpu_ms":84.91900000000285,"wall_ms":93.35787314921618},{"cpu_ms":88.19700000000097,"wall_ms":92.6044350489974},{"cpu_ms":83.81299999999925,"wall_ms":94.22155702486634},{"cpu_ms":84.96999999999844,"wall_ms":91.37933584861457},{"cpu_ms":85.77999999999975,"wall_ms":92.19709294848144},{"cpu_ms":87.54100000000165,"wall_ms":92.9956859908998},{"cpu_ms":86.48800000000278,"wall_ms":92.87288715131581},{"cpu_ms":86.25699999999625,"wall_ms":92.6964869722724},{"cpu_ms":87.21500000000049,"wall_ms":91.66402695700526},{"cpu_ms":85.4360000000014,"wall_ms":91.79094387218356},{"cpu_ms":85.22800000000075,"wall_ms":91.66118199937046},{"cpu_ms":85.19900000000291,"wall_ms":92.63238706625998},{"cpu_ms":84.80399999999833,"wall_ms":93.29860215075314},{"cpu_ms":85.4619999999997,"wall_ms":91.88312594778836},{"cpu_ms":87.47799999999728,"wall_ms":93.94937800243497}],"before_median_cpu_ms":85.12950000000075,"after":[{"cpu_ms":86.46900000000014,"wall_ms":89.85738409683108},{"cpu_ms":88.69600000000011,"wall_ms":93.14020909368992},{"cpu_ms":81.84000000000013,"wall_ms":92.18620602041483},{"cpu_ms":84.26899999999992,"wall_ms":90.680121909827},{"cpu_ms":82.73999999999981,"wall_ms":90.11316206306219},{"cpu_ms":47.99400000000009,"wall_ms":54.32645999826491},{"cpu_ms":76.6,"wall_ms":80.9854930266738},{"cpu_ms":73.1080000000004,"wall_ms":87.51415507867932},{"cpu_ms":72.28600000000007,"wall_ms":80.61123406514525},{"cpu_ms":77.36300000000007,"wall_ms":79.73158010281622},{"cpu_ms":72.07000000000008,"wall_ms":79.43451404571533},{"cpu_ms":74.60900000000059,"wall_ms":80.05296112969518},{"cpu_ms":75.63999999999993,"wall_ms":80.48524800688028},{"cpu_ms":74.554,"wall_ms":80.90298902243376},{"cpu_ms":71.66599999999957,"wall_ms":80.61245596036315},{"cpu_ms":75.67699999999977,"wall_ms":79.08160402439535},{"cpu_ms":74.18899999999962,"wall_ms":79.59655718877912},{"cpu_ms":73.25099999999995,"wall_ms":79.63314605876803},{"cpu_ms":74.71800000000073,"wall_ms":80.0583700183779},{"cpu_ms":74.06299999999976,"wall_ms":80.43814008124173},{"cpu_ms":75.20200000000088,"wall_ms":80.59983211569488},{"cpu_ms":73.1180000000009,"wall_ms":80.46826696954668},{"cpu_ms":75.19199999999948,"wall_ms":79.5749370008707},{"cpu_ms":74.28999999999952,"wall_ms":79.67160688713193},{"cpu_ms":72.18800000000059,"wall_ms":80.60715510509908},{"cpu_ms":72.77700000000031,"wall_ms":82.11531420238316},{"cpu_ms":75.90700000000083,"wall_ms":80.27416281402111},{"cpu_ms":72.82799999999945,"wall_ms":77.23371079191566},{"cpu_ms":77.89900000000038,"wall_ms":85.25840798392892},{"cpu_ms":76.88600000000001,"wall_ms":83.29210383817554},{"cpu_ms":78.01100000000005,"wall_ms":82.44581194594502},{"cpu_ms":75.04100000000058,"wall_ms":83.40168907307088},{"cpu_ms":70.96199999999975,"wall_ms":82.88129488937557},{"cpu_ms":79.96499999999962,"wall_ms":85.34046192653477},{"cpu_ms":86.65099999999981,"wall_ms":94.00667692534626},{"cpu_ms":87.48799999999868,"wall_ms":93.92031584866345},{"cpu_ms":89.97099999999847,"wall_ms":95.43405496515334},{"cpu_ms":86.88300000000027,"wall_ms":94.317268114537},{"cpu_ms":90.1329999999998,"wall_ms":95.5083139706403},{"cpu_ms":92.20200000000034,"wall_ms":95.60546511784196},{"cpu_ms":88.14799999999856,"wall_ms":95.72653216309845},{"cpu_ms":85.12900000000023,"wall_ms":94.54644611105323},{"cpu_ms":85.22199999999991,"wall_ms":94.87021993845701},{"cpu_ms":82.61999999999858,"wall_ms":95.01622407697141},{"cpu_ms":87.21599999999974,"wall_ms":93.61211000941694},{"cpu_ms":90.19600000000239,"wall_ms":92.6227099262178},{"cpu_ms":84.05600000000035,"wall_ms":93.4806540608406},{"cpu_ms":86.09500000000025,"wall_ms":93.49984000436962},{"cpu_ms":87.62700000000123,"wall_ms":94.01833801530302},{"cpu_ms":89.43100000000115,"wall_ms":93.82094908505678},{"cpu_ms":87.82899999999927,"wall_ms":94.28143012337387},{"cpu_ms":90.66500000000133,"wall_ms":96.02674189954996},{"cpu_ms":88.37700000000126,"wall_ms":94.93554197251797},{"cpu_ms":76.91000000000159,"wall_ms":91.26727818511426},{"cpu_ms":48.29300000000103,"wall_ms":55.65323890186846},{"cpu_ms":46.585999999997796,"wall_ms":55.88706489652395},{"cpu_ms":87.42300000000114,"wall_ms":94.87755503505468},{"cpu_ms":89.19599999999761,"wall_ms":94.62439105845988},{"cpu_ms":87.92299999999997,"wall_ms":95.39122390560806},{"cpu_ms":88.36999999999762,"wall_ms":92.77101699262857},{"cpu_ms":84.21500000000037,"wall_ms":92.53068803809583},{"cpu_ms":89.08599999999822,"wall_ms":93.50903611630201},{"cpu_ms":89.1769999999994,"wall_ms":93.56480091810226},{"cpu_ms":88.04499999999749,"wall_ms":94.46301986463368},{"cpu_ms":88.59299999999948,"wall_ms":94.99926096759737},{"cpu_ms":90.19699999999986,"wall_ms":93.5883040074259},{"cpu_ms":89.62000000000003,"wall_ms":95.01817589625716},{"cpu_ms":89.51499999999868,"wall_ms":95.94334522262216},{"cpu_ms":88.6289999999974,"wall_ms":94.044947065413},{"cpu_ms":91.76899999999932,"wall_ms":95.24006093852222},{"cpu_ms":87.26399999999757,"wall_ms":94.72698206081986},{"cpu_ms":89.90100000000112,"wall_ms":96.32479795254767},{"cpu_ms":85.21099999999748,"wall_ms":94.63273594155908},{"cpu_ms":88.16100000000304,"wall_ms":94.58328410983086},{"cpu_ms":88.59800000000106,"wall_ms":94.00041401386261},{"cpu_ms":86.77899999999994,"wall_ms":94.17359810322523},{"cpu_ms":86.71600000000268,"wall_ms":94.19483784586191},{"cpu_ms":86.9339999999994,"wall_ms":95.38233699277043},{"cpu_ms":90.53400000000167,"wall_ms":94.98230880126357},{"cpu_ms":90.67500000000095,"wall_ms":94.09877797588706},{"cpu_ms":91.03499999999798,"wall_ms":94.42533901892602},{"cpu_ms":86.3800000000019,"wall_ms":94.84433103352785},{"cpu_ms":90.41500000000013,"wall_ms":94.80185084976256},{"cpu_ms":89.03299999999703,"wall_ms":93.4607160743326},{"cpu_ms":85.57099999999807,"wall_ms":93.96122093312442},{"cpu_ms":88.6509999999987,"wall_ms":94.03674211353064},{"cpu_ms":88.10000000000073,"wall_ms":95.53582314401865},{"cpu_ms":90.22900000000078,"wall_ms":94.66327191330492},{"cpu_ms":86.90700000000007,"wall_ms":94.31733610108495},{"cpu_ms":91.14299999999886,"wall_ms":95.5920279957354},{"cpu_ms":89.77799999999903,"wall_ms":95.21901118569076},{"cpu_ms":85.66700000000083,"wall_ms":95.0804220046848},{"cpu_ms":84.40699999999879,"wall_ms":93.86676410213113},{"cpu_ms":92.30600000000067,"wall_ms":95.7197470124811},{"cpu_ms":87.82599999999974,"wall_ms":93.19215314462781},{"cpu_ms":86.54700000000304,"wall_ms":93.91261893324554},{"cpu_ms":84.08899999999875,"wall_ms":93.51301193237305},{"cpu_ms":91.62600000000509,"wall_ms":94.10161292180419},{"cpu_ms":89.1550000000052,"wall_ms":93.59754086472094},{"cpu_ms":90.66299999999927,"wall_ms":94.03579006902874}],"after_median_cpu_ms":86.68350000000125,"before_control":[{"cpu_ms":78.32599999999968,"wall_ms":90.74520296417177},{"cpu_ms":82.41399999999999,"wall_ms":88.85025093331933},{"cpu_ms":79.75200000000004,"wall_ms":88.14530493691564},{"cpu_ms":79.45700000000011,"wall_ms":87.8912191838026},{"cpu_ms":81.7249999999996,"wall_ms":90.12694703415036},{"cpu_ms":78.87099999999991,"wall_ms":88.2384090218693},{"cpu_ms":68.73099999999965,"wall_ms":76.07530592940748},{"cpu_ms":72.90499999999955,"wall_ms":80.27510601095855},{"cpu_ms":74.7770000000001,"wall_ms":81.35600504465401},{"cpu_ms":71.20700000000025,"wall_ms":79.50581796467304},{"cpu_ms":75.50500000000059,"wall_ms":79.9230569973588},{"cpu_ms":73.68400000000008,"wall_ms":81.04186900891364},{"cpu_ms":74.41600000000025,"wall_ms":80.72550687938929},{"cpu_ms":74.04600000000005,"wall_ms":81.45448891445994},{"cpu_ms":69.47200000000021,"wall_ms":78.78883695229888},{"cpu_ms":72.12300000000039,"wall_ms":78.53560405783355},{"cpu_ms":72.114,"wall_ms":78.41079100035131},{"cpu_ms":71.94400000000023,"wall_ms":79.35702987015247},{"cpu_ms":73.4129999999995,"wall_ms":78.76166701316833},{"cpu_ms":73.2879999999998,"wall_ms":79.74006491713226},{"cpu_ms":75.18199999999986,"wall_ms":83.51996005512774},{"cpu_ms":76.81799999999939,"wall_ms":84.22583807259798},{"cpu_ms":75.21700000000031,"wall_ms":79.18321480974555},{"cpu_ms":73.68600000000036,"wall_ms":79.14076512679458},{"cpu_ms":73.21600000000039,"wall_ms":79.63138190098107},{"cpu_ms":72.56399999999985,"wall_ms":78.98648688569665},{"cpu_ms":73.80299999999984,"wall_ms":78.16538191400468},{"cpu_ms":74.62100000000049,"wall_ms":80.95619804225862},{"cpu_ms":74.681,"wall_ms":81.06776606291533},{"cpu_ms":75.79899999999995,"wall_ms":83.1663180142641},{"cpu_ms":77.67699999999955,"wall_ms":83.14043004065752},{"cpu_ms":81.7060000000005,"wall_ms":87.33720309101045},{"cpu_ms":80.32099999999964,"wall_ms":84.75084905512631},{"cpu_ms":83.12600000000003,"wall_ms":87.5378679484129},{"cpu_ms":83.44899999999988,"wall_ms":88.88514596037567},{"cpu_ms":89.75799999999978,"wall_ms":95.22130712866783},{"cpu_ms":85.18500000000095,"wall_ms":91.64565498940647},{"cpu_ms":86.79399999999937,"wall_ms":93.26136391609907},{"cpu_ms":86.86799999999906,"wall_ms":93.2804299518466},{"cpu_ms":87.98700000000004,"wall_ms":94.36730085872114},{"cpu_ms":84.3790000000002,"wall_ms":92.76297292672098},{"cpu_ms":87.9929999999991,"wall_ms":92.48706698417664},{"cpu_ms":88.56899999999968,"wall_ms":92.9790970403701},{"cpu_ms":85.63599999999916,"wall_ms":92.02392096631229},{"cpu_ms":84.363999999999,"wall_ms":92.86850993521512},{"cpu_ms":86.35899999999808,"wall_ms":92.82432706095278},{"cpu_ms":84.57800000000049,"wall_ms":91.93469514138997},{"cpu_ms":83.31599999999995,"wall_ms":92.6921300124377},{"cpu_ms":88.41100000000068,"wall_ms":92.84738986752927},{"cpu_ms":86.58400000000199,"wall_ms":92.04751602374017},{"cpu_ms":88.50400000000036,"wall_ms":91.9505450874567},{"cpu_ms":87.08400000000083,"wall_ms":92.53648901358247},{"cpu_ms":85.58299999999974,"wall_ms":92.03199599869549},{"cpu_ms":85.27899999999988,"wall_ms":92.61530684307218},{"cpu_ms":58.84600000000262,"wall_ms":64.17540600523353},{"cpu_ms":87.05099999999888,"wall_ms":93.48249807953835},{"cpu_ms":86.2090000000002,"wall_ms":91.64158697240055},{"cpu_ms":83.83099999999999,"wall_ms":92.19682402908802},{"cpu_ms":88.64499999999964,"wall_ms":93.0433461908251},{"cpu_ms":85.67599999999942,"wall_ms":93.09115493670106},{"cpu_ms":85.3880000000018,"wall_ms":93.05541892535985},{"cpu_ms":85.56800000000209,"wall_ms":92.97037008218467},{"cpu_ms":87.9460000000023,"wall_ms":91.38801204971969},{"cpu_ms":84.52700000000135,"wall_ms":90.95836896449327},{"cpu_ms":89.07500000000113,"wall_ms":92.48656616546214},{"cpu_ms":85.10699999999716,"wall_ms":91.53097914531827},{"cpu_ms":90.25700000000114,"wall_ms":109.82688213698566},{"cpu_ms":88.80500000000069,"wall_ms":93.21158984676003},{"cpu_ms":86.90199999999848,"wall_ms":93.28368492424488},{"cpu_ms":91.37600000000035,"wall_ms":94.82304193079472},{"cpu_ms":86.80000000000021,"wall_ms":93.20975700393319},{"cpu_ms":85.06700000000222,"wall_ms":93.48910814151168},{"cpu_ms":88.46099999999879,"wall_ms":91.9050790835172},{"cpu_ms":87.28800000000092,"wall_ms":92.71859796717763},{"cpu_ms":88.99900000000116,"wall_ms":93.42966810800135},{"cpu_ms":88.89499999999728,"wall_ms":93.30415306612849},{"cpu_ms":86.68499999999923,"wall_ms":93.05489691905677},{"cpu_ms":86.1060000000009,"wall_ms":93.57073111459613},{"cpu_ms":85.82699999999832,"wall_ms":92.23128785379231},{"cpu_ms":87.14600000000061,"wall_ms":92.55707706324756},{"cpu_ms":85.75000000000088,"wall_ms":93.1603938806802},{"cpu_ms":84.18299999999945,"wall_ms":92.62047498486936},{"cpu_ms":86.24599999999916,"wall_ms":91.62039612419903},{"cpu_ms":84.99800000000235,"wall_ms":93.42925599776208},{"cpu_ms":84.93899999999854,"wall_ms":92.4440051894635},{"cpu_ms":82.99000000000234,"wall_ms":91.38601808808744},{"cpu_ms":88.10000000000073,"wall_ms":92.46546495705843},{"cpu_ms":87.60499999999993,"wall_ms":92.05617010593414},{"cpu_ms":87.56500000000145,"wall_ms":92.0503989327699},{"cpu_ms":87.46299999999962,"wall_ms":92.84629696048796},{"cpu_ms":86.15099999999742,"wall_ms":92.57876686751842},{"cpu_ms":85.40800000000104,"wall_ms":92.85900020040572},{"cpu_ms":85.1380000000006,"wall_ms":92.58273500017822},{"cpu_ms":87.71200000000334,"wall_ms":92.30113797821105},{"cpu_ms":83.42700000000036,"wall_ms":91.90649096854031},{"cpu_ms":86.05700000000382,"wall_ms":91.47660713642836},{"cpu_ms":84.93299999999948,"wall_ms":91.35256498120725},{"cpu_ms":85.4009999999974,"wall_ms":91.85684681870043},{"cpu_ms":87.15600000000023,"wall_ms":91.56396007165313},{"cpu_ms":86.19200000000404,"wall_ms":93.47686497494578}],"before_control_median_cpu_ms":85.12249999999888,"after_control":[{"cpu_ms":83.93300000000004,"wall_ms":90.64256097190082},{"cpu_ms":85.11600000000018,"wall_ms":90.83747514523566},{"cpu_ms":83.93799999999985,"wall_ms":89.31812597438693},{"cpu_ms":82.04900000000004,"wall_ms":89.52248794957995},{"cpu_ms":82.70400000000011,"wall_ms":89.1542499884963},{"cpu_ms":82.61399999999996,"wall_ms":89.02636100538075},{"cpu_ms":54.48600000000026,"wall_ms":56.841890793293715},{"cpu_ms":76.21900000000004,"wall_ms":81.66628493927419},{"cpu_ms":76.44099999999997,"wall_ms":80.8708081021905},{"cpu_ms":73.34299999999949,"wall_ms":79.68339882791042},{"cpu_ms":76.2179999999999,"wall_ms":80.61712491326034},{"cpu_ms":73.66299999999981,"wall_ms":81.06311806477606},{"cpu_ms":75.30600000000032,"wall_ms":81.63341414183378},{"cpu_ms":74.35299999999944,"wall_ms":80.8555199764669},{"cpu_ms":75.26699999999931,"wall_ms":82.68951810896397},{"cpu_ms":74.03600000000043,"wall_ms":79.44229803979397},{"cpu_ms":71.86499999999984,"wall_ms":80.31102316454053},{"cpu_ms":73.9799999999997,"wall_ms":80.37038310430944},{"cpu_ms":76.64000000000027,"wall_ms":80.07785584777594},{"cpu_ms":73.47999999999999,"wall_ms":79.89746984094381},{"cpu_ms":81.23600000000053,"wall_ms":86.61698712967336},{"cpu_ms":73.8310000000002,"wall_ms":81.24550897628069},{"cpu_ms":73.0599999999999,"wall_ms":79.44916211999953},{"cpu_ms":72.41299999999917,"wall_ms":79.74704797379673},{"cpu_ms":73.9800000000006,"wall_ms":79.34612897224724},{"cpu_ms":72.18199999999975,"wall_ms":79.55188397318125},{"cpu_ms":72.76799999999994,"wall_ms":79.10513784736395},{"cpu_ms":80.35699999999935,"wall_ms":84.69301089644432},{"cpu_ms":74.26500000000047,"wall_ms":80.65163879655302},{"cpu_ms":86.28699999999867,"wall_ms":91.63759998045862},{"cpu_ms":78.21099999999959,"wall_ms":82.61356502771378},{"cpu_ms":80.97199999999916,"wall_ms":87.11366495117545},{"cpu_ms":80.95400000000019,"wall_ms":89.33995594270527},{"cpu_ms":85.02600000000093,"wall_ms":91.42862888984382},{"cpu_ms":87.52899999999997,"wall_ms":91.92086919210851},{"cpu_ms":84.5930000000017,"wall_ms":94.94464099407196},{"cpu_ms":86.83199999999935,"wall_ms":95.21965403109789},{"cpu_ms":89.58200000000005,"wall_ms":94.94668408297002},{"cpu_ms":86.79200000000087,"wall_ms":93.16486492753029},{"cpu_ms":85.21100000000104,"wall_ms":94.63914996013045},{"cpu_ms":87.69999999999989,"wall_ms":94.2418270278722},{"cpu_ms":87.84300000000123,"wall_ms":94.22890190035105},{"cpu_ms":90.00900000000023,"wall_ms":95.44839197769761},{"cpu_ms":87.12600000000137,"wall_ms":93.52915501222014},{"cpu_ms":84.94600000000219,"wall_ms":95.38054396398365},{"cpu_ms":87.65299999999954,"wall_ms":94.03787483461201},{"cpu_ms":87.1549999999992,"wall_ms":93.55400106869638},{"cpu_ms":86.49799999999885,"wall_ms":92.93277002871037},{"cpu_ms":89.27500000000066,"wall_ms":94.69668590463698},{"cpu_ms":84.25799999999839,"wall_ms":94.7465191129595},{"cpu_ms":85.65899999999971,"wall_ms":94.13208090700209},{"cpu_ms":89.62899999999863,"wall_ms":95.0856739655137},{"cpu_ms":84.99499999999927,"wall_ms":93.42488995753229},{"cpu_ms":86.9320000000009,"wall_ms":93.35144190117717},{"cpu_ms":59.52599999999819,"wall_ms":63.930719159543514},{"cpu_ms":89.82900000000171,"wall_ms":94.2889847792685},{"cpu_ms":89.18399999999949,"wall_ms":93.57341681607068},{"cpu_ms":91.0340000000005,"wall_ms":95.43507592752576},{"cpu_ms":86.82899999999805,"wall_ms":94.32329796254635},{"cpu_ms":84.63000000000065,"wall_ms":92.9818011354655},{"cpu_ms":87.8799999999984,"wall_ms":93.33942993544042},{"cpu_ms":87.9259999999995,"wall_ms":93.3870580047369},{"cpu_ms":90.13899999999708,"wall_ms":94.60570593364537},{"cpu_ms":91.08200000000011,"wall_ms":95.52990412339568},{"cpu_ms":89.2710000000001,"wall_ms":94.65283388271928},{"cpu_ms":86.1060000000009,"wall_ms":93.51758286356926},{"cpu_ms":88.99399999999957,"wall_ms":95.4658230766654},{"cpu_ms":87.39900000000134,"wall_ms":94.80289393104613},{"cpu_ms":89.00900000000078,"wall_ms":98.06766593828797},{"cpu_ms":89.15999999999968,"wall_ms":94.61409109644592},{"cpu_ms":90.11600000000186,"wall_ms":95.5074408557266},{"cpu_ms":87.28099999999728,"wall_ms":94.71396799199283},{"cpu_ms":87.37800000000107,"wall_ms":94.80511792935431},{"cpu_ms":86.94599999999753,"wall_ms":94.43037793971598},{"cpu_ms":89.49099999999888,"wall_ms":94.94592086412013},{"cpu_ms":88.95400000000109,"wall_ms":94.35571799986064},{"cpu_ms":91.24800000000022,"wall_ms":95.73690895922482},{"cpu_ms":88.94300000000044,"wall_ms":94.35954503715038},{"cpu_ms":88.59600000000256,"wall_ms":93.99521397426724},{"cpu_ms":85.32999999999902,"wall_ms":95.0939180329442},{"cpu_ms":88.44399999999908,"wall_ms":93.82806206122041},{"cpu_ms":89.13599999999988,"wall_ms":94.55262613482773},{"cpu_ms":88.5770000000008,"wall_ms":95.00154503621161},{"cpu_ms":87.83400000000086,"wall_ms":95.2774710021913},{"cpu_ms":90.36299999999997,"wall_ms":94.86201405525208},{"cpu_ms":87.5099999999982,"wall_ms":93.90518488362432},{"cpu_ms":84.1569999999976,"wall_ms":93.60428317449987},{"cpu_ms":86.89499999999839,"wall_ms":94.3148008082062},{"cpu_ms":89.85600000000105,"wall_ms":95.26663110591471},{"cpu_ms":87.01699999999946,"wall_ms":93.42919592745602},{"cpu_ms":87.50700000000222,"wall_ms":93.9652279485017},{"cpu_ms":86.47299999999802,"wall_ms":93.95889611914754},{"cpu_ms":86.79899999999918,"wall_ms":94.29303999058902},{"cpu_ms":87.8540000000001,"wall_ms":93.49855594336987},{"cpu_ms":86.74399999999594,"wall_ms":93.12233189120889},{"cpu_ms":89.3389999999954,"wall_ms":93.75763987191021},{"cpu_ms":88.8569999999973,"wall_ms":95.34160001203418},{"cpu_ms":88.70100000000036,"wall_ms":94.0882780123502},{"cpu_ms":87.77099999999649,"wall_ms":93.15329091623425},{"cpu_ms":84.87399999999923,"wall_ms":95.5101849976927}],"after_control_median_cpu_ms":86.79550000000003} +] diff --git a/benchmarks/regexp-construction/final-probe-samples.json b/benchmarks/regexp-construction/final-probe-samples.json new file mode 100644 index 0000000000..0bf3dcef1b --- /dev/null +++ b/benchmarks/regexp-construction/final-probe-samples.json @@ -0,0 +1,4 @@ +[ +{"mode":"all","scale":1,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"construct":486.563,"test-ascii":1.382,"test-emoji":2.087,"stripAnsi":7.598,"stripAnsi-match":24.524,"ignorable":1.895,"segment":46.415,"eastAsianWidth":0.716,"stringWidth":28377.543},{"construct":401.385,"test-ascii":1.387,"test-emoji":2.103,"stripAnsi":7.638,"stripAnsi-match":15.95,"ignorable":1.098,"segment":27.325,"eastAsianWidth":0.456,"stringWidth":27962.527},{"construct":412.168,"test-ascii":1.373,"test-emoji":2.095,"stripAnsi":7.721,"stripAnsi-match":16.015,"ignorable":1.105,"segment":32.948,"eastAsianWidth":0.5,"stringWidth":30986.198},{"construct":418.158,"test-ascii":2.358,"test-emoji":3.516,"stripAnsi":10.297,"stripAnsi-match":16.092,"ignorable":1.088,"segment":27.752,"eastAsianWidth":0.461,"stringWidth":29279.289},{"construct":590.168,"test-ascii":2.172,"test-emoji":3.166,"stripAnsi":12.496,"stripAnsi-match":24.536,"ignorable":1.563,"segment":44.667,"eastAsianWidth":0.718,"stringWidth":29615.987},{"construct":414.224,"test-ascii":1.403,"test-emoji":2.145,"stripAnsi":7.772,"stripAnsi-match":16.326,"ignorable":1.106,"segment":32.94,"eastAsianWidth":0.477,"stringWidth":39308.083},{"construct":633.154,"test-ascii":2.431,"test-emoji":3.702,"stripAnsi":13.803,"stripAnsi-match":28.171,"ignorable":1.896,"segment":49.899,"eastAsianWidth":0.846,"stringWidth":43306.389},{"construct":615.811,"test-ascii":2.402,"test-emoji":3.623,"stripAnsi":13.635,"stripAnsi-match":28.34,"ignorable":1.922,"segment":49.162,"eastAsianWidth":0.835,"stringWidth":34492.87},{"construct":398.079,"test-ascii":1.384,"test-emoji":2.1,"stripAnsi":7.605,"stripAnsi-match":15.906,"ignorable":1.08,"segment":27.147,"eastAsianWidth":0.462,"stringWidth":28050.351},{"construct":438.339,"test-ascii":1.403,"test-emoji":2.127,"stripAnsi":7.713,"stripAnsi-match":16.989,"ignorable":1.11,"segment":28.228,"eastAsianWidth":0.472,"stringWidth":29341.456},{"construct":471.975,"test-ascii":1.407,"test-emoji":2.155,"stripAnsi":7.861,"stripAnsi-match":16.785,"ignorable":1.099,"segment":28.13,"eastAsianWidth":0.502,"stringWidth":30624.78},{"construct":398.361,"test-ascii":1.367,"test-emoji":2.089,"stripAnsi":7.673,"stripAnsi-match":16.175,"ignorable":1.093,"segment":27.169,"eastAsianWidth":0.461,"stringWidth":41827.758}],"after":[{"construct":3.324,"test-ascii":0.507,"test-emoji":1.254,"stripAnsi":0.335,"stripAnsi-match":2.552,"ignorable":0.222,"segment":28.569,"eastAsianWidth":0.443,"stringWidth":235.693},{"construct":3.203,"test-ascii":0.509,"test-emoji":1.259,"stripAnsi":0.341,"stripAnsi-match":2.603,"ignorable":0.219,"segment":28.541,"eastAsianWidth":0.463,"stringWidth":237.043},{"construct":3.323,"test-ascii":0.531,"test-emoji":1.357,"stripAnsi":0.349,"stripAnsi-match":2.636,"ignorable":0.218,"segment":30.054,"eastAsianWidth":0.475,"stringWidth":277.593},{"construct":4.451,"test-ascii":0.593,"test-emoji":1.303,"stripAnsi":0.356,"stripAnsi-match":2.689,"ignorable":0.224,"segment":29.724,"eastAsianWidth":0.481,"stringWidth":236.255},{"construct":4.662,"test-ascii":1.008,"test-emoji":2.252,"stripAnsi":0.69,"stripAnsi-match":4.718,"ignorable":0.41,"segment":52.723,"eastAsianWidth":0.918,"stringWidth":396.287},{"construct":3.453,"test-ascii":0.501,"test-emoji":1.225,"stripAnsi":0.336,"stripAnsi-match":2.604,"ignorable":0.258,"segment":30.67,"eastAsianWidth":0.445,"stringWidth":303.529},{"construct":4.759,"test-ascii":0.902,"test-emoji":2.107,"stripAnsi":0.632,"stripAnsi-match":4.404,"ignorable":0.398,"segment":49.701,"eastAsianWidth":1.174,"stringWidth":384.111},{"construct":3.216,"test-ascii":0.507,"test-emoji":1.237,"stripAnsi":0.357,"stripAnsi-match":2.591,"ignorable":0.236,"segment":27.936,"eastAsianWidth":0.459,"stringWidth":233.837},{"construct":3.354,"test-ascii":0.504,"test-emoji":1.256,"stripAnsi":0.326,"stripAnsi-match":2.514,"ignorable":0.227,"segment":28.531,"eastAsianWidth":0.458,"stringWidth":293.199},{"construct":3.663,"test-ascii":0.51,"test-emoji":1.252,"stripAnsi":0.344,"stripAnsi-match":2.701,"ignorable":0.227,"segment":29.5,"eastAsianWidth":0.451,"stringWidth":239.479},{"construct":3.296,"test-ascii":0.515,"test-emoji":1.333,"stripAnsi":0.34,"stripAnsi-match":2.684,"ignorable":0.224,"segment":29.149,"eastAsianWidth":0.499,"stringWidth":243.677},{"construct":3.215,"test-ascii":0.526,"test-emoji":1.26,"stripAnsi":0.35,"stripAnsi-match":2.664,"ignorable":0.226,"segment":28.269,"eastAsianWidth":0.445,"stringWidth":234.746}],"bun":[{"construct":2.382,"test-ascii":1.335,"test-emoji":0.337,"stripAnsi":0.443,"stripAnsi-match":0.335,"ignorable":0.227,"segment":14.554,"eastAsianWidth":1.07,"stringWidth":25.008},{"construct":2.387,"test-ascii":0.455,"test-emoji":0.31,"stripAnsi":0.306,"stripAnsi-match":0.332,"ignorable":0.186,"segment":9.868,"eastAsianWidth":0.648,"stringWidth":27.321},{"construct":2.206,"test-ascii":0.386,"test-emoji":0.424,"stripAnsi":0.308,"stripAnsi-match":0.323,"ignorable":0.149,"segment":13.794,"eastAsianWidth":0.651,"stringWidth":32.528},{"construct":3.738,"test-ascii":0.669,"test-emoji":0.728,"stripAnsi":0.549,"stripAnsi-match":0.636,"ignorable":0.27,"segment":14.931,"eastAsianWidth":0.803,"stringWidth":30.705},{"construct":3.32,"test-ascii":0.656,"test-emoji":0.561,"stripAnsi":0.437,"stripAnsi-match":0.371,"ignorable":0.269,"segment":15.016,"eastAsianWidth":1.215,"stringWidth":51.317},{"construct":3.413,"test-ascii":0.754,"test-emoji":0.62,"stripAnsi":0.53,"stripAnsi-match":0.528,"ignorable":0.298,"segment":21.254,"eastAsianWidth":1.648,"stringWidth":54.265},{"construct":3.353,"test-ascii":0.721,"test-emoji":0.579,"stripAnsi":0.607,"stripAnsi-match":0.396,"ignorable":0.269,"segment":20.298,"eastAsianWidth":1.021,"stringWidth":49.6},{"construct":2.26,"test-ascii":0.388,"test-emoji":0.307,"stripAnsi":0.308,"stripAnsi-match":0.475,"ignorable":0.155,"segment":13.285,"eastAsianWidth":0.672,"stringWidth":25.033},{"construct":2.274,"test-ascii":0.387,"test-emoji":0.308,"stripAnsi":0.347,"stripAnsi-match":0.341,"ignorable":0.247,"segment":12.333,"eastAsianWidth":0.964,"stringWidth":26.127},{"construct":3.947,"test-ascii":0.692,"test-emoji":0.578,"stripAnsi":0.397,"stripAnsi-match":0.548,"ignorable":0.279,"segment":16.095,"eastAsianWidth":1.061,"stringWidth":26.673},{"construct":2.318,"test-ascii":0.546,"test-emoji":0.345,"stripAnsi":0.363,"stripAnsi-match":0.384,"ignorable":0.211,"segment":12.485,"eastAsianWidth":0.989,"stringWidth":25.862},{"construct":3.756,"test-ascii":0.741,"test-emoji":0.769,"stripAnsi":0.608,"stripAnsi-match":0.666,"ignorable":0.904,"segment":16.996,"eastAsianWidth":1.296,"stringWidth":54.103}]},"output":["constructed g 14116","checksum 162430"],"medians_us":{"before":{"construct":428.24850000000004,"test-ascii":1.403,"test-emoji":2.136,"stripAnsi":7.7465,"stripAnsi-match":16.555500000000002,"ignorable":1.1055000000000001,"segment":30.584,"eastAsianWidth":0.4885,"stringWidth":30120.3835},"after":{"construct":3.339,"test-ascii":0.5125,"test-emoji":1.2595,"stripAnsi":0.3465,"stripAnsi-match":2.6500000000000004,"ignorable":0.2265,"segment":29.3245,"eastAsianWidth":0.461,"stringWidth":241.578},"bun":{"construct":2.8535,"test-ascii":0.6625000000000001,"test-emoji":0.49250000000000005,"stripAnsi":0.41700000000000004,"stripAnsi-match":0.39,"ignorable":0.258,"segment":14.7425,"eastAsianWidth":1.005,"stringWidth":29.012999999999998}}}, +{"mode":"test-emoji","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"test-emoji":3.992},{"test-emoji":3.64},{"test-emoji":2.135},{"test-emoji":2.691},{"test-emoji":3.252},{"test-emoji":2.548},{"test-emoji":2.132},{"test-emoji":2.139},{"test-emoji":2.169},{"test-emoji":2.118},{"test-emoji":2.087},{"test-emoji":2.097}],"after":[{"test-emoji":2.155},{"test-emoji":1.291},{"test-emoji":1.317},{"test-emoji":1.278},{"test-emoji":2.014},{"test-emoji":1.92},{"test-emoji":1.223},{"test-emoji":1.255},{"test-emoji":1.236},{"test-emoji":1.259},{"test-emoji":1.225},{"test-emoji":1.231}],"bun":[{"test-emoji":0.058},{"test-emoji":0.052},{"test-emoji":0.041},{"test-emoji":0.047},{"test-emoji":0.065},{"test-emoji":0.047},{"test-emoji":0.048},{"test-emoji":0.047},{"test-emoji":0.047},{"test-emoji":0.044},{"test-emoji":0.037},{"test-emoji":0.036}]},"output":["checksum 100000"],"medians_us":{"before":{"test-emoji":2.154},"after":{"test-emoji":1.2685},"bun":{"test-emoji":0.047}}} +] diff --git a/benchmarks/regexp-construction/final-results.md b/benchmarks/regexp-construction/final-results.md new file mode 100644 index 0000000000..962bc75a53 --- /dev/null +++ b/benchmarks/regexp-construction/final-results.md @@ -0,0 +1,264 @@ +# RegExp construction cache: final Perex 0.1.4 verification + +Historical verification against Perry 0.5.1554. See +[current-results.md](current-results.md) for the subsequent 0.5.1557 integration. + +The final runtime is `7be32ca293`, integrating main +`5d3bf85f92632cd026ec2695fd1a6929ffb56d72` (Perry 0.5.1554 / Perex 0.1.4). +Its production runtime sources match preserved build snapshot +`b2599a6d60beeae085822d1ccdc359c8f058c8d8`. +A subsequent test-only assertion formatting fix changes no runtime code. +There is one construction cache, upstream's in-cell program-validation witness, +guarded builtin dispatch, and span-based empty replacement. Compilation stays +eager; scratch buffers remain operation-owned. The optional scratch-hint +experiment was removed after an existing-benchmark regression. + +Every build, test, probe, and profile ran on Linux through +`GIT_DIR=/tmp/perry-regexp-lane.git ./remote.sh ''`; none ran on the Mac. +Fresh main and final use matching compiler/runtime/stdlib bundles with identical +explicit release flags. The main bundle is `/tmp/regexp-main-1554-libs` +(built from detached `/tmp/regexp-main-1554`); final is +`/tmp/regexp-cache-final-libs`. Their builds took 5m 01s and 5m 40s. + +## Attribution and implementation + +The original attribution was established before runtime edits; see +[results.md](results.md). The active implementation already used Perex and had +**no construction cache**. Neither SipHash cache misses nor an uncached fallback +engine explained the repeated compilation. Lookbehind and backreferences use +the same engine and cache. + +| Real input | Original sampled CPU attribution | +|---|---| +| 14,116-character emoji construction | Parser cursor 32.13%, sequence 18.28%, escapes 17.83%, character classes 6.73%, emission 6.27%. Every construction reparsed and rebuilt the program. | +| Reused emoji test on ASCII | About 77% validating immutable words (`Program::from_words`, ranges and repeats). Upstream's program witness now handles this across calls. | +| Plain stripAnsi | Generic property/reflective reads, flag getters, closure dispatch, exception traps and runtime handles dominate; program validation contributes 2.12%. No-match replacement already avoided result arrays. | + +The cache bounds source/program payload to 32 MiB and 512 entries and evicts +individual LRU entries. Source-identity plus canonical-flag hits read no pattern +bytes; independent equal strings use hashing followed by exact comparison. +Mutable roots retain source/program cells and rewrite identity keys after +movement. Every RegExp retains its own original source/flags and `lastIndex`. +Source strings are marked shared against in-place append. + +Builtin guards check receiver metadata, expandos, prototype shape, current +`exec` value, flag getter identities and symbol epochs. Observable overrides, +coercions and descriptors retain the ordinary path. Empty replacement returns +the original input on no match and builds matched output from spans without +exec arrays/capture strings. Numeric `lastIndex` avoids a coercion trap. + +Final diagnostics count **three compilations, 200 identity hits and 14,277 +source bytes hashed**, exactly the three initial source lengths. The 200 reused +emoji constructions add no source hashing or compilation. The short final +construction profile encountered heavy kernel page migration/compaction; +its percentage breakdown is not treated as representative. The remaining +user work is object publication, flags, handle management, barriers and a +small identity/flags hash. + +Final ASCII-test samples attribute 6.46% to native method dispatch, 5.64% to +search setup, 4.60% to primitive dispatch, 4.31% to the VM, 4.11% to exception +traps and 2.86% to scratch zeroing. Plain replacement shows search setup +9.35%, VM 6.99%, exception traps 6.36%, replacement entry 6.08%, plus handles, +guards and scratch allocation. Repeated program parsing/validation no longer +dominates either path. These boolean/no-match cases allocate no exec result +object; actual ANSI matches additionally assemble output spans. + +## Real-package probe + +The probe resolves OpenCode v1.18.30's installed emoji-regex 10.6.0, +string-width 7.2.0, strip-ansi 7.1.2 and get-east-asian-width 1.6.0. +Perry reports 10 modules, versus the issue's earlier nine-module count. +Bun is 1.3.14. Both native arms use `--no-auto-optimize`. + +Twelve balanced rounds on CPU 15 cover all six before/after/Bun orders. +Both native builds execute at one fixed pathname/inode; copying is outside +the timed region. Every checksum and non-timing output agrees. `all 1` uses +the original counts: 200 constructions, 2,000 tests/replacements, 200 +segmentations and 90 string-width calls; checksum 162430. These medians include +the first operation and are wall microseconds per operation. + +| Operation | Main | Final | Bun 1.3.14 | +|---|---:|---:|---:| +| Emoji construction | 428.249 | 3.339 | 2.853 | +| Reused emoji test, ASCII | 1.403 | 0.512 | 0.663 | +| Reused emoji test, rocket | 2.136 | 1.260 | 0.493 | +| stripAnsi, plain help line | 7.747 | 0.346 | 0.417 | +| stripAnsi, ANSI-colored text | 16.556 | 2.650 | 0.390 | +| Default_Ignorable test | 1.106 | 0.227 | 0.258 | +| Intl.Segmenter | 30.584 | 29.325 | 14.742 | +| eastAsianWidth | 0.488 | 0.461 | 1.005 | +| stringWidth | 30120.383 | 241.578 | 29.013 | + +Construction improves **128×**, plain stripAnsi **22×**, and stringWidth +**125×**. Construction meets 20 µs, both reused tests meet 2 µs, and plain +stripAnsi meets 1 µs. ANSI text with actual matches still costs 2.650 µs. +The longer isolated rocket test (scale 100, 200,000 calls) measures +**2.154 → 1.269 µs**, Bun 0.047 µs. +[Raw samples](final-probe-samples.json). + +Warm Bun remains much faster at matching. These dependency measurements do +not establish a final startup time for a rebuilt full `opencode --help`. +This is a shared host; absolute times change with other builds. Our own +concurrent checks excluded CPU 15 and its SMT sibling 7. + +## Existing benchmarks and the rejected experiment + +All 12 `benchmarks/app-patterns/kernels/*.ts` are compiled with each matching +bundle and checked against Node 26.5.1. Twelve balanced four-arm rounds follow +one warmup: two identical-copy labels per build, 24 samples/build. Timings are +median child user CPU milliseconds; wall samples are retained too. + +| Kernel | Main ms | Final ms | Change | Main A/A | Final A/A | +|---|---:|---:|---:|---:|---:| +| batch | 42.725 | 42.415 | -0.73% | -3.19% | +0.89% | +| buffer_transcode | 68.292 | 68.619 | +0.48% | -0.59% | -1.77% | +| date_format_parse | 45.191 | 45.535 | +0.76% | -2.21% | +2.07% | +| json_parse_1mb | 83.011 | 84.247 | +1.49% | +0.42% | -0.94% | +| json_stringify_1mb | 131.093 | 131.135 | +0.03% | -3.55% | -0.04% | +| map_1m | 310.660 | 327.919 | +5.56% | -2.70% | -1.61% | +| object_deep_clone | 28.301 | 28.728 | +1.51% | +1.33% | -5.84% | +| promise_all_chains | 141.036 | 122.427 | -13.19% | -17.87% | -17.15% | +| regex_replace | 3091.335 | 2776.635 | -10.18% | +1.27% | -3.63% | +| string_concat_csv | 45.956 | 44.423 | -3.34% | +6.04% | -4.82% | +| string_split_map_join | 273.491 | 251.919 | -7.89% | -2.40% | -8.30% | +| string_template_interp | 65.850 | 63.677 | -3.30% | +4.76% | +4.34% | + +The A/A columns compare identical-copy label medians within each build. +This loaded-host sweep contains large control spreads; its small deltas are +not a universal no-regression guarantee. The existing regex replacement kernel +improves 10.18%. [All raw app samples](final-app-samples.json). + +The longer 100-round loaded controls reduce the Map delta to **+0.33%** +(286.647 → 287.602 ms; 95% block-bootstrap interval -0.33% to +2.54%). +JSON remains **+1.93%** (85.122 → 86.761 ms; interval +0.58% to +2.86%), +despite the earlier quieter control below. The strict no-regression requirement +therefore remains unmet, and the PR is held in draft. No padding or unrelated +JSON optimization was added to hide this result. +Intervals resample balanced four-round blocks 5,000 times, seed 10179; +the raw data, rather than the interval alone, is the evidence. +[Loaded control samples](final-loaded-control-samples.json). + +A 100-round JSON control (200 samples/build) measures +**49.961 → 49.817 ms**, a -0.29% change. The individual identical-copy label +medians are 49.539 / 50.172 ms for main and 49.994 / 49.624 ms for final. +[Raw JSON control](final-json-control-samples.json). + +An optional scalar-hint experiment (`bc9212fcc8`) reduced emoji scratch +growth from 2.000 to 0.500/search and isolated rocket-test time by 28%. +Its unit, moving-GC, parity and Test262 results passed. However, JSON parsing +slowed by 2.59% in 100 balanced rounds, and about 1.6% on another CPU core. +GC diagnostics showed identical collection/object/byte counts and no RegExp +operations. Disassembly showed the same hot parser loop bytes moved from +`0x254194` to `0x256ca4`, crossing a 64-byte instruction-cache line. +That suggests a layout effect, but it does not erase the measured regression. +The experiment was removed; the smaller implementation still meets the regex +targets. [Experiment data](hint-experiment-samples.json). + +## Correctness and required checks + +- Regex runtime slice: **103 passed** (8.43s). +- Full runtime lib: **3,720 passed, four ignored, one failed** (164.80s). + The failure is `native_stack::tests::stack_top_respects_custom_thread_stack_sizes`; + fresh main reproduces it with the same flags (seven other native-stack tests pass). +- Regex parity against Node 26.5.1: **25 passed**, zero differences, compile + failures, crashes or skips (5m 15s, including the HTTP extension rebuild). +- Full pinned Test262: **1,786 passed, 129 runtime failures, zero output + differences, zero compile failures, seven skipped**; 1,915 judged and + 210 negative agreements. All 129 failure-name/category pairs match fresh main. + [Comparison artifact](final-test262-comparison.json). +- Final formatting, file-size cap, registration (296 files/four registries), + GC-root inventory (1,413 holders/155 scanners) and Node-version checks: pass. + The first post-revert fmt check found one assertion layout; it was corrected + and the checks rerun successfully. +- Final `pre-tag-check.sh --quick`: only public benchmark evidence freshness + fails. The same failure was reproduced on freshly built main; its committed + public artifact was already stale. +- Strict performance gate: **not yet met**. The final loaded JSON control + retains a small slowdown, detailed below; this is not a regression-free verdict. + +The current `test.yml` has no separately named regex/Test262 job +(`rg -n regex .github/workflows/test.yml` finds only a feature comment). +Its relevant coverage comes from runtime and parity suites. The focused parity +slice and full RegExp plus Annex B Test262 corpus are run explicitly here. +Test262 is pinned at `4249661388e5d3f92a85186213da140a6481490f`; +Node 26.5.1 matches `.node-version`. + +## Exact host commands + +All commands below ran through the remote wrapper. Each source tree built all +three matching release artifacts; no unrelated prebuilt compiler was used. + +```sh +export RUSTFLAGS="-C force-unwind-tables=yes -C force-frame-pointers=yes" +export PATH=/tmp/regexp-oracle/node-v26.5.1-linux-x64/bin:$PATH +cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static +RUST_TEST_THREADS=1 cargo test -p perry-runtime --lib regex +RUST_TEST_THREADS=1 cargo test -p perry-runtime --lib +# In freshly built detached main: +RUST_TEST_THREADS=1 cargo test -p perry-runtime --lib native_stack +python3 benchmarks/ci_public_baseline_check.py + +cargo fmt --all -- --check +./scripts/check_file_size.sh +python3 scripts/check_test_registration.py +python3 scripts/gc_runtime_root_holders.py +python3 scripts/check_node_version_consistency.py --list +./scripts/pre-tag-check.sh --quick + +python3 benchmarks/regexp-construction/prepare.py /tmp/regexp-probe.ts +PERRY_RUNTIME_DIR=/tmp/regexp-main-1554-libs /tmp/regexp-main-1554-libs/perry \ + compile /tmp/regexp-probe.ts --no-auto-optimize --debug-symbols \ + -o /tmp/regexp-probe-main-1554 +PERRY_RUNTIME_DIR=/tmp/regexp-cache-final-libs /tmp/regexp-cache-final-libs/perry \ + compile /tmp/regexp-probe.ts --no-auto-optimize --debug-symbols \ + -o /tmp/regexp-probe-cache-final +taskset -c 15 python3 benchmarks/regexp-construction/measure.py \ + --before /tmp/regexp-probe-main-1554 --after /tmp/regexp-probe-cache-final \ + --source /tmp/regexp-probe.ts --bun /root/claude-opencode/bun/bin/bun \ + --output /tmp/regexp-nohint-target-recheck.json --runs 12 \ + --case all:1 --case test-emoji:100 +taskset -c 15 python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-main-1554-libs --after /tmp/regexp-cache-final-libs \ + --output /tmp/regexp-app-nohint-final --runs 12 --paired-controls +taskset -c 15 python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-main-1554-libs --after /tmp/regexp-cache-final-libs \ + --output /tmp/regexp-nohint-json-control --runs 100 --paired-controls \ + --filter json_parse_1mb + +for kernel in map_1m json_parse_1mb; do + taskset -c 15 python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-main-1554-libs --after /tmp/regexp-cache-final-libs \ + --output "/tmp/regexp-nohint-final-control-$kernel" --runs 100 \ + --paired-controls --filter "$kernel" +done + +# Match the preserved compiler's build ID before the HTTP extension build: +git checkout --detach b2599a6d60beeae085822d1ccdc359c8f058c8d8 +PERRY_SKIP_BUILD=1 PERRY_BIN=/tmp/regexp-cache-final-libs/perry \ + PERRY_RUNTIME_DIR=/tmp/regexp-cache-final-libs taskset -c 0-6,8-14 \ + ./run_parity_tests.sh --filter regex +PERRY_RUNTIME_DIR=/tmp/regexp-main-1554-libs taskset -c 0-7 \ + python3 scripts/test262_subset.py --root /tmp/regexp-test262 \ + --dir built-ins/RegExp annexB/built-ins/RegExp --all-features --jobs 4 \ + --perry-bin /tmp/regexp-main-1554-libs/perry \ + --report /tmp/regexp-test262-main-1554.json --sample-cap 100000 +PERRY_RUNTIME_DIR=/tmp/regexp-cache-final-libs taskset -c 0-6,8-14 \ + python3 scripts/test262_subset.py --root /tmp/regexp-test262 \ + --dir built-ins/RegExp annexB/built-ins/RegExp --all-features --jobs 8 \ + --perry-bin /tmp/regexp-cache-final-libs/perry \ + --report /tmp/regexp-test262-nohint-final.json --sample-cap 100000 + +PERRY_REGEX_DIAG=1 taskset -c 15 /tmp/regexp-probe-cache-final all 1 +for mode in construct test-ascii stripAnsi; do + taskset -c 15 perf record -q -F 999 -g --call-graph dwarf \ + -o "/tmp/regexp-nohint-final-$mode.perf" \ + /tmp/regexp-probe-cache-final "$mode" 1000 + perf report --stdio --no-children -g none \ + -i "/tmp/regexp-nohint-final-$mode.perf" --percent-limit 1 -F overhead,symbol +done +``` + +The benchmark tools now reject non-positive/non-integer iteration controls. +Fourteen invalid driver cases and 15 invalid direct scales were checked; valid +output agreed across Perry, Node and Bun. The direct positive-scale guard was +added outside the timed loops after the preserved native timing samples. diff --git a/benchmarks/regexp-construction/final-test262-comparison.json b/benchmarks/regexp-construction/final-test262-comparison.json new file mode 100644 index 0000000000..1b822e5346 --- /dev/null +++ b/benchmarks/regexp-construction/final-test262-comparison.json @@ -0,0 +1,160 @@ +{ + "baseline": { + "source": "5d3bf85f92632cd026ec2695fd1a6929ffb56d72", + "generated_at": "2026-09-13T13:56:09Z", + "totals": { + "pass": 1786, + "diff": 0, + "runtime-fail": 129, + "compile-fail": 0, + "skip": 7 + } + }, + "final": { + "source": "7be32ca293", + "build_snapshot": "b2599a6d60beeae085822d1ccdc359c8f058c8d8", + "generated_at": "2026-09-13T15:27:29Z", + "totals": { + "pass": 1786, + "diff": 0, + "runtime-fail": 129, + "compile-fail": 0, + "skip": 7 + } + }, + "test262_pinned": "4249661388e5d3f92a85186213da140a6481490f", + "node_runtime": "v26.5.1", + "judged": 1915, + "negative_agreements": 210, + "identical_failure_pairs": [ + "annexB/built-ins/RegExp/legacy-accessors/input/prop-desc.js|runtime-fail", + "annexB/built-ins/RegExp/prototype/Symbol.split/Symbol.match-getter-recompiles-source.js|runtime-fail", + "built-ins/RegExp/S15.10.2.8_A3_T15.js|runtime-fail", + "built-ins/RegExp/S15.10.2.8_A3_T16.js|runtime-fail", + "built-ins/RegExp/Symbol.species/length.js|runtime-fail", + "built-ins/RegExp/Symbol.species/return-value.js|runtime-fail", + "built-ins/RegExp/Symbol.species/symbol-species-name.js|runtime-fail", + "built-ins/RegExp/Symbol.species/symbol-species.js|runtime-fail", + "built-ins/RegExp/escape/escaped-surrogates.js|runtime-fail", + "built-ins/RegExp/escape/is-function.js|runtime-fail", + "built-ins/RegExp/escape/length.js|runtime-fail", + "built-ins/RegExp/escape/name.js|runtime-fail", + "built-ins/RegExp/escape/non-string-inputs.js|runtime-fail", + "built-ins/RegExp/escape/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/escape/prop-desc.js|runtime-fail", + "built-ins/RegExp/from-regexp-like-get-ctor-err.js|runtime-fail", + "built-ins/RegExp/named-groups/non-unicode-property-names-invalid.js|runtime-fail", + "built-ins/RegExp/named-groups/unicode-property-names-invalid.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/Basic_Emoji.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/Emoji_Keycap_Sequence.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji_Flag_Sequence.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji_Modifier_Sequence.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji_Tag_Sequence.js|runtime-fail", + "built-ins/RegExp/property-escapes/generated/strings/RGI_Emoji_ZWJ_Sequence.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.match/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.matchAll/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.replace/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.search/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/prototype/Symbol.split/not-a-constructor.js|runtime-fail", + "built-ins/RegExp/quantifier-integer-limit.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-escape-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-class-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-property-escape-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/character-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/property-of-strings-escape-union-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-13.1.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-14.0.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-15.0.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-15.1.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-16.0.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/rgi-emoji-17.0.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-difference-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-intersection-string-literal.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-character-class-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-character-class.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-character-property-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-character.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-property-of-strings-escape.js|runtime-fail", + "built-ins/RegExp/unicodeSets/generated/string-literal-union-string-literal.js|runtime-fail" + ] +} diff --git a/benchmarks/regexp-construction/hint-experiment-samples.json b/benchmarks/regexp-construction/hint-experiment-samples.json new file mode 100644 index 0000000000..2f57d2d3fe --- /dev/null +++ b/benchmarks/regexp-construction/hint-experiment-samples.json @@ -0,0 +1,11 @@ +[ +{"runtime_source":"bc9212fcc8"}, +{"decision":"Dropped by 7be32ca293: repeated JSON benchmark regression; final runtime has no scratch hints."}, +{"probe":[{"mode":"all","scale":1,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"construct":806.587,"test-ascii":2.882,"test-emoji":4.283,"stripAnsi":29.214,"stripAnsi-match":36.714,"ignorable":2.189,"segment":189.217,"eastAsianWidth":1.009,"stringWidth":47643.121},{"construct":843.578,"test-ascii":2.494,"test-emoji":3.473,"stripAnsi":14.251,"stripAnsi-match":27.215,"ignorable":1.875,"segment":56.269,"eastAsianWidth":0.865,"stringWidth":41457.79},{"construct":925.378,"test-ascii":2.387,"test-emoji":3.609,"stripAnsi":13.482,"stripAnsi-match":25.866,"ignorable":1.73,"segment":45.502,"eastAsianWidth":0.791,"stringWidth":43408.362},{"construct":589.64,"test-ascii":2.65,"test-emoji":3.925,"stripAnsi":13.595,"stripAnsi-match":26.47,"ignorable":1.596,"segment":51.945,"eastAsianWidth":0.778,"stringWidth":48742.967},{"construct":614.723,"test-ascii":2.73,"test-emoji":3.463,"stripAnsi":15.27,"stripAnsi-match":29.81,"ignorable":2.133,"segment":48.905,"eastAsianWidth":0.805,"stringWidth":46136.825},{"construct":618.672,"test-ascii":2.415,"test-emoji":3.609,"stripAnsi":13.295,"stripAnsi-match":25.628,"ignorable":1.798,"segment":46.171,"eastAsianWidth":0.77,"stringWidth":42584.275},{"construct":610.579,"test-ascii":2.326,"test-emoji":4.89,"stripAnsi":14.467,"stripAnsi-match":27.916,"ignorable":3.281,"segment":45.481,"eastAsianWidth":0.76,"stringWidth":45752.809},{"construct":621.493,"test-ascii":2.262,"test-emoji":3.42,"stripAnsi":13.188,"stripAnsi-match":27.486,"ignorable":1.83,"segment":48.199,"eastAsianWidth":0.778,"stringWidth":48317.35},{"construct":648.068,"test-ascii":2.268,"test-emoji":4.944,"stripAnsi":15.024,"stripAnsi-match":29.684,"ignorable":1.787,"segment":47.858,"eastAsianWidth":0.811,"stringWidth":44857.605},{"construct":609.271,"test-ascii":2.653,"test-emoji":3.129,"stripAnsi":13.374,"stripAnsi-match":30.102,"ignorable":1.598,"segment":48.843,"eastAsianWidth":0.73,"stringWidth":50471.116},{"construct":666.735,"test-ascii":2.44,"test-emoji":5.249,"stripAnsi":15.849,"stripAnsi-match":33.018,"ignorable":2.043,"segment":52.006,"eastAsianWidth":0.92,"stringWidth":45190.704},{"construct":654.262,"test-ascii":3.889,"test-emoji":3.628,"stripAnsi":16.294,"stripAnsi-match":28.046,"ignorable":1.874,"segment":50.862,"eastAsianWidth":0.786,"stringWidth":42865.551}],"after":[{"construct":8.606,"test-ascii":1.035,"test-emoji":3.25,"stripAnsi":0.643,"stripAnsi-match":5.762,"ignorable":0.41,"segment":274.717,"eastAsianWidth":0.773,"stringWidth":384.511},{"construct":31.969,"test-ascii":0.831,"test-emoji":1.418,"stripAnsi":0.501,"stripAnsi-match":3.401,"ignorable":0.314,"segment":61.489,"eastAsianWidth":0.837,"stringWidth":346.228},{"construct":93.504,"test-ascii":0.521,"test-emoji":1.212,"stripAnsi":0.571,"stripAnsi-match":3.439,"ignorable":0.335,"segment":111.282,"eastAsianWidth":0.768,"stringWidth":360.434},{"construct":281.821,"test-ascii":0.996,"test-emoji":1.588,"stripAnsi":0.646,"stripAnsi-match":4.193,"ignorable":0.392,"segment":78.339,"eastAsianWidth":0.838,"stringWidth":387.628},{"construct":4.584,"test-ascii":0.94,"test-emoji":1.556,"stripAnsi":0.605,"stripAnsi-match":4.379,"ignorable":0.382,"segment":49.691,"eastAsianWidth":0.806,"stringWidth":381.955},{"construct":4.679,"test-ascii":0.805,"test-emoji":1.977,"stripAnsi":0.525,"stripAnsi-match":4.982,"ignorable":0.391,"segment":46.497,"eastAsianWidth":0.833,"stringWidth":387.764},{"construct":4.343,"test-ascii":0.84,"test-emoji":2.54,"stripAnsi":0.62,"stripAnsi-match":3.788,"ignorable":0.375,"segment":66.078,"eastAsianWidth":0.826,"stringWidth":413.573},{"construct":4.639,"test-ascii":0.903,"test-emoji":1.51,"stripAnsi":0.562,"stripAnsi-match":3.669,"ignorable":0.358,"segment":62.263,"eastAsianWidth":0.703,"stringWidth":374.987},{"construct":4.497,"test-ascii":0.844,"test-emoji":1.447,"stripAnsi":0.551,"stripAnsi-match":3.668,"ignorable":0.374,"segment":49.256,"eastAsianWidth":0.753,"stringWidth":360.756},{"construct":4.235,"test-ascii":0.935,"test-emoji":1.499,"stripAnsi":0.562,"stripAnsi-match":5.178,"ignorable":0.318,"segment":47.454,"eastAsianWidth":0.862,"stringWidth":368.774},{"construct":4.567,"test-ascii":0.864,"test-emoji":1.603,"stripAnsi":0.586,"stripAnsi-match":3.764,"ignorable":0.379,"segment":61.893,"eastAsianWidth":0.681,"stringWidth":369.075},{"construct":4.064,"test-ascii":0.868,"test-emoji":1.443,"stripAnsi":0.529,"stripAnsi-match":3.462,"ignorable":0.326,"segment":45.762,"eastAsianWidth":0.807,"stringWidth":359.938}],"bun":[{"construct":3.485,"test-ascii":1.677,"test-emoji":0.555,"stripAnsi":0.636,"stripAnsi-match":0.767,"ignorable":0.501,"segment":16.13,"eastAsianWidth":1.406,"stringWidth":34.207},{"construct":4.056,"test-ascii":0.814,"test-emoji":0.479,"stripAnsi":0.507,"stripAnsi-match":0.552,"ignorable":0.931,"segment":19.788,"eastAsianWidth":1.228,"stringWidth":47.958},{"construct":4.414,"test-ascii":0.721,"test-emoji":0.729,"stripAnsi":0.573,"stripAnsi-match":0.634,"ignorable":0.737,"segment":20.307,"eastAsianWidth":1.265,"stringWidth":55.861},{"construct":4.073,"test-ascii":0.736,"test-emoji":0.611,"stripAnsi":0.506,"stripAnsi-match":0.444,"ignorable":0.304,"segment":19.974,"eastAsianWidth":1.299,"stringWidth":53.4},{"construct":3.814,"test-ascii":0.724,"test-emoji":0.573,"stripAnsi":0.642,"stripAnsi-match":0.533,"ignorable":0.302,"segment":16.068,"eastAsianWidth":1.255,"stringWidth":52.768},{"construct":3.426,"test-ascii":0.735,"test-emoji":0.537,"stripAnsi":0.591,"stripAnsi-match":0.688,"ignorable":0.345,"segment":19.668,"eastAsianWidth":1.168,"stringWidth":44.098},{"construct":3.634,"test-ascii":0.742,"test-emoji":0.503,"stripAnsi":0.604,"stripAnsi-match":0.587,"ignorable":0.269,"segment":18.091,"eastAsianWidth":3.179,"stringWidth":51.781},{"construct":3.738,"test-ascii":0.705,"test-emoji":0.59,"stripAnsi":0.426,"stripAnsi-match":0.425,"ignorable":0.307,"segment":17.857,"eastAsianWidth":0.981,"stringWidth":47.816},{"construct":3.071,"test-ascii":0.662,"test-emoji":0.568,"stripAnsi":0.916,"stripAnsi-match":0.402,"ignorable":0.288,"segment":13.578,"eastAsianWidth":1.135,"stringWidth":41.338},{"construct":3.117,"test-ascii":0.793,"test-emoji":0.422,"stripAnsi":0.48,"stripAnsi-match":0.534,"ignorable":0.246,"segment":42.22,"eastAsianWidth":1.176,"stringWidth":51.263},{"construct":3.378,"test-ascii":0.745,"test-emoji":0.5,"stripAnsi":0.546,"stripAnsi-match":0.595,"ignorable":0.283,"segment":23.391,"eastAsianWidth":1.2,"stringWidth":58.854},{"construct":2.754,"test-ascii":0.662,"test-emoji":0.413,"stripAnsi":0.464,"stripAnsi-match":0.48,"ignorable":0.232,"segment":17.753,"eastAsianWidth":0.989,"stringWidth":46.912}]},"output":["constructed g 14116","checksum 162430"],"medians_us":{"before":{"construct":634.7805000000001,"test-ascii":2.467,"test-emoji":3.6185,"stripAnsi":14.359,"stripAnsi-match":27.981,"ignorable":1.852,"segment":48.874,"eastAsianWidth":0.7885,"stringWidth":45471.7565},"after":{"construct":4.6114999999999995,"test-ascii":0.866,"test-emoji":1.533,"stripAnsi":0.5665,"stripAnsi-match":3.776,"ignorable":0.3745,"segment":61.691,"eastAsianWidth":0.8065,"stringWidth":372.031},"bun":{"construct":3.5595,"test-ascii":0.7355,"test-emoji":0.546,"stripAnsi":0.5595,"stripAnsi-match":0.543,"ignorable":0.303,"segment":18.8795,"eastAsianWidth":1.214,"stringWidth":49.6105}}},{"mode":"construct","scale":10,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"construct":671.449},{"construct":772.846},{"construct":647.359},{"construct":829.128},{"construct":818.07},{"construct":629.412},{"construct":609.828},{"construct":650.149},{"construct":635.837},{"construct":680.781},{"construct":611.566},{"construct":624.209}],"after":[{"construct":0.72},{"construct":0.658},{"construct":0.645},{"construct":0.623},{"construct":2.316},{"construct":0.648},{"construct":0.7},{"construct":0.657},{"construct":0.612},{"construct":0.642},{"construct":1.151},{"construct":0.571}],"bun":[{"construct":0.751},{"construct":0.857},{"construct":0.726},{"construct":0.717},{"construct":2.205},{"construct":0.864},{"construct":0.641},{"construct":1.229},{"construct":0.675},{"construct":0.671},{"construct":0.682},{"construct":0.638}]},"output":["constructed g 14116","checksum 0"],"medians_us":{"before":{"construct":648.754},"after":{"construct":0.6525000000000001},"bun":{"construct":0.7215}}},{"mode":"test-ascii","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"test-ascii":2.328},{"test-ascii":2.623},{"test-ascii":2.223},{"test-ascii":2.957},{"test-ascii":2.394},{"test-ascii":2.299},{"test-ascii":2.3},{"test-ascii":2.616},{"test-ascii":2.329},{"test-ascii":2.38},{"test-ascii":2.42},{"test-ascii":2.763}],"after":[{"test-ascii":0.888},{"test-ascii":0.973},{"test-ascii":0.836},{"test-ascii":0.947},{"test-ascii":1.056},{"test-ascii":0.941},{"test-ascii":0.934},{"test-ascii":0.915},{"test-ascii":0.854},{"test-ascii":0.869},{"test-ascii":0.853},{"test-ascii":0.813}],"bun":[{"test-ascii":0.075},{"test-ascii":0.056},{"test-ascii":0.071},{"test-ascii":0.076},{"test-ascii":0.052},{"test-ascii":0.067},{"test-ascii":0.051},{"test-ascii":0.073},{"test-ascii":0.059},{"test-ascii":0.066},{"test-ascii":0.063},{"test-ascii":0.102}]},"output":["checksum 0"],"medians_us":{"before":{"test-ascii":2.387},"after":{"test-ascii":0.9015},"bun":{"test-ascii":0.0665}}},{"mode":"test-emoji","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"test-emoji":3.534},{"test-emoji":3.776},{"test-emoji":3.727},{"test-emoji":4.143},{"test-emoji":3.996},{"test-emoji":3.934},{"test-emoji":5.38},{"test-emoji":4.753},{"test-emoji":6.358},{"test-emoji":4.099},{"test-emoji":4.589},{"test-emoji":5.023}],"after":[{"test-emoji":1.511},{"test-emoji":1.486},{"test-emoji":1.702},{"test-emoji":1.536},{"test-emoji":1.557},{"test-emoji":1.685},{"test-emoji":1.667},{"test-emoji":1.597},{"test-emoji":1.718},{"test-emoji":1.981},{"test-emoji":2.388},{"test-emoji":2.228}],"bun":[{"test-emoji":0.063},{"test-emoji":0.063},{"test-emoji":0.064},{"test-emoji":0.063},{"test-emoji":0.072},{"test-emoji":0.069},{"test-emoji":0.05},{"test-emoji":0.059},{"test-emoji":0.08},{"test-emoji":0.069},{"test-emoji":0.072},{"test-emoji":0.09}]},"output":["checksum 100000"],"medians_us":{"before":{"test-emoji":4.121},"after":{"test-emoji":1.6760000000000002},"bun":{"test-emoji":0.0665}}},{"mode":"stripAnsi","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"stripAnsi":14.8},{"stripAnsi":16.756},{"stripAnsi":14.683},{"stripAnsi":14.583},{"stripAnsi":15.794},{"stripAnsi":14.723},{"stripAnsi":18.285},{"stripAnsi":18.969},{"stripAnsi":16.087},{"stripAnsi":17.21},{"stripAnsi":19.942},{"stripAnsi":18.671}],"after":[{"stripAnsi":0.621},{"stripAnsi":0.788},{"stripAnsi":0.606},{"stripAnsi":0.588},{"stripAnsi":0.758},{"stripAnsi":0.543},{"stripAnsi":0.654},{"stripAnsi":0.956},{"stripAnsi":0.675},{"stripAnsi":0.641},{"stripAnsi":0.717},{"stripAnsi":0.905}],"bun":[{"stripAnsi":0.055},{"stripAnsi":0.069},{"stripAnsi":0.092},{"stripAnsi":0.055},{"stripAnsi":0.068},{"stripAnsi":0.058},{"stripAnsi":0.059},{"stripAnsi":0.071},{"stripAnsi":0.112},{"stripAnsi":0.12},{"stripAnsi":0.083},{"stripAnsi":0.091}]},"output":["checksum 13400000"],"medians_us":{"before":{"stripAnsi":16.4215},"after":{"stripAnsi":0.6645000000000001},"bun":{"stripAnsi":0.07}}},{"mode":"stripAnsi-match","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"stripAnsi-match":38.371},{"stripAnsi-match":38.571},{"stripAnsi-match":45.011},{"stripAnsi-match":33.26},{"stripAnsi-match":36.407},{"stripAnsi-match":39.517},{"stripAnsi-match":40.393},{"stripAnsi-match":36.2},{"stripAnsi-match":39.384},{"stripAnsi-match":35.576},{"stripAnsi-match":29.875},{"stripAnsi-match":36.27}],"after":[{"stripAnsi-match":6.57},{"stripAnsi-match":6.44},{"stripAnsi-match":5.338},{"stripAnsi-match":5.83},{"stripAnsi-match":4.636},{"stripAnsi-match":6.652},{"stripAnsi-match":4.328},{"stripAnsi-match":4.715},{"stripAnsi-match":4.188},{"stripAnsi-match":6.074},{"stripAnsi-match":5.269},{"stripAnsi-match":4.091}],"bun":[{"stripAnsi-match":0.159},{"stripAnsi-match":0.186},{"stripAnsi-match":0.181},{"stripAnsi-match":0.149},{"stripAnsi-match":0.15},{"stripAnsi-match":0.196},{"stripAnsi-match":0.133},{"stripAnsi-match":0.124},{"stripAnsi-match":0.167},{"stripAnsi-match":0.159},{"stripAnsi-match":0.151},{"stripAnsi-match":0.169}]},"output":["checksum 600000"],"medians_us":{"before":{"stripAnsi-match":37.388999999999996},"after":{"stripAnsi-match":5.3035},"bun":{"stripAnsi-match":0.159}}},{"mode":"segment","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"segment":77.164},{"segment":81.664},{"segment":89.847},{"segment":78.444},{"segment":105.729},{"segment":71.687},{"segment":80.031},{"segment":80.608},{"segment":81.739},{"segment":92.065},{"segment":90.029},{"segment":97.648}],"after":[{"segment":84.447},{"segment":69.858},{"segment":87.589},{"segment":76.981},{"segment":80.262},{"segment":71.962},{"segment":87.084},{"segment":88.154},{"segment":92.658},{"segment":78.205},{"segment":89.93},{"segment":91.605}],"bun":[{"segment":7.318},{"segment":7.111},{"segment":7.324},{"segment":7.867},{"segment":7.097},{"segment":7.558},{"segment":9.106},{"segment":8.108},{"segment":7.446},{"segment":6.929},{"segment":9.501},{"segment":9.647}]},"output":["checksum 1340000"],"medians_us":{"before":{"segment":81.70150000000001},"after":{"segment":85.7655},"bun":{"segment":7.502}}},{"mode":"eastAsianWidth","scale":1000,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"eastAsianWidth":1.071},{"eastAsianWidth":0.982},{"eastAsianWidth":0.915},{"eastAsianWidth":0.948},{"eastAsianWidth":0.949},{"eastAsianWidth":1.04},{"eastAsianWidth":0.939},{"eastAsianWidth":0.918},{"eastAsianWidth":0.953},{"eastAsianWidth":0.995},{"eastAsianWidth":1.05},{"eastAsianWidth":0.909}],"after":[{"eastAsianWidth":1.065},{"eastAsianWidth":1.143},{"eastAsianWidth":1.082},{"eastAsianWidth":0.999},{"eastAsianWidth":1.013},{"eastAsianWidth":1.024},{"eastAsianWidth":0.939},{"eastAsianWidth":0.949},{"eastAsianWidth":0.944},{"eastAsianWidth":0.831},{"eastAsianWidth":1.031},{"eastAsianWidth":0.893}],"bun":[{"eastAsianWidth":0.015},{"eastAsianWidth":0.012},{"eastAsianWidth":0.012},{"eastAsianWidth":0.018},{"eastAsianWidth":0.016},{"eastAsianWidth":0.031},{"eastAsianWidth":0.013},{"eastAsianWidth":0.026},{"eastAsianWidth":0.035},{"eastAsianWidth":0.015},{"eastAsianWidth":0.015},{"eastAsianWidth":0.026}]},"output":["checksum 2000000"],"medians_us":{"before":{"eastAsianWidth":0.951},"after":{"eastAsianWidth":1.006},"bun":{"eastAsianWidth":0.0155}}}]}, +{"apps":[{"name":"batch","node_exit":0,"before":[{"cpu_ms":56.09899999999968,"wall_ms":79.80124792084098},{"cpu_ms":55.0679999999999,"wall_ms":82.3371431324631},{"cpu_ms":56.36099999999988,"wall_ms":80.78923379071057},{"cpu_ms":48.182999999999865,"wall_ms":57.63580300845206},{"cpu_ms":49.27400000000004,"wall_ms":71.86840707436204},{"cpu_ms":62.03799999999981,"wall_ms":133.05295701138675},{"cpu_ms":59.05100000000019,"wall_ms":69.29098512046039},{"cpu_ms":56.57399999999946,"wall_ms":70.56642579846084},{"cpu_ms":57.99999999999983,"wall_ms":74.89089109003544},{"cpu_ms":50.73999999999934,"wall_ms":80.02416510134935},{"cpu_ms":49.577000000000204,"wall_ms":74.29876597598195},{"cpu_ms":55.07599999999968,"wall_ms":82.08121592178941}],"before_median_cpu_ms":55.58749999999968,"after":[{"cpu_ms":54.696000000000296,"wall_ms":61.28125009126961},{"cpu_ms":53.765000000000285,"wall_ms":64.276261953637},{"cpu_ms":52.713000000000235,"wall_ms":62.10536905564368},{"cpu_ms":53.250999999999934,"wall_ms":81.45001693628728},{"cpu_ms":48.19299999999993,"wall_ms":55.708104046061635},{"cpu_ms":63.487999999999985,"wall_ms":133.75049899332225},{"cpu_ms":52.09799999999998,"wall_ms":67.08420510403812},{"cpu_ms":56.720000000000326,"wall_ms":70.57253806851804},{"cpu_ms":51.75799999999953,"wall_ms":59.167603962123394},{"cpu_ms":50.696000000000296,"wall_ms":71.62605482153594},{"cpu_ms":58.691999999999744,"wall_ms":84.9361871369183},{"cpu_ms":51.610000000000156,"wall_ms":68.47388995811343}],"after_median_cpu_ms":52.982000000000085,"before_control":[{"cpu_ms":52.353000000000094,"wall_ms":66.35381118394434},{"cpu_ms":62.285999999999845,"wall_ms":116.45868187770247},{"cpu_ms":52.363999999999855,"wall_ms":76.7569849267602},{"cpu_ms":55.22400000000039,"wall_ms":73.56964307837188},{"cpu_ms":52.185999999999844,"wall_ms":76.45213487558067},{"cpu_ms":58.948,"wall_ms":84.3391208909452},{"cpu_ms":44.24199999999967,"wall_ms":58.93477890640497},{"cpu_ms":49.8289999999999,"wall_ms":60.17935206182301},{"cpu_ms":54.45800000000034,"wall_ms":73.39542801491916},{"cpu_ms":47.429000000000165,"wall_ms":73.1223588809371},{"cpu_ms":56.91800000000047,"wall_ms":80.53695200942457},{"cpu_ms":56.79700000000043,"wall_ms":62.49672896228731}],"before_control_median_cpu_ms":53.4110000000001,"after_control":[{"cpu_ms":49.736000000000224,"wall_ms":55.47724291682243},{"cpu_ms":68.04899999999981,"wall_ms":82.8300341963768},{"cpu_ms":53.37000000000014,"wall_ms":62.597837997600436},{"cpu_ms":56.055999999999884,"wall_ms":81.02434314787388},{"cpu_ms":55.486000000000146,"wall_ms":81.61971415393054},{"cpu_ms":57.36499999999989,"wall_ms":127.9292800463736},{"cpu_ms":54.078000000000515,"wall_ms":65.88868307881057},{"cpu_ms":58.75900000000023,"wall_ms":91.5777578484267},{"cpu_ms":52.686000000000455,"wall_ms":75.9699260815978},{"cpu_ms":51.415999999999684,"wall_ms":61.96462595835328},{"cpu_ms":57.015999999999956,"wall_ms":69.62279509752989},{"cpu_ms":54.086999999999996,"wall_ms":69.29720705375075}],"after_control_median_cpu_ms":54.786500000000075},{"name":"buffer_transcode","node_exit":0,"before":[{"cpu_ms":67.83299999999937,"wall_ms":133.15759389661252},{"cpu_ms":79.09500000000058,"wall_ms":121.7160108499229},{"cpu_ms":75.42199999999966,"wall_ms":109.15840486995876},{"cpu_ms":78.41899999999846,"wall_ms":129.87103802151978},{"cpu_ms":74.40900000000106,"wall_ms":106.77097691223025},{"cpu_ms":69.12900000000022,"wall_ms":97.2486580722034},{"cpu_ms":77.11300000000065,"wall_ms":120.8191579207778},{"cpu_ms":74.81300000000068,"wall_ms":106.76433495245874},{"cpu_ms":72.38500000000059,"wall_ms":122.43279092945158},{"cpu_ms":74.67899999999972,"wall_ms":122.20633798278868},{"cpu_ms":78.4729999999989,"wall_ms":108.24595205485821},{"cpu_ms":73.73100000000044,"wall_ms":113.11945202760398}],"before_median_cpu_ms":74.74600000000021,"after":[{"cpu_ms":76.8210000000007,"wall_ms":125.26598013937473},{"cpu_ms":77.77800000000035,"wall_ms":127.90323398075998},{"cpu_ms":78.64600000000088,"wall_ms":101.0683299973607},{"cpu_ms":79.74500000000084,"wall_ms":140.0206491816789},{"cpu_ms":75.34300000000016,"wall_ms":98.01679104566574},{"cpu_ms":73.79399999999947,"wall_ms":126.55141996219754},{"cpu_ms":71.7219999999994,"wall_ms":107.54623496904969},{"cpu_ms":72.56399999999985,"wall_ms":97.81661815941334},{"cpu_ms":74.96999999999865,"wall_ms":101.49085894227028},{"cpu_ms":81.19600000000027,"wall_ms":154.1872718371451},{"cpu_ms":70.70400000000099,"wall_ms":149.95591109618545},{"cpu_ms":74.6349999999989,"wall_ms":138.78209795802832}],"after_median_cpu_ms":75.1564999999994,"before_control":[{"cpu_ms":77.52200000000009,"wall_ms":173.33035194315016},{"cpu_ms":74.87699999999987,"wall_ms":122.70473688840866},{"cpu_ms":75.0820000000001,"wall_ms":128.61131504178047},{"cpu_ms":78.4559999999992,"wall_ms":112.96496214345098},{"cpu_ms":70.60399999999944,"wall_ms":123.1181500479579},{"cpu_ms":75.61600000000013,"wall_ms":158.20929897017777},{"cpu_ms":68.96599999999964,"wall_ms":89.82919505797327},{"cpu_ms":73.31500000000091,"wall_ms":113.84535697288811},{"cpu_ms":74.10100000000064,"wall_ms":155.08832386694849},{"cpu_ms":72.79400000000003,"wall_ms":354.6487740240991},{"cpu_ms":80.07900000000134,"wall_ms":105.11635919101536},{"cpu_ms":75.2839999999999,"wall_ms":147.1580471843481}],"before_control_median_cpu_ms":74.97949999999997,"after_control":[{"cpu_ms":76.46200000000024,"wall_ms":117.96177178621292},{"cpu_ms":74.12699999999984,"wall_ms":122.87795101292431},{"cpu_ms":75.02599999999937,"wall_ms":128.70175507850945},{"cpu_ms":75.09600000000027,"wall_ms":100.56962887756526},{"cpu_ms":71.62499999999916,"wall_ms":115.92555092647672},{"cpu_ms":69.50300000000098,"wall_ms":113.33595612086356},{"cpu_ms":74.52400000000026,"wall_ms":92.97726186923683},{"cpu_ms":73.04899999999925,"wall_ms":132.77255580760539},{"cpu_ms":77.47499999999974,"wall_ms":109.0875631198287},{"cpu_ms":77.56799999999942,"wall_ms":112.45572310872376},{"cpu_ms":76.57599999999931,"wall_ms":99.60029902867973},{"cpu_ms":80.83400000000118,"wall_ms":109.93075789883733}],"after_control_median_cpu_ms":75.06099999999982},{"name":"date_format_parse","node_exit":0,"before":[{"cpu_ms":50.375999999999976,"wall_ms":68.54510493576527},{"cpu_ms":43.63500000000009,"wall_ms":47.08400391973555},{"cpu_ms":37.72999999999982,"wall_ms":45.463938964530826},{"cpu_ms":41.84999999999839,"wall_ms":65.27952780015767},{"cpu_ms":40.88300000000089,"wall_ms":46.841661212965846},{"cpu_ms":44.40299999999908,"wall_ms":98.71573909185827},{"cpu_ms":43.57899999999937,"wall_ms":66.70850608497858},{"cpu_ms":44.765999999999195,"wall_ms":66.41472806222737},{"cpu_ms":45.01499999999936,"wall_ms":67.32358504086733},{"cpu_ms":45.11400000000165,"wall_ms":66.40515988692641},{"cpu_ms":43.02099999999953,"wall_ms":64.28511207923293},{"cpu_ms":45.53300000000071,"wall_ms":63.64717008545995}],"before_median_cpu_ms":44.018999999999586,"after":[{"cpu_ms":44.435,"wall_ms":68.12200509011745},{"cpu_ms":38.63999999999912,"wall_ms":72.4883668590337},{"cpu_ms":39.560000000001594,"wall_ms":60.910739935934544},{"cpu_ms":39.37100000000093,"wall_ms":57.16943508014083},{"cpu_ms":39.63199999999922,"wall_ms":55.88919389992952},{"cpu_ms":42.66600000000054,"wall_ms":97.707306034863},{"cpu_ms":39.03800000000146,"wall_ms":65.31613692641258},{"cpu_ms":45.66200000000009,"wall_ms":70.61834703199565},{"cpu_ms":45.0569999999999,"wall_ms":59.235482942312956},{"cpu_ms":47.39700000000013,"wall_ms":70.27892279438674},{"cpu_ms":43.64499999999971,"wall_ms":58.73868498019874},{"cpu_ms":43.652999999999054,"wall_ms":72.91944301687181}],"after_median_cpu_ms":43.155500000000124,"before_control":[{"cpu_ms":47.02699999999993,"wall_ms":66.43749005161226},{"cpu_ms":46.42199999999974,"wall_ms":70.10252308100462},{"cpu_ms":44.92600000000024,"wall_ms":57.38895502872765},{"cpu_ms":40.54900000000039,"wall_ms":62.7974639646709},{"cpu_ms":38.24400000000061,"wall_ms":95.16385709866881},{"cpu_ms":43.56000000000115,"wall_ms":97.6964458823204},{"cpu_ms":45.97900000000088,"wall_ms":79.98911291360855},{"cpu_ms":45.094999999999885,"wall_ms":56.98813614435494},{"cpu_ms":45.00300000000124,"wall_ms":67.40533700212836},{"cpu_ms":43.50400000000043,"wall_ms":62.17548297718167},{"cpu_ms":45.0330000000001,"wall_ms":63.58847091905773},{"cpu_ms":46.384000000001535,"wall_ms":63.55037889443338}],"before_control_median_cpu_ms":45.01800000000067,"after_control":[{"cpu_ms":40.44000000000025,"wall_ms":65.69743785075843},{"cpu_ms":41.91500000000126,"wall_ms":89.52547912485898},{"cpu_ms":45.50899999999913,"wall_ms":56.97433999739587},{"cpu_ms":39.74099999999936,"wall_ms":66.71353592537344},{"cpu_ms":39.102999999999,"wall_ms":86.23020583763719},{"cpu_ms":43.217999999999535,"wall_ms":96.61565790884197},{"cpu_ms":43.48899999999922,"wall_ms":97.77773707173765},{"cpu_ms":44.10000000000025,"wall_ms":70.09908696636558},{"cpu_ms":45.371999999998636,"wall_ms":68.23323387652636},{"cpu_ms":45.674999999999244,"wall_ms":79.01659701019526},{"cpu_ms":42.740999999999474,"wall_ms":63.86398617178202},{"cpu_ms":42.27299999999978,"wall_ms":65.26434002444148}],"after_control_median_cpu_ms":42.979499999999504},{"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":74.70799999999755,"wall_ms":95.7406850066036},{"cpu_ms":81.63399999999754,"wall_ms":105.5589378811419},{"cpu_ms":81.67599999999808,"wall_ms":97.23253198899329},{"cpu_ms":80.69899999999919,"wall_ms":106.26427480019629},{"cpu_ms":80.93000000000217,"wall_ms":116.66539590805769},{"cpu_ms":90.18700000000024,"wall_ms":192.7089539822191},{"cpu_ms":87.89299999999756,"wall_ms":103.24257309548557},{"cpu_ms":86.5179999999981,"wall_ms":174.5049338787794},{"cpu_ms":91.15099999999998,"wall_ms":237.64539998956025},{"cpu_ms":85.00300000000038,"wall_ms":120.82149717025459},{"cpu_ms":84.422,"wall_ms":119.91236009635031},{"cpu_ms":90.1580000000024,"wall_ms":105.45319179072976}],"before_median_cpu_ms":84.71250000000019,"after":[{"cpu_ms":81.79400000000214,"wall_ms":96.32600890472531},{"cpu_ms":89.07800000000066,"wall_ms":96.46507794968784},{"cpu_ms":89.04000000000067,"wall_ms":122.52870388329029},{"cpu_ms":82.89900000000117,"wall_ms":95.0876260176301},{"cpu_ms":84.5729999999989,"wall_ms":108.06715092621744},{"cpu_ms":83.318000000002,"wall_ms":99.83362909406424},{"cpu_ms":94.53600000000151,"wall_ms":102.90102590806782},{"cpu_ms":88.80600000000172,"wall_ms":133.6796060204506},{"cpu_ms":90.48800000000057,"wall_ms":201.121092075482},{"cpu_ms":82.41200000000148,"wall_ms":140.87897911667824},{"cpu_ms":93.47199999999845,"wall_ms":124.37788816168904},{"cpu_ms":90.47199999999833,"wall_ms":109.66030787676573}],"after_median_cpu_ms":88.9230000000012,"before_control":[{"cpu_ms":85.05800000000008,"wall_ms":105.74915399774909},{"cpu_ms":75.42500000000274,"wall_ms":118.14507911913097},{"cpu_ms":86.62900000000207,"wall_ms":97.02096693217754},{"cpu_ms":80.77499999999915,"wall_ms":189.812364988029},{"cpu_ms":82.58700000000019,"wall_ms":97.05228498205543},{"cpu_ms":90.35400000000138,"wall_ms":191.8183919042349},{"cpu_ms":86.57100000000284,"wall_ms":122.8883359581232},{"cpu_ms":94.58599999999961,"wall_ms":123.09131491929293},{"cpu_ms":88.25099999999964,"wall_ms":222.01673896051943},{"cpu_ms":86.16400000000013,"wall_ms":103.61525090411305},{"cpu_ms":87.92800000000156,"wall_ms":99.90493184886873},{"cpu_ms":78.96200000000064,"wall_ms":101.66447795927525}],"before_control_median_cpu_ms":86.36750000000148,"after_control":[{"cpu_ms":81.48800000000023,"wall_ms":110.65442208200693},{"cpu_ms":85.00899999999945,"wall_ms":124.21411089599133},{"cpu_ms":84.71600000000024,"wall_ms":134.03186295181513},{"cpu_ms":86.35400000000004,"wall_ms":184.8582560196519},{"cpu_ms":83.16199999999796,"wall_ms":100.2201410010457},{"cpu_ms":86.29399999999876,"wall_ms":153.38970790617168},{"cpu_ms":87.34699999999762,"wall_ms":124.71793312579393},{"cpu_ms":90.65299999999965,"wall_ms":114.95474306866527},{"cpu_ms":89.87600000000029,"wall_ms":138.86688207276165},{"cpu_ms":89.93999999999858,"wall_ms":128.80406202748418},{"cpu_ms":87.21599999999796,"wall_ms":119.14808303117752},{"cpu_ms":86.19099999999946,"wall_ms":99.54383899457753}],"after_control_median_cpu_ms":86.3239999999994},{"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":148.13200000000037,"wall_ms":206.5529569517821},{"cpu_ms":144.15199999999828,"wall_ms":157.71232289262116},{"cpu_ms":141.51100000000127,"wall_ms":235.80562905408442},{"cpu_ms":142.66999999999896,"wall_ms":161.5209048613906},{"cpu_ms":129.74700000000183,"wall_ms":158.868522150442},{"cpu_ms":133.00299999999865,"wall_ms":225.53612501360476},{"cpu_ms":131.38899999999865,"wall_ms":144.92241200059652},{"cpu_ms":142.35299999999995,"wall_ms":209.4842130318284},{"cpu_ms":143.08099999999868,"wall_ms":167.28763398714364},{"cpu_ms":147.26300000000236,"wall_ms":211.2221100833267},{"cpu_ms":147.24800000000116,"wall_ms":163.6980320326984},{"cpu_ms":135.7609999999987,"wall_ms":189.06392599456012}],"before_median_cpu_ms":142.51149999999944,"after":[{"cpu_ms":142.3729999999992,"wall_ms":207.13917212560773},{"cpu_ms":146.32999999999896,"wall_ms":170.04824709147215},{"cpu_ms":143.44799999999935,"wall_ms":157.89782791398466},{"cpu_ms":138.16699999999926,"wall_ms":146.06051705777645},{"cpu_ms":128.13099999999977,"wall_ms":136.4595601335168},{"cpu_ms":141.23200000000224,"wall_ms":195.62560599297285},{"cpu_ms":146.11500000000177,"wall_ms":199.11616388708353},{"cpu_ms":142.13699999999818,"wall_ms":198.71079688891768},{"cpu_ms":137.46400000000136,"wall_ms":247.21373291686177},{"cpu_ms":147.05899999999872,"wall_ms":212.1503429953009},{"cpu_ms":133.30600000000103,"wall_ms":191.45597191527486},{"cpu_ms":132.58799999999837,"wall_ms":185.28683297336102}],"after_median_cpu_ms":141.6845000000002,"before_control":[{"cpu_ms":143.34500000000006,"wall_ms":184.2513659503311},{"cpu_ms":152.03799999999745,"wall_ms":174.4376120623201},{"cpu_ms":147.22700000000088,"wall_ms":157.80095802620053},{"cpu_ms":139.5489999999988,"wall_ms":171.05385405011475},{"cpu_ms":139.97499999999974,"wall_ms":150.39938897825778},{"cpu_ms":141.48699999999792,"wall_ms":200.60703693889081},{"cpu_ms":146.19300000000024,"wall_ms":202.60497787967324},{"cpu_ms":146.84899999999956,"wall_ms":220.81079403869808},{"cpu_ms":139.75200000000143,"wall_ms":208.2390789873898},{"cpu_ms":147.84900000000079,"wall_ms":206.2256101053208},{"cpu_ms":149.58000000000027,"wall_ms":217.46960491873324},{"cpu_ms":145.2600000000004,"wall_ms":158.74386089853942}],"before_control_median_cpu_ms":145.72650000000033,"after_control":[{"cpu_ms":145.13000000000176,"wall_ms":158.56651705689728},{"cpu_ms":140.19200000000254,"wall_ms":173.01111901178956},{"cpu_ms":141.52400000000043,"wall_ms":158.0304771196097},{"cpu_ms":140.30500000000146,"wall_ms":322.0715469215065},{"cpu_ms":138.32299999999975,"wall_ms":146.8272579368204},{"cpu_ms":140.1690000000002,"wall_ms":198.2815251685679},{"cpu_ms":128.31900000000118,"wall_ms":137.6208991277963},{"cpu_ms":145.9849999999996,"wall_ms":204.44950298406184},{"cpu_ms":148.01599999999837,"wall_ms":206.46324381232262},{"cpu_ms":147.1480000000014,"wall_ms":203.72179499827325},{"cpu_ms":146.8709999999973,"wall_ms":208.42638006433845},{"cpu_ms":123.55400000000216,"wall_ms":179.94486796669662}],"after_control_median_cpu_ms":140.91450000000094},{"name":"map_1m","node_exit":0,"before":[{"cpu_ms":340.3070000000028,"wall_ms":378.7521531339735},{"cpu_ms":335.56899999999956,"wall_ms":357.58205503225327},{"cpu_ms":334.2310000000026,"wall_ms":466.0304761491716},{"cpu_ms":331.16299999999654,"wall_ms":405.17373103648424},{"cpu_ms":348.0869999999996,"wall_ms":430.2736308891326},{"cpu_ms":325.25900000000263,"wall_ms":414.44561909884214},{"cpu_ms":338.1159999999994,"wall_ms":470.84698593243957},{"cpu_ms":341.4980000000014,"wall_ms":352.1941299550235},{"cpu_ms":343.32299999999805,"wall_ms":394.76565388031304},{"cpu_ms":338.27700000000505,"wall_ms":464.5484699867666},{"cpu_ms":350.74700000000547,"wall_ms":490.2310080360621},{"cpu_ms":346.5150000000037,"wall_ms":355.0712470896542}],"before_median_cpu_ms":339.2920000000039,"after":[{"cpu_ms":332.0730000000012,"wall_ms":354.4169820379466},{"cpu_ms":335.16999999999797,"wall_ms":366.3009339943528},{"cpu_ms":335.62700000000234,"wall_ms":362.61995206587017},{"cpu_ms":335.68700000000007,"wall_ms":415.08855507709086},{"cpu_ms":355.4420000000036,"wall_ms":379.1731628589332},{"cpu_ms":324.6899999999968,"wall_ms":362.8177160862833},{"cpu_ms":354.86900000000077,"wall_ms":485.15913099981844},{"cpu_ms":348.6249999999984,"wall_ms":379.78328694589436},{"cpu_ms":334.22900000000055,"wall_ms":358.67460211738944},{"cpu_ms":327.54899999999765,"wall_ms":349.4045538827777},{"cpu_ms":343.872999999995,"wall_ms":380.8869521599263},{"cpu_ms":334.60999999999785,"wall_ms":358.19170507602394}],"after_median_cpu_ms":335.3985000000001,"before_control":[{"cpu_ms":373.01699999999727,"wall_ms":780.9434519149363},{"cpu_ms":344.0440000000038,"wall_ms":401.19484905153513},{"cpu_ms":358.1179999999975,"wall_ms":367.8598760161549},{"cpu_ms":318.19300000000084,"wall_ms":462.559336097911},{"cpu_ms":314.29299999999927,"wall_ms":392.2807190101594},{"cpu_ms":336.8940000000009,"wall_ms":362.17726906761527},{"cpu_ms":339.5439999999965,"wall_ms":395.76320396736264},{"cpu_ms":360.7769999999988,"wall_ms":596.7631780076772},{"cpu_ms":325.5139999999983,"wall_ms":369.01293811388314},{"cpu_ms":341.4849999999987,"wall_ms":376.0456999298185},{"cpu_ms":370.3699999999941,"wall_ms":393.04532506503165},{"cpu_ms":334.1819999999984,"wall_ms":455.6263838894665}],"before_control_median_cpu_ms":340.5144999999976,"after_control":[{"cpu_ms":335.07099999999923,"wall_ms":361.9934010785073},{"cpu_ms":340.7330000000002,"wall_ms":393.9227098599076},{"cpu_ms":325.3780000000006,"wall_ms":372.85753805190325},{"cpu_ms":351.04499999999916,"wall_ms":521.4189121033996},{"cpu_ms":324.625999999995,"wall_ms":350.07316689006984},{"cpu_ms":348.64500000000476,"wall_ms":487.61334200389683},{"cpu_ms":327.0160000000004,"wall_ms":362.55933390930295},{"cpu_ms":349.6190000000041,"wall_ms":453.3476219512522},{"cpu_ms":330.6180000000012,"wall_ms":388.4477480314672},{"cpu_ms":335.3350000000006,"wall_ms":382.8792099375278},{"cpu_ms":338.74600000000044,"wall_ms":398.3191519510001},{"cpu_ms":333.49400000000173,"wall_ms":481.7244859877974}],"after_control_median_cpu_ms":335.2029999999999},{"name":"object_deep_clone","node_exit":0,"before":[{"cpu_ms":29.107000000003325,"wall_ms":41.61721211858094},{"cpu_ms":30.43699999999916,"wall_ms":37.4295418150723},{"cpu_ms":34.00800000000004,"wall_ms":41.78618686273694},{"cpu_ms":30.50400000000053,"wall_ms":45.585391111671925},{"cpu_ms":32.513999999999044,"wall_ms":35.916256019845605},{"cpu_ms":30.532999999998367,"wall_ms":41.16356489248574},{"cpu_ms":34.62899999999536,"wall_ms":38.028921000659466},{"cpu_ms":32.730000000000814,"wall_ms":45.300129102542996},{"cpu_ms":29.696000000001277,"wall_ms":45.97651096992195},{"cpu_ms":25.288999999993678,"wall_ms":33.242122968658805},{"cpu_ms":25.117999999999086,"wall_ms":49.13659207522869},{"cpu_ms":25.93699999999899,"wall_ms":39.53209798783064}],"before_median_cpu_ms":30.470499999999845,"after":[{"cpu_ms":26.870000000002392,"wall_ms":41.238793870434165},{"cpu_ms":32.22199999999731,"wall_ms":38.68650901131332},{"cpu_ms":27.60399999999663,"wall_ms":167.58413892239332},{"cpu_ms":30.70199999999801,"wall_ms":35.24674498476088},{"cpu_ms":29.8569999999998,"wall_ms":61.76058482378721},{"cpu_ms":30.242999999998688,"wall_ms":46.93443002179265},{"cpu_ms":29.892000000003804,"wall_ms":39.576370967552066},{"cpu_ms":33.56000000000137,"wall_ms":52.56314110010862},{"cpu_ms":33.01299999999685,"wall_ms":49.46966399438679},{"cpu_ms":25.914000000000215,"wall_ms":34.07292510382831},{"cpu_ms":31.514000000001374,"wall_ms":61.988500878214836},{"cpu_ms":30.307000000000528,"wall_ms":38.39660785160959}],"after_median_cpu_ms":30.274999999999608,"before_control":[{"cpu_ms":29.004000000000474,"wall_ms":44.85417506657541},{"cpu_ms":23.026000000001545,"wall_ms":38.87163498438895},{"cpu_ms":30.903000000002123,"wall_ms":40.64078815281391},{"cpu_ms":31.162999999999386,"wall_ms":42.302630841732025},{"cpu_ms":30.447000000002333,"wall_ms":36.791420076042414},{"cpu_ms":30.648999999996818,"wall_ms":36.13006486557424},{"cpu_ms":31.767999999999574,"wall_ms":37.374028004705906},{"cpu_ms":32.92700000000082,"wall_ms":51.48299387656152},{"cpu_ms":27.200000000000557,"wall_ms":42.370266979560256},{"cpu_ms":25.727000000003386,"wall_ms":35.20016884431243},{"cpu_ms":27.813000000001864,"wall_ms":35.5263480450958},{"cpu_ms":32.429000000000485,"wall_ms":38.84598705917597}],"before_control_median_cpu_ms":30.547999999999575,"after_control":[{"cpu_ms":21.963999999996986,"wall_ms":49.44496788084507},{"cpu_ms":32.83400000000114,"wall_ms":162.76275622658432},{"cpu_ms":34.44900000000217,"wall_ms":37.309347884729505},{"cpu_ms":28.88200000000296,"wall_ms":37.47591911815107},{"cpu_ms":30.560999999998728,"wall_ms":36.02604102343321},{"cpu_ms":30.390000000004136,"wall_ms":53.174772998318076},{"cpu_ms":33.3790000000036,"wall_ms":65.49094105139375},{"cpu_ms":37.89599999999638,"wall_ms":51.51323089376092},{"cpu_ms":28.067000000000064,"wall_ms":39.294194197282195},{"cpu_ms":27.169000000000665,"wall_ms":41.61633993498981},{"cpu_ms":29.485000000001094,"wall_ms":41.7773500084877},{"cpu_ms":33.68199999999888,"wall_ms":38.28211408108473}],"after_control_median_cpu_ms":30.475500000001432},{"name":"promise_all_chains","node_exit":0,"before":[{"cpu_ms":180.26900000000268,"wall_ms":225.1365790143609},{"cpu_ms":179.28500000000014,"wall_ms":241.12103693187237},{"cpu_ms":172.03600000000563,"wall_ms":221.0939519573003},{"cpu_ms":170.535000000001,"wall_ms":188.66248102858663},{"cpu_ms":179.5849999999959,"wall_ms":259.28283180110157},{"cpu_ms":180.14399999999853,"wall_ms":201.12564507871866},{"cpu_ms":178.89000000000266,"wall_ms":236.51861492544413},{"cpu_ms":191.1349999999956,"wall_ms":306.7082520574331},{"cpu_ms":197.58499999999657,"wall_ms":227.41992608644068},{"cpu_ms":185.53799999999399,"wall_ms":217.31620002537966},{"cpu_ms":178.71600000000853,"wall_ms":197.97960203140974},{"cpu_ms":183.70500000000334,"wall_ms":245.79011113382876}],"before_median_cpu_ms":179.8644999999972,"after":[{"cpu_ms":182.12799999999874,"wall_ms":211.89158596098423},{"cpu_ms":184.76900000000285,"wall_ms":253.36599303409457},{"cpu_ms":176.48399999999498,"wall_ms":220.45781300403178},{"cpu_ms":176.39900000000353,"wall_ms":202.9117289930582},{"cpu_ms":180.52100000000593,"wall_ms":245.59383979067206},{"cpu_ms":181.99600000000515,"wall_ms":234.96400192379951},{"cpu_ms":180.0840000000008,"wall_ms":223.35489396937191},{"cpu_ms":188.48200000000048,"wall_ms":288.3431389927864},{"cpu_ms":181.6040000000001,"wall_ms":208.88552581891418},{"cpu_ms":196.58599999999637,"wall_ms":469.43352394737303},{"cpu_ms":177.15599999999654,"wall_ms":225.27664410881698},{"cpu_ms":180.99399999999832,"wall_ms":218.31862209364772}],"after_median_cpu_ms":181.2989999999992,"before_control":[{"cpu_ms":182.3789999999974,"wall_ms":260.7535468414426},{"cpu_ms":174.94399999999644,"wall_ms":232.97596792690456},{"cpu_ms":176.29699999999815,"wall_ms":243.58542007394135},{"cpu_ms":172.92099999999522,"wall_ms":252.5525640230626},{"cpu_ms":176.6059999999996,"wall_ms":256.5340190194547},{"cpu_ms":176.63399999999996,"wall_ms":221.63367993198335},{"cpu_ms":176.565999999994,"wall_ms":205.80442203208804},{"cpu_ms":179.427000000004,"wall_ms":212.6191679853946},{"cpu_ms":194.84400000000335,"wall_ms":223.67408080026507},{"cpu_ms":182.89399999999745,"wall_ms":212.30867900885642},{"cpu_ms":179.07099999999332,"wall_ms":232.23966686055064},{"cpu_ms":171.72899999999913,"wall_ms":208.8469360023737}],"before_control_median_cpu_ms":176.61999999999978,"after_control":[{"cpu_ms":183.2550000000026,"wall_ms":264.44596122018993},{"cpu_ms":183.25300000000055,"wall_ms":283.78729801625013},{"cpu_ms":190.56099999999532,"wall_ms":280.45765100978315},{"cpu_ms":177.7879999999996,"wall_ms":232.40823904052377},{"cpu_ms":175.8790000000019,"wall_ms":201.01482700556517},{"cpu_ms":188.49699999999814,"wall_ms":233.93294704146683},{"cpu_ms":172.62300000000153,"wall_ms":208.76429905183613},{"cpu_ms":191.75200000000103,"wall_ms":357.74365183897316},{"cpu_ms":176.92699999999917,"wall_ms":213.87955197133124},{"cpu_ms":186.08000000000402,"wall_ms":270.9104670211673},{"cpu_ms":179.04300000000717,"wall_ms":201.98374008759856},{"cpu_ms":182.82499999999402,"wall_ms":210.9594999346882}],"after_control_median_cpu_ms":183.0389999999973},{"name":"regex_replace","node_exit":0,"before":[{"cpu_ms":4199.501999999995,"wall_ms":5620.470701018348},{"cpu_ms":3688.5429999999956,"wall_ms":3750.0289459712803},{"cpu_ms":3632.4010000000017,"wall_ms":3759.8297020886093},{"cpu_ms":4134.792000000019,"wall_ms":5506.50012306869},{"cpu_ms":4079.876000000013,"wall_ms":4393.4881060849875},{"cpu_ms":4355.423000000002,"wall_ms":4542.580202920362},{"cpu_ms":4296.570000000002,"wall_ms":4596.721714828163},{"cpu_ms":4174.970000000002,"wall_ms":4425.225069979206},{"cpu_ms":4261.7850000000035,"wall_ms":5603.206729982048},{"cpu_ms":4066.1379999999954,"wall_ms":5622.0511791761965},{"cpu_ms":4283.388000000003,"wall_ms":5913.105763029307},{"cpu_ms":4126.686000000007,"wall_ms":5662.920002825558}],"before_median_cpu_ms":4154.88100000001,"after":[{"cpu_ms":3486.2490000000007,"wall_ms":4649.886957136914},{"cpu_ms":2913.188000000005,"wall_ms":3007.6400828547776},{"cpu_ms":3281.2319999999886,"wall_ms":3764.2162658739835},{"cpu_ms":3330.281999999983,"wall_ms":3798.051996855065},{"cpu_ms":3308.4109999999782,"wall_ms":3356.147925835103},{"cpu_ms":3383.862999999991,"wall_ms":3504.0867931675166},{"cpu_ms":3269.120999999984,"wall_ms":3871.7639499809593},{"cpu_ms":3396.6120000000046,"wall_ms":3660.25273501873},{"cpu_ms":3560.2159999999967,"wall_ms":4961.718082195148},{"cpu_ms":3481.14799999999,"wall_ms":3984.526978805661},{"cpu_ms":3326.4800000000037,"wall_ms":4388.866074150428},{"cpu_ms":3677.806000000004,"wall_ms":4760.389807866886}],"after_median_cpu_ms":3357.072499999987,"before_control":[{"cpu_ms":4083.340000000007,"wall_ms":5047.321085818112},{"cpu_ms":4023.24200000001,"wall_ms":4930.6467671412975},{"cpu_ms":3398.2390000000037,"wall_ms":3532.1898360271007},{"cpu_ms":4130.713000000014,"wall_ms":4216.233466053382},{"cpu_ms":3918.2679999999837,"wall_ms":4272.503596032038},{"cpu_ms":4324.258000000014,"wall_ms":4627.919859020039},{"cpu_ms":4055.312000000015,"wall_ms":4278.464739909396},{"cpu_ms":4128.282999999982,"wall_ms":5773.514995118603},{"cpu_ms":4105.778000000015,"wall_ms":5340.35504097119},{"cpu_ms":4218.985000000003,"wall_ms":6109.878859017044},{"cpu_ms":4365.144999999984,"wall_ms":6244.823816930875},{"cpu_ms":4259.784999999965,"wall_ms":4393.12378782779}],"before_control_median_cpu_ms":4117.030499999999,"after_control":[{"cpu_ms":3585.276999999991,"wall_ms":4345.110927941278},{"cpu_ms":3311.094999999995,"wall_ms":3515.192869119346},{"cpu_ms":3090.947,"wall_ms":3186.9247080758214},{"cpu_ms":3429.979000000003,"wall_ms":3682.2865619324148},{"cpu_ms":3281.8550000000073,"wall_ms":3395.616044057533},{"cpu_ms":3516.472999999991,"wall_ms":3572.150914929807},{"cpu_ms":3271.4359999999942,"wall_ms":3604.078280972317},{"cpu_ms":3374.6570000000133,"wall_ms":4198.473405092955},{"cpu_ms":3344.413000000003,"wall_ms":4646.995759103447},{"cpu_ms":3421.6729999999984,"wall_ms":4215.710018062964},{"cpu_ms":3594.2070000000112,"wall_ms":5140.212700935081},{"cpu_ms":3534.0360000000146,"wall_ms":4502.556218067184}],"after_control_median_cpu_ms":3398.165000000006},{"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":46.53200000001334,"wall_ms":61.73537205904722},{"cpu_ms":47.513999999978296,"wall_ms":63.303461065515876},{"cpu_ms":46.277000000031876,"wall_ms":54.781954968348145},{"cpu_ms":49.165000000016335,"wall_ms":60.95678801648319},{"cpu_ms":48.65100000000666,"wall_ms":61.00769294425845},{"cpu_ms":47.808000000031825,"wall_ms":62.032636953517795},{"cpu_ms":47.13299999997389,"wall_ms":59.655918972566724},{"cpu_ms":46.34899999996378,"wall_ms":61.90700293518603},{"cpu_ms":40.24199999997791,"wall_ms":49.433925887569785},{"cpu_ms":47.01300000004949,"wall_ms":50.41836597956717},{"cpu_ms":43.32500000003847,"wall_ms":48.62404498271644},{"cpu_ms":42.06200000004401,"wall_ms":48.452284187078476}],"before_median_cpu_ms":46.772500000031414,"after":[{"cpu_ms":47.18099999996639,"wall_ms":59.6046419814229},{"cpu_ms":47.82800000003817,"wall_ms":62.092348001897335},{"cpu_ms":48.68499999997766,"wall_ms":62.04609200358391},{"cpu_ms":46.9449999999938,"wall_ms":61.36550102382898},{"cpu_ms":41.123999999967964,"wall_ms":56.49076704867184},{"cpu_ms":48.37099999997463,"wall_ms":107.98168601468205},{"cpu_ms":50.02100000001519,"wall_ms":65.51497080363333},{"cpu_ms":39.86000000003287,"wall_ms":56.320167845115066},{"cpu_ms":46.3839999999891,"wall_ms":56.73758778721094},{"cpu_ms":45.66599999998289,"wall_ms":51.93559895269573},{"cpu_ms":42.747999999960484,"wall_ms":50.24235602468252},{"cpu_ms":43.76600000000508,"wall_ms":50.148071022704244}],"after_median_cpu_ms":46.66449999999145,"before_control":[{"cpu_ms":50.149999999973716,"wall_ms":60.61669206246734},{"cpu_ms":47.89599999998018,"wall_ms":59.19171194545925},{"cpu_ms":47.21100000000433,"wall_ms":54.57914597354829},{"cpu_ms":47.3660000000109,"wall_ms":60.734722996130586},{"cpu_ms":47.16400000000931,"wall_ms":59.58446487784386},{"cpu_ms":47.37699999998313,"wall_ms":54.729798110201955},{"cpu_ms":48.78800000000183,"wall_ms":60.16998807899654},{"cpu_ms":48.446000000012646,"wall_ms":61.04509299620986},{"cpu_ms":49.3720000000053,"wall_ms":54.795741103589535},{"cpu_ms":45.14500000004773,"wall_ms":53.573318058624864},{"cpu_ms":46.09799999997222,"wall_ms":51.58498394303024},{"cpu_ms":42.7670000000262,"wall_ms":49.172958824783564}],"before_control_median_cpu_ms":47.37149999999701,"after_control":[{"cpu_ms":47.62000000005173,"wall_ms":58.07077814824879},{"cpu_ms":47.46899999997822,"wall_ms":61.943329870700836},{"cpu_ms":50.36899999998923,"wall_ms":60.84628100506961},{"cpu_ms":47.44199999998955,"wall_ms":59.531555976718664},{"cpu_ms":49.31800000002795,"wall_ms":60.85030804388225},{"cpu_ms":42.84300000000485,"wall_ms":55.199805879965425},{"cpu_ms":49.93500000000495,"wall_ms":60.404927004128695},{"cpu_ms":45.81999999999198,"wall_ms":61.216193018481135},{"cpu_ms":48.004999999989195,"wall_ms":54.783127969130874},{"cpu_ms":44.297999999969306,"wall_ms":51.45442998036742},{"cpu_ms":43.79799999998113,"wall_ms":49.18029299005866},{"cpu_ms":42.634999999961565,"wall_ms":48.979568062350154}],"after_control_median_cpu_ms":47.455499999983886},{"name":"string_split_map_join","node_exit":0,"before":[{"cpu_ms":210.64899999998943,"wall_ms":214.0643848106265},{"cpu_ms":169.083999999998,"wall_ms":175.20632990635931},{"cpu_ms":171.703999999977,"wall_ms":176.93961807526648},{"cpu_ms":166.84199999997418,"wall_ms":199.41294193267822},{"cpu_ms":168.63499999999476,"wall_ms":179.62802993133664},{"cpu_ms":173.04400000000442,"wall_ms":326.32024586200714},{"cpu_ms":180.69500000001426,"wall_ms":200.67681302316487},{"cpu_ms":177.49199999997245,"wall_ms":190.94715104438365},{"cpu_ms":189.49700000001712,"wall_ms":228.39784785173833},{"cpu_ms":172.92900000001055,"wall_ms":267.53631699830294},{"cpu_ms":169.39300000001367,"wall_ms":257.5270519591868},{"cpu_ms":173.95199999998567,"wall_ms":220.73645889759064}],"before_median_cpu_ms":172.98650000000748,"after":[{"cpu_ms":183.9390000000094,"wall_ms":190.27479295618832},{"cpu_ms":199.58400000001575,"wall_ms":206.9061459042132},{"cpu_ms":173.0110000000309,"wall_ms":178.30640217289329},{"cpu_ms":182.391999999993,"wall_ms":187.75179283693433},{"cpu_ms":176.07699999996385,"wall_ms":186.73035502433777},{"cpu_ms":194.02999999999793,"wall_ms":200.45520807616413},{"cpu_ms":173.3560000000125,"wall_ms":187.78914213180542},{"cpu_ms":190.84200000003193,"wall_ms":211.09097404405475},{"cpu_ms":197.8099999999472,"wall_ms":227.37823287025094},{"cpu_ms":236.19899999999916,"wall_ms":248.86940303258598},{"cpu_ms":209.25999999997202,"wall_ms":279.3151568621397},{"cpu_ms":172.59100000001126,"wall_ms":424.7456172015518}],"after_median_cpu_ms":187.39050000002067,"before_control":[{"cpu_ms":207.41999999995642,"wall_ms":212.7192229963839},{"cpu_ms":180.6980000000067,"wall_ms":185.07473194040358},{"cpu_ms":204.13100000001805,"wall_ms":209.4460001681},{"cpu_ms":171.37000000002445,"wall_ms":176.7781071830541},{"cpu_ms":180.09100000000444,"wall_ms":331.28856495022774},{"cpu_ms":205.0249999999778,"wall_ms":247.08750401623547},{"cpu_ms":164.87000000000762,"wall_ms":181.42883386462927},{"cpu_ms":168.6990000000037,"wall_ms":196.49349013343453},{"cpu_ms":209.76899999999432,"wall_ms":242.21694795414805},{"cpu_ms":277.35000000001264,"wall_ms":335.15484794043005},{"cpu_ms":213.33900000001904,"wall_ms":239.0322401188314},{"cpu_ms":217.69900000003872,"wall_ms":294.54594291746616}],"before_control_median_cpu_ms":204.57799999999793,"after_control":[{"cpu_ms":166.83199999999943,"wall_ms":173.1474760454148},{"cpu_ms":166.78300000000945,"wall_ms":172.1257569734007},{"cpu_ms":217.24999999997863,"wall_ms":223.28266291879117},{"cpu_ms":167.38800000001675,"wall_ms":174.70059706829488},{"cpu_ms":190.4800000000364,"wall_ms":226.0387900751084},{"cpu_ms":193.89799999999013,"wall_ms":209.02027585543692},{"cpu_ms":172.35099999999193,"wall_ms":484.4455460552126},{"cpu_ms":165.65800000000763,"wall_ms":188.027256866917},{"cpu_ms":203.8220000000024,"wall_ms":228.78701402805746},{"cpu_ms":175.04600000000892,"wall_ms":343.7377989757806},{"cpu_ms":170.92199999996183,"wall_ms":183.29763412475586},{"cpu_ms":176.94499999998925,"wall_ms":292.7077601198107}],"after_control_median_cpu_ms":173.69850000000042},{"name":"string_template_interp","node_exit":0,"before":[{"cpu_ms":63.31999999997606,"wall_ms":273.649106035009},{"cpu_ms":48.05800000002591,"wall_ms":61.702653067186475},{"cpu_ms":47.57599999999229,"wall_ms":59.21655986458063},{"cpu_ms":54.01399999999512,"wall_ms":60.32870593480766},{"cpu_ms":43.92799999999397,"wall_ms":50.22074794396758},{"cpu_ms":44.33900000003632,"wall_ms":67.86881014704704},{"cpu_ms":45.163000000002285,"wall_ms":73.6384151969105},{"cpu_ms":48.76400000000558,"wall_ms":80.5169721134007},{"cpu_ms":47.426000000029944,"wall_ms":60.233678901568055},{"cpu_ms":43.275999999991654,"wall_ms":52.65536508522928},{"cpu_ms":44.263999999998305,"wall_ms":54.63001294992864},{"cpu_ms":46.387999999979,"wall_ms":55.81288295798004}],"before_median_cpu_ms":46.90700000000447,"after":[{"cpu_ms":57.11900000000014,"wall_ms":142.50811305828393},{"cpu_ms":47.23599999999806,"wall_ms":54.7027790453285},{"cpu_ms":47.395999999992,"wall_ms":90.16175591386855},{"cpu_ms":45.06800000001476,"wall_ms":50.385907059535384},{"cpu_ms":41.574000000025535,"wall_ms":49.78683707304299},{"cpu_ms":45.05599999998822,"wall_ms":59.94952796027064},{"cpu_ms":47.685999999998785,"wall_ms":77.83764787018299},{"cpu_ms":51.93700000000945,"wall_ms":83.45591090619564},{"cpu_ms":47.43199999995795,"wall_ms":78.30744609236717},{"cpu_ms":49.83500000003005,"wall_ms":63.14067798666656},{"cpu_ms":45.40900000000647,"wall_ms":61.56686902977526},{"cpu_ms":45.511000000033164,"wall_ms":59.0631531085819}],"after_median_cpu_ms":47.31599999999503,"before_control":[{"cpu_ms":58.33499999999958,"wall_ms":173.0265631340444},{"cpu_ms":52.02699999995275,"wall_ms":124.93619415909052},{"cpu_ms":52.59599999999409,"wall_ms":56.90650502219796},{"cpu_ms":42.51899999997022,"wall_ms":52.74562421254814},{"cpu_ms":43.81200000000263,"wall_ms":50.07867282256484},{"cpu_ms":50.169999999980064,"wall_ms":107.89045784622431},{"cpu_ms":46.206999999981235,"wall_ms":142.65355607494712},{"cpu_ms":52.05700000004754,"wall_ms":111.51238298043609},{"cpu_ms":48.86599999997543,"wall_ms":57.1634650696069},{"cpu_ms":47.00900000000274,"wall_ms":62.41733720526099},{"cpu_ms":45.43100000000777,"wall_ms":55.018188897520304},{"cpu_ms":52.120000000002165,"wall_ms":68.9045439939946}],"before_control_median_cpu_ms":49.51799999997775,"after_control":[{"cpu_ms":48.98300000002109,"wall_ms":118.26735897921026},{"cpu_ms":46.683000000030006,"wall_ms":52.98598320223391},{"cpu_ms":52.35699999997223,"wall_ms":60.59033493511379},{"cpu_ms":43.943000000012944,"wall_ms":51.26990703865886},{"cpu_ms":53.104000000018914,"wall_ms":68.97920300252736},{"cpu_ms":53.55900000000702,"wall_ms":75.6236428860575},{"cpu_ms":47.15499999997519,"wall_ms":74.9066830612719},{"cpu_ms":52.78299999997671,"wall_ms":95.97253892570734},{"cpu_ms":50.3360000000157,"wall_ms":61.1091039609164},{"cpu_ms":51.35899999999083,"wall_ms":65.78895496204495},{"cpu_ms":46.99399999998377,"wall_ms":61.458386946469545},{"cpu_ms":44.493999999986045,"wall_ms":51.7076849937439}],"after_control_median_cpu_ms":49.6595000000184}]}, +{"app_controls":[{"name":"promise_all_chains","node_exit":0,"before":[{"cpu_ms":108.97300000000021,"wall_ms":110.28809705749154},{"cpu_ms":103.86500000000032,"wall_ms":110.18884205259383},{"cpu_ms":102.82400000000003,"wall_ms":109.17115095071495},{"cpu_ms":150.95800000000014,"wall_ms":156.26292885281146},{"cpu_ms":115.42800000000054,"wall_ms":122.6971847936511},{"cpu_ms":117.87599999999986,"wall_ms":126.55464000999928},{"cpu_ms":104.3649999999996,"wall_ms":111.77277006208897},{"cpu_ms":128.46699999999967,"wall_ms":135.81497687846422},{"cpu_ms":142.9980000000004,"wall_ms":148.39241886511445},{"cpu_ms":124.77500000000052,"wall_ms":131.09059212729335},{"cpu_ms":138.2059999999994,"wall_ms":145.53891913965344},{"cpu_ms":96.8359999999997,"wall_ms":102.13703312911093},{"cpu_ms":122.27299999999985,"wall_ms":124.57513296976686},{"cpu_ms":141.21000000000095,"wall_ms":143.57881899923086},{"cpu_ms":103.06199999999954,"wall_ms":110.45159306377172},{"cpu_ms":102.94999999999987,"wall_ms":107.36225289292634},{"cpu_ms":112.22199999999916,"wall_ms":116.56181584112346},{"cpu_ms":133.00100000000015,"wall_ms":137.368889991194},{"cpu_ms":119.79699999999838,"wall_ms":125.1938990317285},{"cpu_ms":103.5029999999999,"wall_ms":109.85393705777824},{"cpu_ms":173.921,"wall_ms":180.2998469211161},{"cpu_ms":107.16999999999999,"wall_ms":116.4481642190367},{"cpu_ms":119.4500000000005,"wall_ms":127.8332700021565},{"cpu_ms":116.88400000000065,"wall_ms":125.69101806730032},{"cpu_ms":118.34900000000026,"wall_ms":122.7140580303967},{"cpu_ms":106.2530000000006,"wall_ms":310.9707979019731},{"cpu_ms":116.29100000000037,"wall_ms":264.6902040578425},{"cpu_ms":114.12499999999959,"wall_ms":449.7334221377969},{"cpu_ms":100.63000000000066,"wall_ms":105.05066602490842},{"cpu_ms":110.97499999999982,"wall_ms":119.57736895419657},{"cpu_ms":123.93399999999843,"wall_ms":130.35130384378135},{"cpu_ms":112.93600000000126,"wall_ms":226.8062538933009},{"cpu_ms":126.57099999999843,"wall_ms":135.60904399491847},{"cpu_ms":110.73000000000022,"wall_ms":118.11895598657429},{"cpu_ms":117.15500000000034,"wall_ms":304.17581810615957},{"cpu_ms":122.97599999999775,"wall_ms":129.3845579493791},{"cpu_ms":96.3169999999991,"wall_ms":104.65763206593692},{"cpu_ms":145.9210000000013,"wall_ms":161.33355302736163},{"cpu_ms":166.2329999999983,"wall_ms":174.60928088985384},{"cpu_ms":182.26999999999904,"wall_ms":187.7507669851184},{"cpu_ms":167.29899999999986,"wall_ms":173.79659321159124},{"cpu_ms":163.20499999999782,"wall_ms":170.9296889603138},{"cpu_ms":167.2740000000026,"wall_ms":178.13260201364756},{"cpu_ms":122.42600000000081,"wall_ms":128.76884918659925},{"cpu_ms":170.29699999999792,"wall_ms":179.72812219522893},{"cpu_ms":118.38400000000249,"wall_ms":125.81678503192961},{"cpu_ms":176.27200000000087,"wall_ms":186.6927209775895},{"cpu_ms":149.93199999999973,"wall_ms":154.2494238819927},{"cpu_ms":174.7160000000001,"wall_ms":182.1866249665618},{"cpu_ms":139.42800000000233,"wall_ms":146.85549191199243},{"cpu_ms":177.3750000000014,"wall_ms":183.78091300837696},{"cpu_ms":165.81899999999905,"wall_ms":177.5019750930369},{"cpu_ms":159.76500000000016,"wall_ms":175.30065402388573},{"cpu_ms":161.48099999999843,"wall_ms":168.2662651874125},{"cpu_ms":178.801,"wall_ms":188.68109583854675},{"cpu_ms":182.1230000000007,"wall_ms":196.18364004418254},{"cpu_ms":160.37899999999894,"wall_ms":171.10520810820162},{"cpu_ms":191.31600000000049,"wall_ms":207.50767085701227},{"cpu_ms":163.60600000000147,"wall_ms":176.06249707750976},{"cpu_ms":179.2429999999996,"wall_ms":186.29894708283246},{"cpu_ms":132.4860000000001,"wall_ms":144.38259392045438},{"cpu_ms":178.5499999999942,"wall_ms":181.9624078925699},{"cpu_ms":157.48799999999363,"wall_ms":163.9018328860402},{"cpu_ms":100.89800000000082,"wall_ms":104.30706012994051},{"cpu_ms":173.07199999999767,"wall_ms":179.36890409328043},{"cpu_ms":176.42299999999977,"wall_ms":182.81827704049647},{"cpu_ms":113.03200000000402,"wall_ms":122.45454592630267},{"cpu_ms":161.7740000000012,"wall_ms":169.30887405760586},{"cpu_ms":132.31000000000392,"wall_ms":139.7713217884302},{"cpu_ms":101.88499999999578,"wall_ms":113.07392502203584},{"cpu_ms":110.0319999999968,"wall_ms":114.48011198081076},{"cpu_ms":157.68299999999869,"wall_ms":217.3879980109632},{"cpu_ms":191.70700000000096,"wall_ms":261.750329984352},{"cpu_ms":180.54300000000012,"wall_ms":203.8778441492468},{"cpu_ms":189.03099999999995,"wall_ms":267.5334229134023},{"cpu_ms":195.15599999999722,"wall_ms":407.79590397141874},{"cpu_ms":183.35499999999882,"wall_ms":270.81177686341107},{"cpu_ms":183.73199999999912,"wall_ms":295.46347283758223},{"cpu_ms":189.83699999999715,"wall_ms":304.7109670005739},{"cpu_ms":176.4750000000035,"wall_ms":233.71416493318975},{"cpu_ms":180.76399999999637,"wall_ms":199.79399698786438},{"cpu_ms":200.5969999999948,"wall_ms":278.3801849000156},{"cpu_ms":177.07099999999798,"wall_ms":243.1524449493736},{"cpu_ms":180.66099999999352,"wall_ms":197.91735196486115},{"cpu_ms":191.34899999999533,"wall_ms":224.0131760481745},{"cpu_ms":172.15699999999856,"wall_ms":197.27963185869157},{"cpu_ms":176.13399999999757,"wall_ms":198.69253085926175},{"cpu_ms":178.23399999999623,"wall_ms":263.15498794429004},{"cpu_ms":191.3020000000003,"wall_ms":207.79062598012388},{"cpu_ms":169.3610000000021,"wall_ms":221.04579396545887},{"cpu_ms":181.62000000000234,"wall_ms":223.979172995314},{"cpu_ms":184.79100000000415,"wall_ms":237.95839515514672},{"cpu_ms":175.47199999999918,"wall_ms":192.01955804601312},{"cpu_ms":163.12500000000085,"wall_ms":169.68638193793595},{"cpu_ms":148.87900000000087,"wall_ms":156.5661057829857},{"cpu_ms":188.4160000000037,"wall_ms":193.08814383111894},{"cpu_ms":177.70900000000012,"wall_ms":186.61360000260174},{"cpu_ms":179.37099999999617,"wall_ms":212.45515998452902},{"cpu_ms":173.62599999999873,"wall_ms":194.09566605463624},{"cpu_ms":177.05200000000332,"wall_ms":193.70184116996825}],"before_median_cpu_ms":158.72399999999942,"after":[{"cpu_ms":100.1749999999999,"wall_ms":105.45012005604804},{"cpu_ms":121.76399999999975,"wall_ms":124.11160697229207},{"cpu_ms":135.50700000000003,"wall_ms":143.67539808154106},{"cpu_ms":113.42499999999988,"wall_ms":118.76129382289946},{"cpu_ms":107.97999999999996,"wall_ms":113.35660796612501},{"cpu_ms":117.13700000000048,"wall_ms":122.45384999550879},{"cpu_ms":125.21700000000013,"wall_ms":128.51805705577135},{"cpu_ms":106.05500000000046,"wall_ms":111.46246991120279},{"cpu_ms":116.04299999999945,"wall_ms":122.48300504870713},{"cpu_ms":137.42399999999932,"wall_ms":142.8133479785174},{"cpu_ms":103.33700000000067,"wall_ms":107.6625941786915},{"cpu_ms":137.35300000000007,"wall_ms":146.7183621134609},{"cpu_ms":102.94000000000025,"wall_ms":111.22319498099387},{"cpu_ms":105.7389999999998,"wall_ms":110.08622008375823},{"cpu_ms":114.00700000000086,"wall_ms":119.40154992043972},{"cpu_ms":114.57599999999957,"wall_ms":117.95303388498724},{"cpu_ms":121.07000000000134,"wall_ms":125.46610692515969},{"cpu_ms":153.16200000000003,"wall_ms":160.50624614581466},{"cpu_ms":109.51000000000022,"wall_ms":116.85193795710802},{"cpu_ms":116.00699999999975,"wall_ms":123.42618708498776},{"cpu_ms":115.11699999999969,"wall_ms":121.42051220871508},{"cpu_ms":114.55500000000107,"wall_ms":119.00409800000489},{"cpu_ms":141.4559999999998,"wall_ms":148.81170401349664},{"cpu_ms":133.34099999999972,"wall_ms":137.66677607782185},{"cpu_ms":108.76399999999897,"wall_ms":118.96778107620776},{"cpu_ms":109.64000000000063,"wall_ms":290.5684500001371},{"cpu_ms":116.50500000000008,"wall_ms":259.8453350365162},{"cpu_ms":117.9900000000007,"wall_ms":279.3431030586362},{"cpu_ms":101.14799999999846,"wall_ms":436.92698306404054},{"cpu_ms":112.55799999999994,"wall_ms":119.96514303609729},{"cpu_ms":111.80099999999982,"wall_ms":355.3235619328916},{"cpu_ms":107.22099999999912,"wall_ms":125.35654404200613},{"cpu_ms":96.0390000000011,"wall_ms":102.37388592213392},{"cpu_ms":121.93500000000057,"wall_ms":132.30328913778067},{"cpu_ms":104.20700000000238,"wall_ms":200.55915997363627},{"cpu_ms":97.46000000000166,"wall_ms":104.88539794459939},{"cpu_ms":116.65100000000095,"wall_ms":124.00959897786379},{"cpu_ms":184.59499999999807,"wall_ms":190.29447785578668},{"cpu_ms":157.29600000000232,"wall_ms":167.66460915096104},{"cpu_ms":179.64900000000128,"wall_ms":185.4449501261115},{"cpu_ms":168.61000000000104,"wall_ms":182.02199693769217},{"cpu_ms":131.25900000000001,"wall_ms":136.69312908314168},{"cpu_ms":164.70699999999994,"wall_ms":169.81720994226635},{"cpu_ms":181.94599999999994,"wall_ms":186.38808210380375},{"cpu_ms":148.0150000000009,"wall_ms":154.40489305183291},{"cpu_ms":125.95400000000012,"wall_ms":131.31929305382073},{"cpu_ms":130.78799999999902,"wall_ms":138.17146187648177},{"cpu_ms":117.66900000000291,"wall_ms":121.99857411906123},{"cpu_ms":151.2129999999985,"wall_ms":158.71630609035492},{"cpu_ms":177.623999999998,"wall_ms":187.1768559794873},{"cpu_ms":168.91000000000034,"wall_ms":182.64472112059593},{"cpu_ms":174.866999999999,"wall_ms":183.7345960084349},{"cpu_ms":169.4330000000015,"wall_ms":178.9964670315385},{"cpu_ms":176.1790000000012,"wall_ms":184.68242790549994},{"cpu_ms":180.87600000000137,"wall_ms":190.43807801790535},{"cpu_ms":179.8579999999994,"wall_ms":189.94975602254272},{"cpu_ms":171.62599999999983,"wall_ms":189.830563031137},{"cpu_ms":184.09100000000223,"wall_ms":193.65424592979252},{"cpu_ms":182.93200000000098,"wall_ms":196.93992310203612},{"cpu_ms":172.8120000000004,"wall_ms":181.15008994936943},{"cpu_ms":166.29299999999603,"wall_ms":180.77436788007617},{"cpu_ms":177.34899999999953,"wall_ms":182.95822804793715},{"cpu_ms":181.9240000000022,"wall_ms":186.3254769705236},{"cpu_ms":156.01900000000057,"wall_ms":165.41851707734168},{"cpu_ms":122.77699999999925,"wall_ms":129.16387803852558},{"cpu_ms":118.81400000000042,"wall_ms":124.198965029791},{"cpu_ms":138.027000000001,"wall_ms":148.18152110092342},{"cpu_ms":184.55299999999397,"wall_ms":190.9944589715451},{"cpu_ms":134.99499999999642,"wall_ms":140.42863016948104},{"cpu_ms":146.97300000000268,"wall_ms":151.3707779813558},{"cpu_ms":151.33800000000264,"wall_ms":156.87282499857247},{"cpu_ms":183.93700000000024,"wall_ms":252.42740684188902},{"cpu_ms":178.51899999999432,"wall_ms":235.68609706126153},{"cpu_ms":181.319000000002,"wall_ms":225.3478968050331},{"cpu_ms":190.43200000000127,"wall_ms":302.8977890498936},{"cpu_ms":181.1000000000007,"wall_ms":314.3158059101552},{"cpu_ms":174.01000000000266,"wall_ms":191.95219897665083},{"cpu_ms":189.11800000000056,"wall_ms":249.08204982057214},{"cpu_ms":183.03399999999925,"wall_ms":302.03023878857493},{"cpu_ms":183.81099999999861,"wall_ms":196.54520810581744},{"cpu_ms":187.95700000000437,"wall_ms":217.90397400036454},{"cpu_ms":181.94800000000555,"wall_ms":219.4070410914719},{"cpu_ms":180.50400000000622,"wall_ms":198.1843609828502},{"cpu_ms":172.99400000000276,"wall_ms":241.97959387674928},{"cpu_ms":184.31000000000353,"wall_ms":235.25432287715375},{"cpu_ms":176.0930000000016,"wall_ms":198.38972482830286},{"cpu_ms":167.18500000000347,"wall_ms":176.08901113271713},{"cpu_ms":189.44199999999967,"wall_ms":216.35486092418432},{"cpu_ms":195.962999999999,"wall_ms":214.79561901651323},{"cpu_ms":179.50399999999433,"wall_ms":201.62831479683518},{"cpu_ms":187.84499999999582,"wall_ms":217.18818903900683},{"cpu_ms":172.93399999999792,"wall_ms":183.21776716038585},{"cpu_ms":157.6480000000018,"wall_ms":167.55070304498076},{"cpu_ms":170.05000000000337,"wall_ms":175.84881302900612},{"cpu_ms":97.25099999999998,"wall_ms":108.98174392059445},{"cpu_ms":188.45299999999554,"wall_ms":196.7765330336988},{"cpu_ms":187.79699999999622,"wall_ms":196.32361689582467},{"cpu_ms":179.60900000000635,"wall_ms":221.30311583168805},{"cpu_ms":183.43999999999738,"wall_ms":206.39310614205897},{"cpu_ms":177.96599999999785,"wall_ms":184.51082310639322}],"after_median_cpu_ms":154.5905000000003,"before_control":[{"cpu_ms":101.97999999999973,"wall_ms":106.26194719225168},{"cpu_ms":171.21500000000012,"wall_ms":176.63358803838491},{"cpu_ms":134.56500000000028,"wall_ms":138.86604504659772},{"cpu_ms":120.32700000000007,"wall_ms":125.68549602292478},{"cpu_ms":120.42199999999958,"wall_ms":126.8054279498756},{"cpu_ms":101.6250000000003,"wall_ms":108.9761161711067},{"cpu_ms":105.2300000000006,"wall_ms":109.5784711651504},{"cpu_ms":114.05400000000031,"wall_ms":123.3673170208931},{"cpu_ms":151.10299999999998,"wall_ms":158.4119340404868},{"cpu_ms":114.72899999999964,"wall_ms":120.11519307270646},{"cpu_ms":137.27100000000013,"wall_ms":140.63672302290797},{"cpu_ms":108.41799999999947,"wall_ms":116.22465495020151},{"cpu_ms":128.72600000000034,"wall_ms":133.1239710561931},{"cpu_ms":110.31300000000144,"wall_ms":114.58576493896544},{"cpu_ms":113.69700000000016,"wall_ms":119.00605098344386},{"cpu_ms":109.70899999999872,"wall_ms":117.03865602612495},{"cpu_ms":130.26299999999935,"wall_ms":137.57023611105978},{"cpu_ms":124.56599999999973,"wall_ms":128.9438831154257},{"cpu_ms":120.22300000000108,"wall_ms":125.52761193364859},{"cpu_ms":106.68899999999937,"wall_ms":110.01194198615849},{"cpu_ms":127.60800000000039,"wall_ms":131.93507981486619},{"cpu_ms":116.40500000000031,"wall_ms":120.71713781915605},{"cpu_ms":108.88800000000032,"wall_ms":114.19345298781991},{"cpu_ms":102.49300000000083,"wall_ms":107.77688794769347},{"cpu_ms":121.41399999999969,"wall_ms":154.96516693383455},{"cpu_ms":127.01000000000029,"wall_ms":399.9397959560156},{"cpu_ms":114.10700000000062,"wall_ms":159.92447012104094},{"cpu_ms":123.46700000000155,"wall_ms":253.71838081628084},{"cpu_ms":111.52300000000004,"wall_ms":118.11774410307407},{"cpu_ms":126.13300000000116,"wall_ms":130.48551394604146},{"cpu_ms":103.06800000000038,"wall_ms":108.44367416575551},{"cpu_ms":119.91199999999935,"wall_ms":132.32931797392666},{"cpu_ms":158.9500000000008,"wall_ms":167.39731887355447},{"cpu_ms":114.33699999999902,"wall_ms":118.71968815103173},{"cpu_ms":130.15899999999903,"wall_ms":186.03315786458552},{"cpu_ms":121.58200000000008,"wall_ms":128.94152011722326},{"cpu_ms":100.22299999999973,"wall_ms":108.11500996351242},{"cpu_ms":136.4089999999969,"wall_ms":144.91870300844312},{"cpu_ms":98.16500000000161,"wall_ms":316.65451591834426},{"cpu_ms":145.73599999999942,"wall_ms":153.1835210043937},{"cpu_ms":159.27199999999786,"wall_ms":176.20137380436063},{"cpu_ms":146.76899999999904,"wall_ms":154.23810086213052},{"cpu_ms":165.88899999999995,"wall_ms":176.14278593100607},{"cpu_ms":169.4519999999997,"wall_ms":175.87356315925717},{"cpu_ms":130.9679999999993,"wall_ms":138.3649231866002},{"cpu_ms":172.47800000000169,"wall_ms":178.8943149149418},{"cpu_ms":164.01499999999913,"wall_ms":172.48428310267627},{"cpu_ms":143.4899999999999,"wall_ms":146.9647861085832},{"cpu_ms":144.601999999999,"wall_ms":152.03725080937147},{"cpu_ms":135.1889999999969,"wall_ms":139.53799405135214},{"cpu_ms":157.01899999999824,"wall_ms":165.43681896291673},{"cpu_ms":171.97599999999724,"wall_ms":189.21533413231373},{"cpu_ms":174.33400000000177,"wall_ms":195.43367880396545},{"cpu_ms":177.16300000000018,"wall_ms":192.5329309888184},{"cpu_ms":179.1009999999993,"wall_ms":215.23837093263865},{"cpu_ms":175.23600000000172,"wall_ms":183.06036805734038},{"cpu_ms":185.7629999999979,"wall_ms":204.33140895329416},{"cpu_ms":179.52600000000274,"wall_ms":189.08934900537133},{"cpu_ms":175.86700000000377,"wall_ms":185.54707313887775},{"cpu_ms":174.92299999999972,"wall_ms":203.23933917097747},{"cpu_ms":169.36400000000162,"wall_ms":176.86579702422023},{"cpu_ms":143.3099999999996,"wall_ms":150.83510684780777},{"cpu_ms":142.6100000000048,"wall_ms":150.02074604853988},{"cpu_ms":130.31099999999896,"wall_ms":137.65310496091843},{"cpu_ms":124.48900000000407,"wall_ms":128.89914400875568},{"cpu_ms":129.3500000000023,"wall_ms":134.70412604510784},{"cpu_ms":153.90699999999669,"wall_ms":163.36369910277426},{"cpu_ms":126.336000000002,"wall_ms":131.74200407229364},{"cpu_ms":173.5219999999984,"wall_ms":180.92102301307023},{"cpu_ms":135.21400000000483,"wall_ms":140.66512091085315},{"cpu_ms":160.2049999999977,"wall_ms":166.63248487748206},{"cpu_ms":171.122000000004,"wall_ms":230.51973595283926},{"cpu_ms":180.3209999999993,"wall_ms":223.37039303965867},{"cpu_ms":182.8959999999995,"wall_ms":305.7373920455575},{"cpu_ms":181.89500000000436,"wall_ms":284.5261439215392},{"cpu_ms":178.30199999999508,"wall_ms":239.44037011824548},{"cpu_ms":191.57599999999775,"wall_ms":402.5526198092848},{"cpu_ms":183.34999999999724,"wall_ms":292.095011100173},{"cpu_ms":181.1530000000019,"wall_ms":240.38984416984022},{"cpu_ms":174.2009999999965,"wall_ms":191.1767718847841},{"cpu_ms":175.5770000000041,"wall_ms":215.79816099256277},{"cpu_ms":178.7390000000002,"wall_ms":215.44819697737694},{"cpu_ms":177.44499999999874,"wall_ms":227.9200330376625},{"cpu_ms":181.98799999999693,"wall_ms":206.49798912927508},{"cpu_ms":191.91200000000208,"wall_ms":264.83731996268034},{"cpu_ms":180.20500000000084,"wall_ms":237.3912448529154},{"cpu_ms":174.9149999999986,"wall_ms":198.59281508252025},{"cpu_ms":167.5250000000048,"wall_ms":236.8928538635373},{"cpu_ms":182.0639999999969,"wall_ms":238.07899001985788},{"cpu_ms":184.80300000000227,"wall_ms":222.69440302625299},{"cpu_ms":197.42300000000057,"wall_ms":290.22967000491917},{"cpu_ms":187.3730000000009,"wall_ms":225.40007811039686},{"cpu_ms":174.47599999999852,"wall_ms":184.38430596143007},{"cpu_ms":164.86799999999846,"wall_ms":172.40406898781657},{"cpu_ms":168.81199999999552,"wall_ms":173.2223469298333},{"cpu_ms":190.51500000000487,"wall_ms":195.02852810546756},{"cpu_ms":176.17100000000363,"wall_ms":184.04806917533278},{"cpu_ms":182.64699999999578,"wall_ms":214.0651559457183},{"cpu_ms":169.7370000000049,"wall_ms":210.05916385911405},{"cpu_ms":180.2899999999994,"wall_ms":196.64756301790476}],"before_control_median_cpu_ms":152.50499999999835,"after_control":[{"cpu_ms":115.29200000000017,"wall_ms":122.63767304830253},{"cpu_ms":105.35899999999998,"wall_ms":109.66333001852036},{"cpu_ms":142.74399999999955,"wall_ms":148.09529413469136},{"cpu_ms":145.3249999999997,"wall_ms":149.6897218748927},{"cpu_ms":125.94700000000003,"wall_ms":132.2253921534866},{"cpu_ms":122.01499999999932,"wall_ms":125.30559697188437},{"cpu_ms":119.23499999999976,"wall_ms":124.64685691520572},{"cpu_ms":120.24499999999972,"wall_ms":128.63471498712897},{"cpu_ms":116.57000000000028,"wall_ms":119.9316291604191},{"cpu_ms":104.61100000000023,"wall_ms":113.9828790910542},{"cpu_ms":108.76000000000019,"wall_ms":116.11567181535065},{"cpu_ms":110.02400000000011,"wall_ms":112.30044602416456},{"cpu_ms":123.89299999999892,"wall_ms":131.31005293689668},{"cpu_ms":129.57699999999937,"wall_ms":132.90764694102108},{"cpu_ms":99.2999999999995,"wall_ms":104.66459416784346},{"cpu_ms":104.62500000000041,"wall_ms":109.95401395484805},{"cpu_ms":121.1650000000013,"wall_ms":124.54305309802294},{"cpu_ms":159.26699999999983,"wall_ms":163.73439505696297},{"cpu_ms":131.02699999999956,"wall_ms":136.34717091917992},{"cpu_ms":128.57500000000144,"wall_ms":129.91610006429255},{"cpu_ms":98.05099999999989,"wall_ms":103.42414793558419},{"cpu_ms":94.80499999999914,"wall_ms":101.12927109003067},{"cpu_ms":108.5339999999988,"wall_ms":114.95869210921228},{"cpu_ms":121.75799999999981,"wall_ms":130.01627614721656},{"cpu_ms":108.18500000000064,"wall_ms":208.0279579386115},{"cpu_ms":103.89599999999888,"wall_ms":297.57694993168116},{"cpu_ms":114.23999999999879,"wall_ms":139.63131699711084},{"cpu_ms":110.60199999999831,"wall_ms":309.08686900511384},{"cpu_ms":107.55799999999915,"wall_ms":117.94014205224812},{"cpu_ms":109.4589999999993,"wall_ms":116.81367713026702},{"cpu_ms":137.20200000000204,"wall_ms":169.78428699076176},{"cpu_ms":117.39299999999986,"wall_ms":159.2676539439708},{"cpu_ms":105.19700000000043,"wall_ms":110.56353407911956},{"cpu_ms":119.34199999999962,"wall_ms":125.70135807618499},{"cpu_ms":120.83200000000005,"wall_ms":127.19452707096934},{"cpu_ms":101.2689999999985,"wall_ms":108.59264200553298},{"cpu_ms":169.30900000000193,"wall_ms":174.74518390372396},{"cpu_ms":108.05800000000332,"wall_ms":113.56469010934234},{"cpu_ms":124.17899999999804,"wall_ms":239.34763995930552},{"cpu_ms":173.56399999999894,"wall_ms":178.00164711661637},{"cpu_ms":136.5530000000028,"wall_ms":142.26124994456768},{"cpu_ms":157.13500000000025,"wall_ms":163.57383807189763},{"cpu_ms":183.74999999999986,"wall_ms":188.99878789670765},{"cpu_ms":181.04299999999895,"wall_ms":188.45991184934974},{"cpu_ms":147.01300000000117,"wall_ms":153.43185514211655},{"cpu_ms":160.24999999999778,"wall_ms":167.68908593803644},{"cpu_ms":174.74499999999793,"wall_ms":180.1757188513875},{"cpu_ms":167.77599999999993,"wall_ms":174.1155891213566},{"cpu_ms":146.13000000000298,"wall_ms":154.88633210770786},{"cpu_ms":168.38300000000217,"wall_ms":177.7977468445897},{"cpu_ms":124.71700000000041,"wall_ms":130.12116495519876},{"cpu_ms":177.66900000000163,"wall_ms":193.4265790041536},{"cpu_ms":170.47899999999672,"wall_ms":179.96694915927947},{"cpu_ms":174.00500000000108,"wall_ms":179.46091387420893},{"cpu_ms":175.08300000000077,"wall_ms":184.22585492953658},{"cpu_ms":178.17299999999747,"wall_ms":182.70387197844684},{"cpu_ms":189.57000000000335,"wall_ms":204.4683350250125},{"cpu_ms":179.01599999999718,"wall_ms":185.47466699965298},{"cpu_ms":196.8259999999944,"wall_ms":202.59235007688403},{"cpu_ms":181.94799999999844,"wall_ms":190.46592083759606},{"cpu_ms":156.09800000000007,"wall_ms":161.50910407304764},{"cpu_ms":122.95500000000459,"wall_ms":129.36696712858975},{"cpu_ms":124.5970000000014,"wall_ms":131.97786500677466},{"cpu_ms":171.23300000000086,"wall_ms":178.59397712163627},{"cpu_ms":132.81999999999528,"wall_ms":140.25726891122758},{"cpu_ms":168.6210000000017,"wall_ms":177.00229305773973},{"cpu_ms":134.77999999999923,"wall_ms":141.17584517225623},{"cpu_ms":185.3490000000022,"wall_ms":191.85888417996466},{"cpu_ms":138.73499999999694,"wall_ms":145.05921909585595},{"cpu_ms":103.79799999999761,"wall_ms":109.27910800091922},{"cpu_ms":162.21700000000538,"wall_ms":169.78644602932036},{"cpu_ms":169.8769999999996,"wall_ms":251.40749220736325},{"cpu_ms":185.37200000000098,"wall_ms":196.63135916925967},{"cpu_ms":180.95000000000283,"wall_ms":281.86056413687766},{"cpu_ms":182.17499999999376,"wall_ms":227.36914199776947},{"cpu_ms":173.5570000000024,"wall_ms":241.23954004608095},{"cpu_ms":176.37100000000316,"wall_ms":195.28347090817988},{"cpu_ms":186.1420000000038,"wall_ms":271.90946810878813},{"cpu_ms":181.13699999999966,"wall_ms":288.160203024745},{"cpu_ms":176.46799999999985,"wall_ms":208.50559906102717},{"cpu_ms":183.74000000000024,"wall_ms":307.26771405898035},{"cpu_ms":186.32299999999447,"wall_ms":258.40175803750753},{"cpu_ms":187.24000000000274,"wall_ms":212.35286514274776},{"cpu_ms":189.84900000000238,"wall_ms":261.30925002507865},{"cpu_ms":191.46299999999883,"wall_ms":263.7674219440669},{"cpu_ms":167.152999999999,"wall_ms":231.33622505702078},{"cpu_ms":175.5940000000038,"wall_ms":198.7651470117271},{"cpu_ms":187.99800000000033,"wall_ms":224.34881306253374},{"cpu_ms":160.2579999999989,"wall_ms":180.4710659198463},{"cpu_ms":192.98100000000318,"wall_ms":237.60908213444054},{"cpu_ms":180.1320000000004,"wall_ms":199.3164368905127},{"cpu_ms":191.48799999999966,"wall_ms":233.811890007928},{"cpu_ms":178.75000000000085,"wall_ms":183.92957700416446},{"cpu_ms":168.98599999999675,"wall_ms":174.4112870655954},{"cpu_ms":171.14000000000118,"wall_ms":176.58865405246615},{"cpu_ms":178.84600000000006,"wall_ms":188.32977511920035},{"cpu_ms":188.56099999999998,"wall_ms":209.54182697460055},{"cpu_ms":178.78199999999822,"wall_ms":207.83247519284487},{"cpu_ms":171.34399999999772,"wall_ms":181.0565108899027},{"cpu_ms":180.2419999999998,"wall_ms":204.6549201477319}],"after_control_median_cpu_ms":159.7584999999988},{"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":88.85599999999982,"wall_ms":136.77737396210432},{"cpu_ms":72.25200000000021,"wall_ms":104.1670108679682},{"cpu_ms":87.22899999999979,"wall_ms":127.68955784849823},{"cpu_ms":84.27499999999988,"wall_ms":101.2802291661501},{"cpu_ms":77.01000000000002,"wall_ms":88.57455686666071},{"cpu_ms":74.75399999999954,"wall_ms":81.25820104032755},{"cpu_ms":76.62700000000022,"wall_ms":80.99612104706466},{"cpu_ms":85.67400000000004,"wall_ms":90.10528610087931},{"cpu_ms":85.60899999999982,"wall_ms":95.02791007980704},{"cpu_ms":86.75699999999952,"wall_ms":140.7171729952097},{"cpu_ms":85.12900000000023,"wall_ms":104.20020413585007},{"cpu_ms":83.80700000000019,"wall_ms":112.22869018092752},{"cpu_ms":79.11000000000001,"wall_ms":112.82863095402718},{"cpu_ms":74.76100000000052,"wall_ms":97.27054997347295},{"cpu_ms":79.8040000000002,"wall_ms":150.50768107175827},{"cpu_ms":78.33299999999977,"wall_ms":103.60880894586444},{"cpu_ms":89.29200000000037,"wall_ms":126.76591379567981},{"cpu_ms":86.00299999999983,"wall_ms":113.46712289378047},{"cpu_ms":89.93500000000054,"wall_ms":112.48354613780975},{"cpu_ms":87.29600000000026,"wall_ms":103.65932388231158},{"cpu_ms":91.5480000000013,"wall_ms":109.51624414883554},{"cpu_ms":91.65099999999882,"wall_ms":126.95034919306636},{"cpu_ms":83.54600000000012,"wall_ms":114.68104203231633},{"cpu_ms":84.60900000000038,"wall_ms":124.13973896764219},{"cpu_ms":86.37500000000031,"wall_ms":125.20720297470689},{"cpu_ms":86.21400000000001,"wall_ms":124.67225408181548},{"cpu_ms":91.07600000000105,"wall_ms":123.71003697626293},{"cpu_ms":81.57800000000037,"wall_ms":126.7205101903528},{"cpu_ms":89.37599999999968,"wall_ms":125.69175800308585},{"cpu_ms":84.79900000000029,"wall_ms":93.26816396787763},{"cpu_ms":78.2399999999992,"wall_ms":109.76214287802577},{"cpu_ms":80.3130000000003,"wall_ms":115.04451092332602},{"cpu_ms":86.63100000000057,"wall_ms":114.48887293227017},{"cpu_ms":84.03400000000083,"wall_ms":117.9332360625267},{"cpu_ms":79.04399999999967,"wall_ms":116.30649818107486},{"cpu_ms":78.8189999999993,"wall_ms":97.25669492036104},{"cpu_ms":75.8669999999988,"wall_ms":82.8531610313803},{"cpu_ms":76.48799999999945,"wall_ms":89.24587187357247},{"cpu_ms":77.6899999999987,"wall_ms":97.92063501663506},{"cpu_ms":75.85700000000095,"wall_ms":89.21493380330503},{"cpu_ms":74.55299999999987,"wall_ms":81.91667194478214},{"cpu_ms":76.56899999999922,"wall_ms":84.51934391632676},{"cpu_ms":58.60099999999946,"wall_ms":63.87834786437452},{"cpu_ms":84.99199999999973,"wall_ms":91.66195499710739},{"cpu_ms":84.93299999999948,"wall_ms":93.43094914220273},{"cpu_ms":82.68100000000089,"wall_ms":89.97636707499623},{"cpu_ms":85.87899999999848,"wall_ms":99.05217890627682},{"cpu_ms":87.19600000000227,"wall_ms":96.58016194589436},{"cpu_ms":87.28299999999933,"wall_ms":146.78952819667757},{"cpu_ms":84.72799999999836,"wall_ms":113.8149849139154},{"cpu_ms":86.89599999999942,"wall_ms":113.81246102973819},{"cpu_ms":83.61700000000027,"wall_ms":115.99228112027049},{"cpu_ms":83.93700000000237,"wall_ms":98.4654740896076},{"cpu_ms":83.59800000000206,"wall_ms":109.100969042629},{"cpu_ms":82.82799999999924,"wall_ms":98.47796708345413},{"cpu_ms":88.29199999999915,"wall_ms":99.77503004483879},{"cpu_ms":88.8549999999988,"wall_ms":190.8573021646589},{"cpu_ms":84.66499999999755,"wall_ms":102.2321789059788},{"cpu_ms":89.95999999999782,"wall_ms":121.29508680664003},{"cpu_ms":84.45999999999998,"wall_ms":104.92532001808286},{"cpu_ms":86.42399999999739,"wall_ms":114.63144002482295},{"cpu_ms":86.85900000000046,"wall_ms":102.41549205966294},{"cpu_ms":83.95699999999806,"wall_ms":215.9478198736906},{"cpu_ms":89.20499999999976,"wall_ms":105.66561296582222},{"cpu_ms":89.56399999999931,"wall_ms":126.7464798875153},{"cpu_ms":82.41600000000204,"wall_ms":115.81674497574568},{"cpu_ms":83.38699999999832,"wall_ms":100.92056985013187},{"cpu_ms":78.14200000000326,"wall_ms":87.58816705085337},{"cpu_ms":87.94299999999922,"wall_ms":110.79414305277169},{"cpu_ms":90.93799999999774,"wall_ms":116.32271017879248},{"cpu_ms":85.5169999999994,"wall_ms":97.27229503914714},{"cpu_ms":82.69100000000051,"wall_ms":100.35491408780217},{"cpu_ms":81.7060000000005,"wall_ms":96.22323699295521},{"cpu_ms":82.8950000000006,"wall_ms":106.44943709485233},{"cpu_ms":88.14199999999772,"wall_ms":109.58891198970377},{"cpu_ms":89.73399999999998,"wall_ms":109.1225310228765},{"cpu_ms":79.53900000000047,"wall_ms":112.94079292565584},{"cpu_ms":88.1520000000009,"wall_ms":132.52845196984708},{"cpu_ms":82.88800000000052,"wall_ms":126.26360007561743},{"cpu_ms":81.52600000000021,"wall_ms":97.94800798408687},{"cpu_ms":88.72900000000072,"wall_ms":119.38078282400966},{"cpu_ms":77.26300000000208,"wall_ms":94.6905231103301},{"cpu_ms":85.57300000000012,"wall_ms":124.92073094472289},{"cpu_ms":86.62700000000001,"wall_ms":112.11953987367451},{"cpu_ms":83.89900000000239,"wall_ms":104.15453091263771},{"cpu_ms":86.04599999999962,"wall_ms":108.51950501091778},{"cpu_ms":85.67000000000036,"wall_ms":108.01648581400514},{"cpu_ms":85.94300000000032,"wall_ms":111.95189598947763},{"cpu_ms":84.22199999999691,"wall_ms":96.9874041620642},{"cpu_ms":84.82499999999504,"wall_ms":96.28548310138285},{"cpu_ms":84.893000000001,"wall_ms":95.49408499151468},{"cpu_ms":85.02299999999963,"wall_ms":102.4561699014157},{"cpu_ms":87.92900000000259,"wall_ms":102.94752707704902},{"cpu_ms":88.00100000000555,"wall_ms":101.43912001512945},{"cpu_ms":87.69900000000064,"wall_ms":112.46624705381691},{"cpu_ms":83.80000000000365,"wall_ms":106.6501229070127},{"cpu_ms":88.36500000000314,"wall_ms":123.87609085999429},{"cpu_ms":83.58500000000646,"wall_ms":111.58351902849972},{"cpu_ms":88.58199999999528,"wall_ms":108.53508510626853},{"cpu_ms":85.95900000000256,"wall_ms":97.26995206438005}],"before_median_cpu_ms":84.85899999999802,"after":[{"cpu_ms":88.28600000000009,"wall_ms":127.28895992040634},{"cpu_ms":87.09899999999982,"wall_ms":132.2216740809381},{"cpu_ms":80.80100000000013,"wall_ms":100.77933291904628},{"cpu_ms":84.06700000000012,"wall_ms":112.51159803941846},{"cpu_ms":79.65899999999992,"wall_ms":118.90319688245654},{"cpu_ms":79.04700000000008,"wall_ms":84.73155810497701},{"cpu_ms":78.92999999999972,"wall_ms":89.54260498285294},{"cpu_ms":83.86400000000016,"wall_ms":93.34814385510981},{"cpu_ms":89.47199999999978,"wall_ms":103.00595290027559},{"cpu_ms":85.9209999999999,"wall_ms":112.45107580907643},{"cpu_ms":81.64499999999997,"wall_ms":95.63770005479455},{"cpu_ms":87.90099999999956,"wall_ms":122.65020585618913},{"cpu_ms":88.96700000000024,"wall_ms":102.66486601904035},{"cpu_ms":77.08199999999988,"wall_ms":85.53826482966542},{"cpu_ms":77.9759999999996,"wall_ms":125.78397011384368},{"cpu_ms":82.268,"wall_ms":113.64839202724397},{"cpu_ms":90.52399999999938,"wall_ms":129.40716790035367},{"cpu_ms":86.00799999999964,"wall_ms":123.88980202376842},{"cpu_ms":86.64699999999925,"wall_ms":124.0755079779774},{"cpu_ms":87.88200000000046,"wall_ms":121.99754687026143},{"cpu_ms":91.3529999999998,"wall_ms":104.76300492882729},{"cpu_ms":87.92900000000081,"wall_ms":121.80111091583967},{"cpu_ms":93.92899999999926,"wall_ms":115.61383283697069},{"cpu_ms":91.035999999999,"wall_ms":127.40776292048395},{"cpu_ms":86.87199999999962,"wall_ms":114.43428113125265},{"cpu_ms":89.22700000000106,"wall_ms":104.63404492475092},{"cpu_ms":86.15499999999976,"wall_ms":127.02924711629748},{"cpu_ms":88.72299999999989,"wall_ms":125.20540994592011},{"cpu_ms":89.50800000000037,"wall_ms":129.24000597558916},{"cpu_ms":87.95099999999856,"wall_ms":125.53345295600593},{"cpu_ms":80.85900000000024,"wall_ms":99.51649606227875},{"cpu_ms":83.74300000000012,"wall_ms":114.98331604525447},{"cpu_ms":82.43500000000026,"wall_ms":95.8972661755979},{"cpu_ms":85.93199999999968,"wall_ms":128.36776697076857},{"cpu_ms":82.1310000000004,"wall_ms":123.0499038938433},{"cpu_ms":84.30000000000071,"wall_ms":184.64165087789297},{"cpu_ms":76.23900000000106,"wall_ms":84.78622208349407},{"cpu_ms":76.6050000000007,"wall_ms":93.87847501784563},{"cpu_ms":79.47300000000013,"wall_ms":88.82387401536107},{"cpu_ms":79.52799999999982,"wall_ms":88.27036013826728},{"cpu_ms":83.55099999999993,"wall_ms":96.18526394478977},{"cpu_ms":86.91699999999969,"wall_ms":93.37188908830285},{"cpu_ms":54.32199999999909,"wall_ms":62.675391091033816},{"cpu_ms":90.27400000000085,"wall_ms":96.80366911925375},{"cpu_ms":87.2819999999983,"wall_ms":95.75391793623567},{"cpu_ms":87.10900000000166,"wall_ms":104.69527984969318},{"cpu_ms":93.22699999999884,"wall_ms":105.22659285925329},{"cpu_ms":85.62999999999832,"wall_ms":115.16697006300092},{"cpu_ms":82.66200000000268,"wall_ms":97.2351951058954},{"cpu_ms":90.72600000000008,"wall_ms":117.25568189285696},{"cpu_ms":87.5079999999997,"wall_ms":104.1260389611125},{"cpu_ms":88.673,"wall_ms":99.22592504881322},{"cpu_ms":78.6289999999994,"wall_ms":102.9293320607394},{"cpu_ms":88.18799999999882,"wall_ms":122.55814694799483},{"cpu_ms":86.66200000000046,"wall_ms":94.48344586417079},{"cpu_ms":89.74900000000119,"wall_ms":103.12355495989323},{"cpu_ms":80.29300000000106,"wall_ms":116.84154788963497},{"cpu_ms":89.32899999999933,"wall_ms":104.06052507460117},{"cpu_ms":91.24200000000116,"wall_ms":114.71724999137223},{"cpu_ms":83.72900000000172,"wall_ms":99.71744194626808},{"cpu_ms":88.97800000000089,"wall_ms":112.42518899962306},{"cpu_ms":85.92699999999809,"wall_ms":113.24292700737715},{"cpu_ms":89.70100000000159,"wall_ms":109.27709913812578},{"cpu_ms":87.79799999999938,"wall_ms":122.29995522648096},{"cpu_ms":90.54300000000026,"wall_ms":212.60424493812025},{"cpu_ms":85.03999999999934,"wall_ms":124.48014691472054},{"cpu_ms":83.71999999999957,"wall_ms":108.34443802013993},{"cpu_ms":82.10499999999854,"wall_ms":100.99019994959235},{"cpu_ms":85.5390000000007,"wall_ms":102.99267084337771},{"cpu_ms":90.51000000000187,"wall_ms":110.44983007013798},{"cpu_ms":88.71399999999952,"wall_ms":111.29058990627527},{"cpu_ms":87.59200000000078,"wall_ms":130.01505588181317},{"cpu_ms":86.93900000000099,"wall_ms":101.37734399177134},{"cpu_ms":91.11800000000159,"wall_ms":129.3921221513301},{"cpu_ms":90.21700000000266,"wall_ms":130.35980984568596},{"cpu_ms":92.10899999999711,"wall_ms":108.62167505547404},{"cpu_ms":82.67499999999828,"wall_ms":119.07112412154675},{"cpu_ms":93.47800000000106,"wall_ms":183.27412917278707},{"cpu_ms":87.59200000000078,"wall_ms":104.22214702703059},{"cpu_ms":87.47400000000027,"wall_ms":100.96835996955633},{"cpu_ms":88.53999999999829,"wall_ms":123.90719703398645},{"cpu_ms":86.98399999999751,"wall_ms":108.51869313046336},{"cpu_ms":85.95199999999892,"wall_ms":106.0914690606296},{"cpu_ms":91.07299999999796,"wall_ms":99.6622999664396},{"cpu_ms":88.31099999999736,"wall_ms":120.72379095479846},{"cpu_ms":86.99000000000012,"wall_ms":125.42738812044263},{"cpu_ms":91.27200000000002,"wall_ms":123.79391700960696},{"cpu_ms":83.476000000001,"wall_ms":94.91859981790185},{"cpu_ms":87.03400000000272,"wall_ms":98.85868895798922},{"cpu_ms":84.90400000000164,"wall_ms":100.38052196614444},{"cpu_ms":88.83000000000152,"wall_ms":111.74442898482084},{"cpu_ms":79.8950000000005,"wall_ms":116.61509796977043},{"cpu_ms":89.95799999999576,"wall_ms":107.46512492187321},{"cpu_ms":90.93999999999625,"wall_ms":123.50421608425677},{"cpu_ms":87.60499999999638,"wall_ms":113.25486097484827},{"cpu_ms":88.22899999999834,"wall_ms":116.71216785907745},{"cpu_ms":84.91199999999566,"wall_ms":119.49024815112352},{"cpu_ms":85.97999999999928,"wall_ms":119.52175688929856},{"cpu_ms":88.76700000000426,"wall_ms":103.35309500806034},{"cpu_ms":88.50799999999737,"wall_ms":96.3330720551312}],"after_median_cpu_ms":87.06650000000127,"before_control":[{"cpu_ms":84.60000000000001,"wall_ms":102.17954916879535},{"cpu_ms":87.52699999999969,"wall_ms":113.54878498241305},{"cpu_ms":88.74000000000049,"wall_ms":141.1387799307704},{"cpu_ms":81.499,"wall_ms":98.03224308416247},{"cpu_ms":73.75299999999996,"wall_ms":81.16154000163078},{"cpu_ms":75.42600000000022,"wall_ms":92.0913761947304},{"cpu_ms":77.4910000000002,"wall_ms":83.01717601716518},{"cpu_ms":85.52400000000037,"wall_ms":95.67219391465187},{"cpu_ms":87.08100000000042,"wall_ms":93.57542707584798},{"cpu_ms":82.89100000000005,"wall_ms":101.35931707918644},{"cpu_ms":85.62199999999987,"wall_ms":127.24882597103715},{"cpu_ms":84.61099999999976,"wall_ms":116.19865614920855},{"cpu_ms":81.33500000000015,"wall_ms":98.86106080375612},{"cpu_ms":76.9460000000004,"wall_ms":112.55154316313565},{"cpu_ms":82.19000000000065,"wall_ms":115.37933489307761},{"cpu_ms":87.02100000000002,"wall_ms":121.87320506200194},{"cpu_ms":87.65999999999963,"wall_ms":125.17947098240256},{"cpu_ms":88.32100000000054,"wall_ms":127.02994793653488},{"cpu_ms":88.79699999999957,"wall_ms":101.71644506044686},{"cpu_ms":84.66800000000063,"wall_ms":120.03607419319451},{"cpu_ms":84.54099999999976,"wall_ms":93.99233688600361},{"cpu_ms":81.45200000000052,"wall_ms":102.07616700790823},{"cpu_ms":87.56200000000014,"wall_ms":121.06186989694834},{"cpu_ms":86.23199999999898,"wall_ms":125.74725109152496},{"cpu_ms":89.56300000000006,"wall_ms":114.20115502551198},{"cpu_ms":87.03099999999964,"wall_ms":125.3736240323633},{"cpu_ms":77.58999999999894,"wall_ms":106.98368400335312},{"cpu_ms":88.96399999999893,"wall_ms":107.42700193077326},{"cpu_ms":79.50800000000058,"wall_ms":101.86204710043967},{"cpu_ms":87.39699999999928,"wall_ms":97.97605918720365},{"cpu_ms":86.06100000000083,"wall_ms":103.27656916342676},{"cpu_ms":82.0849999999993,"wall_ms":114.55687996931374},{"cpu_ms":83.64199999999933,"wall_ms":114.12083497270942},{"cpu_ms":81.22900000000044,"wall_ms":111.74201196990907},{"cpu_ms":81.18200000000009,"wall_ms":107.14773996733129},{"cpu_ms":77.93899999999887,"wall_ms":109.39454799517989},{"cpu_ms":73.36300000000051,"wall_ms":85.32112091779709},{"cpu_ms":87.58200000000116,"wall_ms":157.22147305496037},{"cpu_ms":79.22100000000043,"wall_ms":92.8016840480268},{"cpu_ms":73.63100000000067,"wall_ms":83.0988499801606},{"cpu_ms":59.41299999999927,"wall_ms":68.92172899097204},{"cpu_ms":86.81499999999964,"wall_ms":93.2498809415847},{"cpu_ms":75.62700000000078,"wall_ms":85.23082290776074},{"cpu_ms":60.69000000000102,"wall_ms":67.10113817825913},{"cpu_ms":84.38800000000057,"wall_ms":91.82404796592891},{"cpu_ms":81.17200000000224,"wall_ms":87.68031815998256},{"cpu_ms":85.39599999999936,"wall_ms":103.5125209018588},{"cpu_ms":87.99099999999882,"wall_ms":100.47345515340567},{"cpu_ms":85.33200000000107,"wall_ms":133.8438969105482},{"cpu_ms":84.34699999999751,"wall_ms":94.66459299437702},{"cpu_ms":86.41400000000132,"wall_ms":111.35693406686187},{"cpu_ms":83.81400000000028,"wall_ms":115.22531998343766},{"cpu_ms":79.62199999999697,"wall_ms":95.10255209170282},{"cpu_ms":84.05499999999932,"wall_ms":106.57336912117898},{"cpu_ms":81.8550000000009,"wall_ms":102.37112897448242},{"cpu_ms":77.95700000000139,"wall_ms":106.5855510532856},{"cpu_ms":87.63199999999927,"wall_ms":104.13939203135669},{"cpu_ms":85.53399999999911,"wall_ms":101.06045100837946},{"cpu_ms":86.68400000000176,"wall_ms":99.9552570283413},{"cpu_ms":84.344999999999,"wall_ms":98.94642210565507},{"cpu_ms":89.85799999999955,"wall_ms":116.3260459434241},{"cpu_ms":84.66799999999708,"wall_ms":185.89897104538977},{"cpu_ms":87.85500000000113,"wall_ms":125.21651317365468},{"cpu_ms":87.96500000000052,"wall_ms":104.49276305735111},{"cpu_ms":83.11899999999994,"wall_ms":103.96730108186603},{"cpu_ms":84.63499999999868,"wall_ms":111.64134507998824},{"cpu_ms":84.30900000000108,"wall_ms":119.05513401143253},{"cpu_ms":87.79300000000134,"wall_ms":116.22579907998443},{"cpu_ms":90.40699999999902,"wall_ms":117.85637494176626},{"cpu_ms":86.17900000000134,"wall_ms":110.15382711775601},{"cpu_ms":85.92200000000005,"wall_ms":102.68337302841246},{"cpu_ms":82.13699999999946,"wall_ms":97.82972699031234},{"cpu_ms":87.23499999999973,"wall_ms":100.72663798928261},{"cpu_ms":83.81299999999925,"wall_ms":99.37037480995059},{"cpu_ms":89.77700000000155,"wall_ms":101.07479896396399},{"cpu_ms":82.38200000000262,"wall_ms":109.80348312295973},{"cpu_ms":81.63899999999913,"wall_ms":102.15650894679129},{"cpu_ms":84.41299999999785,"wall_ms":91.8698851019144},{"cpu_ms":82.72499999999994,"wall_ms":102.28596115484834},{"cpu_ms":84.74800000000116,"wall_ms":103.39449113234878},{"cpu_ms":85.90399999999931,"wall_ms":103.92217803746462},{"cpu_ms":84.40099999999973,"wall_ms":99.67031492851675},{"cpu_ms":77.88100000000142,"wall_ms":92.7401299122721},{"cpu_ms":83.09500000000014,"wall_ms":120.37314590997994},{"cpu_ms":87.97899999999714,"wall_ms":125.75189396739006},{"cpu_ms":88.19699999999742,"wall_ms":97.63479302637279},{"cpu_ms":85.60799999999702,"wall_ms":113.1645308341831},{"cpu_ms":88.5719999999992,"wall_ms":93.01785891875625},{"cpu_ms":82.43600000000129,"wall_ms":109.09202392213047},{"cpu_ms":86.07899999999802,"wall_ms":108.43912488780916},{"cpu_ms":89.15299999999604,"wall_ms":95.7037671469152},{"cpu_ms":88.78900000000556,"wall_ms":113.89555688947439},{"cpu_ms":87.23299999999767,"wall_ms":106.08916520141065},{"cpu_ms":81.0199999999952,"wall_ms":106.53382586315274},{"cpu_ms":88.44299999999805,"wall_ms":115.27898209169507},{"cpu_ms":76.4430000000047,"wall_ms":96.02470695972443},{"cpu_ms":88.56000000000108,"wall_ms":99.58378504961729},{"cpu_ms":82.92099999999891,"wall_ms":110.45343801379204},{"cpu_ms":81.5550000000016,"wall_ms":115.99383689463139},{"cpu_ms":85.95100000000144,"wall_ms":110.44749687425792}],"before_control_median_cpu_ms":84.62299999999922,"after_control":[{"cpu_ms":88.48100000000025,"wall_ms":141.25463599339128},{"cpu_ms":86.62800000000016,"wall_ms":128.9819220546633},{"cpu_ms":85.06499999999973,"wall_ms":110.13364605605602},{"cpu_ms":89.12299999999985,"wall_ms":123.24123014695942},{"cpu_ms":80.33599999999996,"wall_ms":98.94163091666996},{"cpu_ms":81.2710000000001,"wall_ms":91.61245101131499},{"cpu_ms":81.57499999999996,"wall_ms":92.54453214816749},{"cpu_ms":82.5149999999999,"wall_ms":97.83906186930835},{"cpu_ms":85.85699999999986,"wall_ms":93.52252911776304},{"cpu_ms":87.19400000000022,"wall_ms":106.61060619167984},{"cpu_ms":89.1580000000003,"wall_ms":101.19253490120173},{"cpu_ms":86.15599999999989,"wall_ms":109.974498860538},{"cpu_ms":80.20099999999974,"wall_ms":106.53470503166318},{"cpu_ms":83.5669999999995,"wall_ms":119.93658798746765},{"cpu_ms":76.67799999999936,"wall_ms":113.06563392281532},{"cpu_ms":92.38700000000044,"wall_ms":118.58801892958581},{"cpu_ms":89.32800000000007,"wall_ms":125.50665205344558},{"cpu_ms":87.9209999999997,"wall_ms":125.4867750685662},{"cpu_ms":92.26100000000059,"wall_ms":99.83059205114841},{"cpu_ms":84.61299999999916,"wall_ms":109.09170983359218},{"cpu_ms":83.61299999999972,"wall_ms":91.22884506359696},{"cpu_ms":76.4459999999989,"wall_ms":116.01621494628489},{"cpu_ms":90.01500000000107,"wall_ms":97.4234661553055},{"cpu_ms":92.97000000000111,"wall_ms":124.44976810365915},{"cpu_ms":78.87600000000106,"wall_ms":112.51871194690466},{"cpu_ms":85.78499999999956,"wall_ms":117.69841704517603},{"cpu_ms":87.28999999999942,"wall_ms":125.8530889172107},{"cpu_ms":88.20500000000031,"wall_ms":128.59363784082234},{"cpu_ms":85.6469999999998,"wall_ms":103.18416683003306},{"cpu_ms":84.96100000000162,"wall_ms":104.34413305483758},{"cpu_ms":90.88099999999955,"wall_ms":126.24773709103465},{"cpu_ms":83.87500000000081,"wall_ms":93.3134900406003},{"cpu_ms":84.60000000000001,"wall_ms":99.17562897317111},{"cpu_ms":82.2389999999995,"wall_ms":116.78995098918676},{"cpu_ms":82.85899999999913,"wall_ms":131.90632709302008},{"cpu_ms":76.1680000000009,"wall_ms":90.67849791608751},{"cpu_ms":76.8039999999992,"wall_ms":105.22686201147735},{"cpu_ms":76.9289999999998,"wall_ms":85.40503797121346},{"cpu_ms":80.67400000000013,"wall_ms":95.98381817340851},{"cpu_ms":81.98799999999906,"wall_ms":89.36076611280441},{"cpu_ms":78.0100000000008,"wall_ms":86.39490697532892},{"cpu_ms":87.48500000000092,"wall_ms":93.84417091496289},{"cpu_ms":69.59600000000066,"wall_ms":80.17533901147544},{"cpu_ms":84.07700000000062,"wall_ms":90.49634798429906},{"cpu_ms":82.20499999999831,"wall_ms":88.77231809310615},{"cpu_ms":85.91299999999791,"wall_ms":116.91647814586759},{"cpu_ms":83.27500000000043,"wall_ms":109.79551682248712},{"cpu_ms":90.10100000000065,"wall_ms":115.98824500106275},{"cpu_ms":89.48799999999935,"wall_ms":109.99737400561571},{"cpu_ms":84.53400000000144,"wall_ms":118.07192605920136},{"cpu_ms":86.68300000000073,"wall_ms":105.39875412359834},{"cpu_ms":90.30399999999972,"wall_ms":103.81441586650908},{"cpu_ms":85.561000000002,"wall_ms":93.07049703784287},{"cpu_ms":83.55099999999993,"wall_ms":96.87933116219938},{"cpu_ms":88.11499999999839,"wall_ms":99.95629801414907},{"cpu_ms":76.10199999999878,"wall_ms":95.0401860754937},{"cpu_ms":89.89800000000159,"wall_ms":102.49342792667449},{"cpu_ms":86.41800000000188,"wall_ms":112.95550991781056},{"cpu_ms":88.38000000000079,"wall_ms":98.92485197633505},{"cpu_ms":76.92199999999971,"wall_ms":88.2520959712565},{"cpu_ms":91.15400000000307,"wall_ms":130.54690905846655},{"cpu_ms":85.34100000000322,"wall_ms":99.77177390828729},{"cpu_ms":87.52099999999885,"wall_ms":112.06149007193744},{"cpu_ms":88.03200000000189,"wall_ms":129.78112907148898},{"cpu_ms":86.233,"wall_ms":102.72865789011121},{"cpu_ms":87.67699999999934,"wall_ms":106.28575109876692},{"cpu_ms":80.60199999999895,"wall_ms":115.35530304536223},{"cpu_ms":83.32199999999901,"wall_ms":98.66903489455581},{"cpu_ms":88.1450000000008,"wall_ms":107.9652588814497},{"cpu_ms":85.68499999999801,"wall_ms":112.0228380896151},{"cpu_ms":90.89600000000075,"wall_ms":106.45700199529529},{"cpu_ms":87.29699999999951,"wall_ms":111.00635887123644},{"cpu_ms":83.99800000000113,"wall_ms":127.44166981428862},{"cpu_ms":90.93899999999877,"wall_ms":111.91003699786961},{"cpu_ms":89.61799999999798,"wall_ms":103.10982004739344},{"cpu_ms":86.01900000000029,"wall_ms":102.51286416314542},{"cpu_ms":88.02100000000124,"wall_ms":96.57287900336087},{"cpu_ms":87.55299999999977,"wall_ms":127.49879597686231},{"cpu_ms":86.7609999999992,"wall_ms":134.22279618680477},{"cpu_ms":90.49899999999766,"wall_ms":116.01603799499571},{"cpu_ms":90.56400000000053,"wall_ms":109.68877794221044},{"cpu_ms":86.90600000000259,"wall_ms":123.64401598460972},{"cpu_ms":77.81999999999911,"wall_ms":98.25201611965895},{"cpu_ms":85.61400000000319,"wall_ms":110.95503508113325},{"cpu_ms":89.38400000000257,"wall_ms":103.83675806224346},{"cpu_ms":90.3880000000008,"wall_ms":109.01652299799025},{"cpu_ms":87.1030000000026,"wall_ms":103.53875090368092},{"cpu_ms":86.02799999999888,"wall_ms":95.47774400562048},{"cpu_ms":88.2440000000031,"wall_ms":103.37327187880874},{"cpu_ms":88.26499999999982,"wall_ms":105.07678403519094},{"cpu_ms":90.38800000000435,"wall_ms":111.86773888766766},{"cpu_ms":81.50899999999695,"wall_ms":96.12424112856388},{"cpu_ms":87.16900000000294,"wall_ms":121.71503412537277},{"cpu_ms":87.94500000000482,"wall_ms":111.37296515516937},{"cpu_ms":87.82599999999974,"wall_ms":104.68936106190085},{"cpu_ms":88.43799999999646,"wall_ms":110.73317099362612},{"cpu_ms":84.93399999999696,"wall_ms":124.89596498198807},{"cpu_ms":84.85100000000045,"wall_ms":113.49070118740201},{"cpu_ms":80.20899999999642,"wall_ms":99.67824001796544},{"cpu_ms":89.25899999999842,"wall_ms":99.2294829338789}],"after_control_median_cpu_ms":86.19449999999995}]}, +{"hint_controls":[{"mode":"all","scale":1,"orders":[["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"]],"samples":{"before":[{"construct":3.345,"test-ascii":0.508,"test-emoji":1.508,"stripAnsi":0.434,"stripAnsi-match":2.636,"ignorable":0.217,"segment":29.087,"eastAsianWidth":0.455,"stringWidth":234.502},{"construct":3.22,"test-ascii":0.501,"test-emoji":1.27,"stripAnsi":0.339,"stripAnsi-match":2.562,"ignorable":0.225,"segment":28.637,"eastAsianWidth":0.544,"stringWidth":238.21},{"construct":3.272,"test-ascii":0.513,"test-emoji":1.271,"stripAnsi":0.347,"stripAnsi-match":2.605,"ignorable":0.22,"segment":30.898,"eastAsianWidth":0.473,"stringWidth":240.024},{"construct":3.374,"test-ascii":0.5,"test-emoji":1.245,"stripAnsi":0.337,"stripAnsi-match":2.659,"ignorable":0.215,"segment":30.472,"eastAsianWidth":0.444,"stringWidth":236.146},{"construct":3.258,"test-ascii":0.511,"test-emoji":1.24,"stripAnsi":0.343,"stripAnsi-match":2.625,"ignorable":0.216,"segment":36.494,"eastAsianWidth":0.46,"stringWidth":237.186},{"construct":3.332,"test-ascii":0.502,"test-emoji":1.255,"stripAnsi":0.336,"stripAnsi-match":2.599,"ignorable":0.223,"segment":82.402,"eastAsianWidth":0.447,"stringWidth":242.981},{"construct":3.245,"test-ascii":0.529,"test-emoji":1.243,"stripAnsi":0.349,"stripAnsi-match":2.717,"ignorable":0.215,"segment":29.259,"eastAsianWidth":0.451,"stringWidth":240.725},{"construct":20.804,"test-ascii":0.521,"test-emoji":1.259,"stripAnsi":0.342,"stripAnsi-match":2.574,"ignorable":0.233,"segment":36.891,"eastAsianWidth":0.463,"stringWidth":236.816},{"construct":3.228,"test-ascii":0.507,"test-emoji":1.259,"stripAnsi":0.333,"stripAnsi-match":2.599,"ignorable":0.219,"segment":28.56,"eastAsianWidth":0.465,"stringWidth":239.603},{"construct":3.461,"test-ascii":0.531,"test-emoji":1.299,"stripAnsi":0.343,"stripAnsi-match":2.78,"ignorable":0.268,"segment":29.479,"eastAsianWidth":0.497,"stringWidth":256.271},{"construct":3.245,"test-ascii":0.515,"test-emoji":1.256,"stripAnsi":0.338,"stripAnsi-match":2.647,"ignorable":0.219,"segment":34.915,"eastAsianWidth":0.463,"stringWidth":236.542},{"construct":3.327,"test-ascii":0.506,"test-emoji":1.261,"stripAnsi":0.343,"stripAnsi-match":2.616,"ignorable":0.216,"segment":39.439,"eastAsianWidth":0.465,"stringWidth":240.25}],"after":[{"construct":3.262,"test-ascii":0.512,"test-emoji":0.905,"stripAnsi":0.362,"stripAnsi-match":2.646,"ignorable":0.241,"segment":30.343,"eastAsianWidth":0.59,"stringWidth":241.053},{"construct":3.274,"test-ascii":0.5,"test-emoji":0.907,"stripAnsi":0.341,"stripAnsi-match":2.312,"ignorable":0.222,"segment":28.478,"eastAsianWidth":0.443,"stringWidth":234.579},{"construct":3.449,"test-ascii":0.573,"test-emoji":0.982,"stripAnsi":0.351,"stripAnsi-match":2.323,"ignorable":0.245,"segment":28.897,"eastAsianWidth":0.462,"stringWidth":235.224},{"construct":3.215,"test-ascii":0.512,"test-emoji":0.913,"stripAnsi":0.351,"stripAnsi-match":2.301,"ignorable":0.229,"segment":40.44,"eastAsianWidth":0.478,"stringWidth":235.214},{"construct":3.29,"test-ascii":0.528,"test-emoji":0.905,"stripAnsi":0.352,"stripAnsi-match":2.289,"ignorable":0.224,"segment":34.423,"eastAsianWidth":0.468,"stringWidth":234.796},{"construct":3.734,"test-ascii":0.497,"test-emoji":0.909,"stripAnsi":0.339,"stripAnsi-match":2.253,"ignorable":0.22,"segment":422.398,"eastAsianWidth":0.46,"stringWidth":232.162},{"construct":3.291,"test-ascii":0.508,"test-emoji":0.904,"stripAnsi":0.361,"stripAnsi-match":2.364,"ignorable":0.219,"segment":28.659,"eastAsianWidth":0.472,"stringWidth":251.866},{"construct":3.294,"test-ascii":0.51,"test-emoji":0.913,"stripAnsi":0.382,"stripAnsi-match":2.337,"ignorable":0.234,"segment":34.541,"eastAsianWidth":0.467,"stringWidth":252.783},{"construct":3.234,"test-ascii":0.517,"test-emoji":0.944,"stripAnsi":0.349,"stripAnsi-match":2.294,"ignorable":0.222,"segment":31.417,"eastAsianWidth":0.457,"stringWidth":236.542},{"construct":3.361,"test-ascii":0.528,"test-emoji":0.948,"stripAnsi":0.352,"stripAnsi-match":2.366,"ignorable":0.231,"segment":30.129,"eastAsianWidth":0.524,"stringWidth":257.164},{"construct":3.241,"test-ascii":0.514,"test-emoji":0.916,"stripAnsi":0.372,"stripAnsi-match":2.259,"ignorable":0.219,"segment":40.62,"eastAsianWidth":0.46,"stringWidth":254.687},{"construct":3.329,"test-ascii":0.526,"test-emoji":0.903,"stripAnsi":0.352,"stripAnsi-match":2.334,"ignorable":0.222,"segment":33.741,"eastAsianWidth":0.468,"stringWidth":238.081}],"before_control":[{"construct":3.279,"test-ascii":0.511,"test-emoji":1.253,"stripAnsi":0.352,"stripAnsi-match":2.686,"ignorable":0.225,"segment":28.309,"eastAsianWidth":0.476,"stringWidth":239.112},{"construct":3.333,"test-ascii":0.509,"test-emoji":1.255,"stripAnsi":0.353,"stripAnsi-match":2.648,"ignorable":0.227,"segment":28.782,"eastAsianWidth":0.468,"stringWidth":242.027},{"construct":3.293,"test-ascii":0.517,"test-emoji":1.305,"stripAnsi":0.336,"stripAnsi-match":2.691,"ignorable":0.219,"segment":28.849,"eastAsianWidth":0.467,"stringWidth":240.735},{"construct":3.285,"test-ascii":0.492,"test-emoji":1.269,"stripAnsi":0.338,"stripAnsi-match":2.63,"ignorable":0.23,"segment":33.457,"eastAsianWidth":0.454,"stringWidth":239.035},{"construct":4.422,"test-ascii":0.504,"test-emoji":1.293,"stripAnsi":0.336,"stripAnsi-match":2.592,"ignorable":0.219,"segment":75.491,"eastAsianWidth":0.48,"stringWidth":267.409},{"construct":23.699,"test-ascii":0.514,"test-emoji":1.284,"stripAnsi":0.339,"stripAnsi-match":2.584,"ignorable":0.219,"segment":36.44,"eastAsianWidth":0.469,"stringWidth":238.679},{"construct":3.338,"test-ascii":0.494,"test-emoji":1.25,"stripAnsi":0.348,"stripAnsi-match":2.708,"ignorable":0.22,"segment":33.486,"eastAsianWidth":0.462,"stringWidth":238.402},{"construct":3.319,"test-ascii":0.522,"test-emoji":1.26,"stripAnsi":0.336,"stripAnsi-match":2.641,"ignorable":0.221,"segment":28.467,"eastAsianWidth":0.471,"stringWidth":238.88},{"construct":3.35,"test-ascii":0.507,"test-emoji":1.259,"stripAnsi":0.394,"stripAnsi-match":2.902,"ignorable":0.286,"segment":30.618,"eastAsianWidth":0.468,"stringWidth":267.764},{"construct":3.386,"test-ascii":0.508,"test-emoji":1.246,"stripAnsi":0.334,"stripAnsi-match":2.675,"ignorable":0.222,"segment":29.21,"eastAsianWidth":0.498,"stringWidth":249.385},{"construct":4.351,"test-ascii":0.886,"test-emoji":1.483,"stripAnsi":0.334,"stripAnsi-match":2.622,"ignorable":0.228,"segment":55.34,"eastAsianWidth":0.442,"stringWidth":242.077},{"construct":3.34,"test-ascii":0.495,"test-emoji":1.278,"stripAnsi":0.335,"stripAnsi-match":2.937,"ignorable":0.223,"segment":38.712,"eastAsianWidth":0.45,"stringWidth":246.628}],"after_control":[{"construct":3.232,"test-ascii":0.544,"test-emoji":0.938,"stripAnsi":0.401,"stripAnsi-match":2.326,"ignorable":0.247,"segment":29.563,"eastAsianWidth":0.466,"stringWidth":253.168},{"construct":3.286,"test-ascii":0.544,"test-emoji":0.939,"stripAnsi":0.353,"stripAnsi-match":2.288,"ignorable":0.221,"segment":28.773,"eastAsianWidth":0.456,"stringWidth":238.74},{"construct":3.279,"test-ascii":0.499,"test-emoji":0.937,"stripAnsi":0.358,"stripAnsi-match":2.294,"ignorable":0.23,"segment":35.315,"eastAsianWidth":0.45,"stringWidth":232.6},{"construct":3.245,"test-ascii":0.511,"test-emoji":0.902,"stripAnsi":0.355,"stripAnsi-match":2.315,"ignorable":0.227,"segment":44.041,"eastAsianWidth":0.465,"stringWidth":237.524},{"construct":3.321,"test-ascii":0.514,"test-emoji":0.908,"stripAnsi":0.349,"stripAnsi-match":2.318,"ignorable":0.221,"segment":34.254,"eastAsianWidth":0.466,"stringWidth":234.091},{"construct":3.262,"test-ascii":0.51,"test-emoji":0.937,"stripAnsi":0.343,"stripAnsi-match":2.261,"ignorable":0.218,"segment":38.084,"eastAsianWidth":0.44,"stringWidth":232.949},{"construct":3.262,"test-ascii":0.525,"test-emoji":0.91,"stripAnsi":0.353,"stripAnsi-match":2.377,"ignorable":0.223,"segment":52.639,"eastAsianWidth":0.46,"stringWidth":245.339},{"construct":3.286,"test-ascii":0.539,"test-emoji":0.94,"stripAnsi":0.358,"stripAnsi-match":2.365,"ignorable":0.232,"segment":62.601,"eastAsianWidth":0.457,"stringWidth":242.502},{"construct":4.019,"test-ascii":0.514,"test-emoji":0.963,"stripAnsi":0.352,"stripAnsi-match":2.347,"ignorable":0.227,"segment":50.056,"eastAsianWidth":0.458,"stringWidth":244.133},{"construct":3.338,"test-ascii":0.513,"test-emoji":0.897,"stripAnsi":0.346,"stripAnsi-match":2.491,"ignorable":0.252,"segment":29.634,"eastAsianWidth":0.485,"stringWidth":252.993},{"construct":3.228,"test-ascii":0.506,"test-emoji":0.928,"stripAnsi":0.352,"stripAnsi-match":2.316,"ignorable":0.219,"segment":34.709,"eastAsianWidth":0.459,"stringWidth":234.944},{"construct":3.567,"test-ascii":0.51,"test-emoji":0.911,"stripAnsi":0.347,"stripAnsi-match":2.286,"ignorable":0.224,"segment":46.006,"eastAsianWidth":0.462,"stringWidth":259.625}]},"output":["constructed g 14116","checksum 162430"],"medians_us":{"before":{"construct":3.2995,"test-ascii":0.5095000000000001,"test-emoji":1.259,"stripAnsi":0.3425,"stripAnsi-match":2.6205,"ignorable":0.219,"segment":30.685000000000002,"eastAsianWidth":0.463,"stringWidth":238.9065},"after":{"construct":3.2904999999999998,"test-ascii":0.513,"test-emoji":0.911,"stripAnsi":0.352,"stripAnsi-match":2.3175,"ignorable":0.223,"segment":32.579,"eastAsianWidth":0.4675,"stringWidth":237.3115},"before_control":{"construct":3.339,"test-ascii":0.5085,"test-emoji":1.2645,"stripAnsi":0.337,"stripAnsi-match":2.6615,"ignorable":0.2225,"segment":32.0375,"eastAsianWidth":0.468,"stringWidth":241.381},"after_control":{"construct":3.2824999999999998,"test-ascii":0.5135000000000001,"test-emoji":0.9325000000000001,"stripAnsi":0.3525,"stripAnsi-match":2.317,"ignorable":0.2255,"segment":36.6995,"eastAsianWidth":0.4595,"stringWidth":240.621}}},{"mode":"test-emoji","scale":100,"orders":[["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"]],"samples":{"before":[{"test-emoji":1.266},{"test-emoji":1.314},{"test-emoji":1.232},{"test-emoji":1.285},{"test-emoji":1.259},{"test-emoji":1.237},{"test-emoji":1.258},{"test-emoji":1.269},{"test-emoji":1.279},{"test-emoji":1.218},{"test-emoji":1.221},{"test-emoji":1.204}],"after":[{"test-emoji":0.959},{"test-emoji":0.933},{"test-emoji":0.892},{"test-emoji":0.89},{"test-emoji":0.887},{"test-emoji":0.92},{"test-emoji":0.92},{"test-emoji":0.91},{"test-emoji":0.912},{"test-emoji":0.894},{"test-emoji":0.893},{"test-emoji":0.907}],"before_control":[{"test-emoji":1.26},{"test-emoji":1.23},{"test-emoji":1.378},{"test-emoji":1.263},{"test-emoji":1.225},{"test-emoji":1.246},{"test-emoji":1.234},{"test-emoji":1.225},{"test-emoji":1.275},{"test-emoji":1.227},{"test-emoji":1.316},{"test-emoji":1.206}],"after_control":[{"test-emoji":0.888},{"test-emoji":0.895},{"test-emoji":0.894},{"test-emoji":0.902},{"test-emoji":0.903},{"test-emoji":0.902},{"test-emoji":0.905},{"test-emoji":0.906},{"test-emoji":0.904},{"test-emoji":0.894},{"test-emoji":0.902},{"test-emoji":0.884}]},"output":["checksum 100000"],"medians_us":{"before":{"test-emoji":1.2585},"after":{"test-emoji":0.9085000000000001},"before_control":{"test-emoji":1.24},"after_control":{"test-emoji":0.902}}},{"mode":"stripAnsi-match","scale":100,"orders":[["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"]],"samples":{"before":[{"stripAnsi-match":3.041},{"stripAnsi-match":2.883},{"stripAnsi-match":2.903},{"stripAnsi-match":3.68},{"stripAnsi-match":2.723},{"stripAnsi-match":2.739},{"stripAnsi-match":2.705},{"stripAnsi-match":2.698},{"stripAnsi-match":2.867},{"stripAnsi-match":2.635},{"stripAnsi-match":2.804},{"stripAnsi-match":2.896}],"after":[{"stripAnsi-match":2.699},{"stripAnsi-match":2.274},{"stripAnsi-match":3.036},{"stripAnsi-match":2.553},{"stripAnsi-match":2.391},{"stripAnsi-match":2.411},{"stripAnsi-match":2.332},{"stripAnsi-match":2.379},{"stripAnsi-match":2.604},{"stripAnsi-match":2.373},{"stripAnsi-match":2.345},{"stripAnsi-match":2.563}],"before_control":[{"stripAnsi-match":2.879},{"stripAnsi-match":2.865},{"stripAnsi-match":3.238},{"stripAnsi-match":2.999},{"stripAnsi-match":2.693},{"stripAnsi-match":2.641},{"stripAnsi-match":2.674},{"stripAnsi-match":2.688},{"stripAnsi-match":3.245},{"stripAnsi-match":2.643},{"stripAnsi-match":2.872},{"stripAnsi-match":2.635}],"after_control":[{"stripAnsi-match":2.432},{"stripAnsi-match":2.845},{"stripAnsi-match":2.803},{"stripAnsi-match":2.639},{"stripAnsi-match":2.33},{"stripAnsi-match":2.325},{"stripAnsi-match":2.355},{"stripAnsi-match":2.389},{"stripAnsi-match":2.544},{"stripAnsi-match":2.583},{"stripAnsi-match":2.368},{"stripAnsi-match":2.549}]},"output":["checksum 600000"],"medians_us":{"before":{"stripAnsi-match":2.8354999999999997},"after":{"stripAnsi-match":2.401},"before_control":{"stripAnsi-match":2.779},"after_control":{"stripAnsi-match":2.488}}}]}, +{"unrelated_controls":[{"mode":"segment","scale":10,"orders":[["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"]],"samples":{"before":[{"segment":54.966},{"segment":48.041},{"segment":36.004},{"segment":33.658},{"segment":52.726},{"segment":32.831},{"segment":32.801},{"segment":33.164},{"segment":44.508},{"segment":32.885},{"segment":32.599},{"segment":54.038},{"segment":62.227},{"segment":33.541},{"segment":34.958},{"segment":33.61},{"segment":33.658},{"segment":40.277},{"segment":34.899},{"segment":33.276},{"segment":32.599},{"segment":47.479},{"segment":66.5},{"segment":101.587},{"segment":57.723},{"segment":33.507},{"segment":32.701},{"segment":32.92},{"segment":32.774},{"segment":32.079},{"segment":36.549},{"segment":32.533},{"segment":33.804},{"segment":33.695},{"segment":33.748},{"segment":31.845},{"segment":32.091},{"segment":32.318},{"segment":31.768},{"segment":32.876},{"segment":32.608},{"segment":31.953},{"segment":31.993},{"segment":31.804},{"segment":32.506},{"segment":32.225},{"segment":32.793},{"segment":35.366}],"after":[{"segment":73.963},{"segment":66.352},{"segment":38.128},{"segment":34.042},{"segment":60.032},{"segment":52.661},{"segment":33.536},{"segment":34.057},{"segment":47.836},{"segment":32.645},{"segment":34.434},{"segment":35.691},{"segment":52.506},{"segment":33.558},{"segment":33.263},{"segment":33.906},{"segment":37.044},{"segment":33.419},{"segment":32.864},{"segment":32.019},{"segment":34.45},{"segment":38.159},{"segment":98.528},{"segment":107.129},{"segment":34.58},{"segment":33.254},{"segment":32.583},{"segment":33.152},{"segment":34.358},{"segment":33.518},{"segment":33.175},{"segment":32.21},{"segment":32.857},{"segment":33.26},{"segment":34.674},{"segment":32.557},{"segment":34.974},{"segment":32.927},{"segment":31.763},{"segment":31.894},{"segment":32.03},{"segment":33.158},{"segment":31.923},{"segment":32.674},{"segment":32.123},{"segment":31.893},{"segment":32.329},{"segment":32.89}],"before_control":[{"segment":56.744},{"segment":56.097},{"segment":33.892},{"segment":34.339},{"segment":83.988},{"segment":33.118},{"segment":39.323},{"segment":33.603},{"segment":34.115},{"segment":42.116},{"segment":56.86},{"segment":37.547},{"segment":34.092},{"segment":34.005},{"segment":33.944},{"segment":34.467},{"segment":34.609},{"segment":37.357},{"segment":33.896},{"segment":33.651},{"segment":33.11},{"segment":60.367},{"segment":59.99},{"segment":59.252},{"segment":32.569},{"segment":33.369},{"segment":32.749},{"segment":33.311},{"segment":32.801},{"segment":34.914},{"segment":32.724},{"segment":31.988},{"segment":32.117},{"segment":32.584},{"segment":32.598},{"segment":33.331},{"segment":32.176},{"segment":32.3},{"segment":31.633},{"segment":32.673},{"segment":33.102},{"segment":32.367},{"segment":34.005},{"segment":32.244},{"segment":32.472},{"segment":31.941},{"segment":34.369},{"segment":32.912}],"after_control":[{"segment":70.906},{"segment":38.788},{"segment":33.118},{"segment":36.401},{"segment":48.671},{"segment":38.799},{"segment":68.256},{"segment":33.521},{"segment":63.929},{"segment":55.133},{"segment":34.296},{"segment":34.78},{"segment":33.746},{"segment":32.961},{"segment":33.793},{"segment":34.307},{"segment":33.891},{"segment":36.64},{"segment":34.127},{"segment":33.794},{"segment":33.699},{"segment":56.541},{"segment":72.193},{"segment":36.923},{"segment":33.203},{"segment":33.818},{"segment":32.553},{"segment":32.242},{"segment":32.356},{"segment":34.196},{"segment":32.072},{"segment":32.708},{"segment":32.153},{"segment":34.395},{"segment":35.65},{"segment":33.232},{"segment":32.317},{"segment":32.898},{"segment":31.953},{"segment":31.964},{"segment":32.924},{"segment":32.758},{"segment":32.297},{"segment":32.732},{"segment":32.098},{"segment":32.377},{"segment":32.713},{"segment":32.369}]},"output":["checksum 134000"],"medians_us":{"before":{"segment":33.3915},"after":{"segment":33.4685},"before_control":{"segment":33.627},"after_control":{"segment":33.7225}}},{"mode":"eastAsianWidth","scale":100,"orders":[["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"],["before","after","after_control","before_control"],["after","before","before_control","after_control"],["before_control","after_control","after","before"],["after_control","before_control","before","after"]],"samples":{"before":[{"eastAsianWidth":0.447},{"eastAsianWidth":0.446},{"eastAsianWidth":0.46},{"eastAsianWidth":0.453},{"eastAsianWidth":0.449},{"eastAsianWidth":0.45},{"eastAsianWidth":0.454},{"eastAsianWidth":0.443},{"eastAsianWidth":0.455},{"eastAsianWidth":0.453},{"eastAsianWidth":0.449},{"eastAsianWidth":0.451},{"eastAsianWidth":0.455},{"eastAsianWidth":0.456},{"eastAsianWidth":0.445},{"eastAsianWidth":0.446},{"eastAsianWidth":0.446},{"eastAsianWidth":0.452},{"eastAsianWidth":0.469},{"eastAsianWidth":0.462},{"eastAsianWidth":0.453},{"eastAsianWidth":0.45},{"eastAsianWidth":0.453},{"eastAsianWidth":0.456},{"eastAsianWidth":0.456},{"eastAsianWidth":0.448},{"eastAsianWidth":0.457},{"eastAsianWidth":0.45},{"eastAsianWidth":0.455},{"eastAsianWidth":0.457},{"eastAsianWidth":0.451},{"eastAsianWidth":0.455},{"eastAsianWidth":0.454},{"eastAsianWidth":0.471},{"eastAsianWidth":0.454},{"eastAsianWidth":0.464},{"eastAsianWidth":0.464},{"eastAsianWidth":0.464},{"eastAsianWidth":0.458},{"eastAsianWidth":0.464},{"eastAsianWidth":0.457},{"eastAsianWidth":0.461},{"eastAsianWidth":0.464},{"eastAsianWidth":0.513},{"eastAsianWidth":0.805},{"eastAsianWidth":0.65},{"eastAsianWidth":0.636},{"eastAsianWidth":0.682}],"after":[{"eastAsianWidth":0.449},{"eastAsianWidth":0.431},{"eastAsianWidth":0.447},{"eastAsianWidth":0.448},{"eastAsianWidth":0.445},{"eastAsianWidth":0.432},{"eastAsianWidth":0.441},{"eastAsianWidth":0.46},{"eastAsianWidth":0.432},{"eastAsianWidth":0.445},{"eastAsianWidth":0.43},{"eastAsianWidth":0.427},{"eastAsianWidth":0.445},{"eastAsianWidth":0.432},{"eastAsianWidth":0.447},{"eastAsianWidth":0.446},{"eastAsianWidth":0.446},{"eastAsianWidth":0.427},{"eastAsianWidth":0.445},{"eastAsianWidth":0.431},{"eastAsianWidth":0.447},{"eastAsianWidth":0.443},{"eastAsianWidth":0.443},{"eastAsianWidth":0.451},{"eastAsianWidth":0.449},{"eastAsianWidth":0.447},{"eastAsianWidth":0.449},{"eastAsianWidth":0.473},{"eastAsianWidth":0.454},{"eastAsianWidth":0.433},{"eastAsianWidth":0.446},{"eastAsianWidth":0.448},{"eastAsianWidth":0.471},{"eastAsianWidth":0.434},{"eastAsianWidth":0.451},{"eastAsianWidth":0.434},{"eastAsianWidth":0.452},{"eastAsianWidth":0.459},{"eastAsianWidth":0.454},{"eastAsianWidth":0.457},{"eastAsianWidth":0.446},{"eastAsianWidth":0.454},{"eastAsianWidth":0.477},{"eastAsianWidth":0.615},{"eastAsianWidth":0.626},{"eastAsianWidth":0.767},{"eastAsianWidth":0.577},{"eastAsianWidth":0.65}],"before_control":[{"eastAsianWidth":0.451},{"eastAsianWidth":0.454},{"eastAsianWidth":0.453},{"eastAsianWidth":0.455},{"eastAsianWidth":0.447},{"eastAsianWidth":0.447},{"eastAsianWidth":0.52},{"eastAsianWidth":0.45},{"eastAsianWidth":0.452},{"eastAsianWidth":0.453},{"eastAsianWidth":0.452},{"eastAsianWidth":0.451},{"eastAsianWidth":0.453},{"eastAsianWidth":0.453},{"eastAsianWidth":0.452},{"eastAsianWidth":0.453},{"eastAsianWidth":0.456},{"eastAsianWidth":0.45},{"eastAsianWidth":0.452},{"eastAsianWidth":0.445},{"eastAsianWidth":0.453},{"eastAsianWidth":0.452},{"eastAsianWidth":0.447},{"eastAsianWidth":0.453},{"eastAsianWidth":0.457},{"eastAsianWidth":0.451},{"eastAsianWidth":0.455},{"eastAsianWidth":0.451},{"eastAsianWidth":0.451},{"eastAsianWidth":0.452},{"eastAsianWidth":0.452},{"eastAsianWidth":0.445},{"eastAsianWidth":0.448},{"eastAsianWidth":0.447},{"eastAsianWidth":0.456},{"eastAsianWidth":0.455},{"eastAsianWidth":0.459},{"eastAsianWidth":0.463},{"eastAsianWidth":0.464},{"eastAsianWidth":0.455},{"eastAsianWidth":0.46},{"eastAsianWidth":0.476},{"eastAsianWidth":0.464},{"eastAsianWidth":0.461},{"eastAsianWidth":0.619},{"eastAsianWidth":0.788},{"eastAsianWidth":0.687},{"eastAsianWidth":0.687}],"after_control":[{"eastAsianWidth":0.436},{"eastAsianWidth":0.449},{"eastAsianWidth":0.464},{"eastAsianWidth":0.434},{"eastAsianWidth":0.426},{"eastAsianWidth":0.445},{"eastAsianWidth":0.443},{"eastAsianWidth":0.424},{"eastAsianWidth":0.443},{"eastAsianWidth":0.446},{"eastAsianWidth":0.447},{"eastAsianWidth":0.444},{"eastAsianWidth":0.444},{"eastAsianWidth":0.448},{"eastAsianWidth":0.431},{"eastAsianWidth":0.447},{"eastAsianWidth":0.444},{"eastAsianWidth":0.446},{"eastAsianWidth":0.448},{"eastAsianWidth":0.444},{"eastAsianWidth":0.445},{"eastAsianWidth":0.454},{"eastAsianWidth":0.446},{"eastAsianWidth":0.443},{"eastAsianWidth":0.451},{"eastAsianWidth":0.434},{"eastAsianWidth":0.462},{"eastAsianWidth":0.442},{"eastAsianWidth":0.448},{"eastAsianWidth":0.452},{"eastAsianWidth":0.445},{"eastAsianWidth":0.429},{"eastAsianWidth":0.434},{"eastAsianWidth":0.43},{"eastAsianWidth":0.451},{"eastAsianWidth":0.455},{"eastAsianWidth":0.454},{"eastAsianWidth":0.438},{"eastAsianWidth":0.454},{"eastAsianWidth":0.455},{"eastAsianWidth":0.453},{"eastAsianWidth":0.452},{"eastAsianWidth":0.457},{"eastAsianWidth":0.457},{"eastAsianWidth":0.633},{"eastAsianWidth":0.711},{"eastAsianWidth":0.582},{"eastAsianWidth":0.652}]},"output":["checksum 200000"],"medians_us":{"before":{"eastAsianWidth":0.455},"after":{"eastAsianWidth":0.447},"before_control":{"eastAsianWidth":0.453},"after_control":{"eastAsianWidth":0.4465}}}]}, +{"test262":{"totals":{"pass":1786,"diff":0,"runtime-fail":129,"compile-fail":0,"skip":7},"baseline_failure_pairs_identical":true}}, +{"app_control_statistics":[{"name":"promise_all_chains","before":157.25349999999594,"after":157.21550000000127,"delta":-0.024164803959636494,"ci95":[-5.546877596463995,7.062799364989392],"aa_before":-3.9181220231351843,"aa_after":3.3430256063590624},{"name":"json_parse_1mb","before":84.6979999999995,"after":86.8890000000011,"delta":2.586837941865938,"ci95":[1.323046888781776,3.277527214338938],"aa_before":-0.27810839156577893,"aa_after":-1.0015333107467383}]} +] diff --git a/benchmarks/regexp-construction/measure.py b/benchmarks/regexp-construction/measure.py new file mode 100644 index 0000000000..cd4290ea9a --- /dev/null +++ b/benchmarks/regexp-construction/measure.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +"""Alternate the real-input probe against baseline Perry and Bun; retain samples.""" +import argparse +import json +from pathlib import Path +import shutil +import statistics +import subprocess + + +def positive_integer(value): + try: + number = int(value) + except ValueError: + raise argparse.ArgumentTypeError('expected a positive integer') from None + if number < 1: + raise argparse.ArgumentTypeError('expected a positive integer') + return number + + +def probe_case(value): + mode, separator, scale = value.partition(':') + if not mode or not separator: + raise argparse.ArgumentTypeError('expected MODE:SCALE') + return mode, positive_integer(scale) + + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('--before', required=True) +parser.add_argument('--after', required=True) +parser.add_argument('--source', required=True) +parser.add_argument('--bun', required=True) +parser.add_argument('--output', type=Path, required=True) +parser.add_argument('--runs', type=positive_integer, default=12) +parser.add_argument('--case', type=probe_case, action='append', help='MODE:SCALE; repeat to override the default cases') +parser.add_argument('--paired-controls', action='store_true', help='Compare two identical-copy labels per build in each round, without Bun') +args = parser.parse_args() +commands = {'before': [args.before], 'after': [args.after], 'bun': [args.bun, args.source]} +if args.paired_controls: + commands = {'before': [args.before], 'after': [args.after], + 'before_control': [args.before], 'after_control': [args.after]} +runner = args.output.with_suffix('.runner') +rows = [] +cases = args.case or [ + ('all', 1), ('construct', 10), ('test-ascii', 100), + ('test-emoji', 100), ('stripAnsi', 100), ('stripAnsi-match', 100)] +for mode, scale in cases: + row = {'mode': mode, 'scale': scale, 'orders': [], + 'samples': {label: [] for label in commands}} + expected = None + for iteration in range(args.runs): + order = list(commands) + if args.paired_controls: + order = [('before', 'after', 'after_control', 'before_control'), + ('after', 'before', 'before_control', 'after_control'), + ('before_control', 'after_control', 'after', 'before'), + ('after_control', 'before_control', 'before', 'after')][iteration % 4] + else: + # Rotation plus reversal covers all six orders; reversal alone + # would always run the candidate in the middle of three builds. + offset = iteration % len(order) + order = order[offset:] + order[:offset] + if iteration % 2: + order.reverse() + row['orders'].append(order) + for label in order: + command = commands[label] + if label != 'bun': + # Startup layout can affect allocation/GC inside a timed loop. + # Keep argv[0], execPath, pathname and inode identical. + shutil.copyfile(command[0], runner) + runner.chmod(0o755) + command = [str(runner)] + result = subprocess.run(command + [mode, str(scale)], check=True, + capture_output=True, text=True, timeout=180) + times = {} + other = [] + for line in result.stdout.splitlines(): + fields = line.split() + if len(fields) == 3 and fields[2] == 'us/iter': + times[fields[0]] = float(fields[1]) + else: + other.append(line) + if not times: + raise RuntimeError(f'{mode} {label}: no timing output') + if expected is None: + expected = other + if other != expected: + raise RuntimeError(f'{mode} {label}: checksum mismatch {other} != {expected}') + row['samples'][label].append(times) + row['output'] = expected + row['medians_us'] = {label: {name: statistics.median(s[name] for s in samples) + for name in samples[0]} + for label, samples in row['samples'].items()} + rows.append(row) + args.output.write_text(json.dumps(rows, indent=2) + '\n') + print(json.dumps({k: v for k, v in row.items() if k != 'samples'}), flush=True) diff --git a/benchmarks/regexp-construction/merged-app-samples.json b/benchmarks/regexp-construction/merged-app-samples.json new file mode 100644 index 0000000000..5ecabf1b5e --- /dev/null +++ b/benchmarks/regexp-construction/merged-app-samples.json @@ -0,0 +1,14 @@ +[ + {"name":"batch","node_exit":0,"before":[{"cpu_ms":48.967999999999904,"wall_ms":76.27779105678201},{"cpu_ms":49.393999999999934,"wall_ms":78.90696497634053},{"cpu_ms":48.044999999999675,"wall_ms":59.444249141961336},{"cpu_ms":46.641000000000155,"wall_ms":66.54995493590832},{"cpu_ms":48.73299999999992,"wall_ms":62.476715072989464},{"cpu_ms":45.551000000000116,"wall_ms":56.651429971680045},{"cpu_ms":51.52899999999994,"wall_ms":72.2560971044004},{"cpu_ms":50.73799999999995,"wall_ms":62.43673013523221},{"cpu_ms":51.79499999999982,"wall_ms":63.21077607572079},{"cpu_ms":52.2560000000003,"wall_ms":62.81729112379253},{"cpu_ms":50.511999999999446,"wall_ms":64.03230084106326},{"cpu_ms":48.08600000000052,"wall_ms":61.45280203782022},{"cpu_ms":54.681999999999675,"wall_ms":71.11184997484088},{"cpu_ms":51.24700000000004,"wall_ms":66.56102603301406},{"cpu_ms":50.42399999999958,"wall_ms":73.84622888639569}],"before_median_cpu_ms":50.42399999999958,"after":[{"cpu_ms":57.86099999999994,"wall_ms":71.28303893841803},{"cpu_ms":47.81900000000006,"wall_ms":53.254182916134596},{"cpu_ms":49.64700000000022,"wall_ms":66.42271811142564},{"cpu_ms":51.88999999999977,"wall_ms":73.33286898210645},{"cpu_ms":45.41200000000023,"wall_ms":60.759685933589935},{"cpu_ms":49.22899999999997,"wall_ms":69.88938595168293},{"cpu_ms":49.47699999999999,"wall_ms":70.94045891426504},{"cpu_ms":50.30200000000029,"wall_ms":67.16326111927629},{"cpu_ms":51.68499999999998,"wall_ms":60.31438405625522},{"cpu_ms":49.32299999999934,"wall_ms":64.84918692149222},{"cpu_ms":47.573000000000754,"wall_ms":64.89849998615682},{"cpu_ms":51.09899999999978,"wall_ms":60.55071693845093},{"cpu_ms":50.339000000000134,"wall_ms":63.75316996127367},{"cpu_ms":49.736000000000224,"wall_ms":72.50038417987525},{"cpu_ms":48.21399999999976,"wall_ms":62.043144134804606}],"after_median_cpu_ms":49.64700000000022}, + {"name":"buffer_transcode","node_exit":0,"before":[{"cpu_ms":77.66199999999924,"wall_ms":108.26651216484606},{"cpu_ms":81.5450000000002,"wall_ms":100.3382580820471},{"cpu_ms":71.86200000000031,"wall_ms":105.22267408668995},{"cpu_ms":72.34300000000005,"wall_ms":104.041957994923},{"cpu_ms":83.09999999999995,"wall_ms":132.52628012560308},{"cpu_ms":71.98800000000017,"wall_ms":121.42872414551675},{"cpu_ms":78.13400000000037,"wall_ms":105.6944748852402},{"cpu_ms":73.05499999999921,"wall_ms":129.4343329500407},{"cpu_ms":74.96800000000015,"wall_ms":101.53580503538251},{"cpu_ms":74.3109999999998,"wall_ms":103.62760303542018},{"cpu_ms":76.74300000000045,"wall_ms":106.15273099392653},{"cpu_ms":75.53099999999979,"wall_ms":101.4747810550034},{"cpu_ms":76.66100000000142,"wall_ms":102.1531478036195},{"cpu_ms":71.91499999999884,"wall_ms":118.2651340495795},{"cpu_ms":67.58800000000065,"wall_ms":116.84681288897991}],"before_median_cpu_ms":74.96800000000015,"after":[{"cpu_ms":69.46800000000053,"wall_ms":102.1001001354307},{"cpu_ms":74.16599999999995,"wall_ms":106.19300696998835},{"cpu_ms":76.28900000000004,"wall_ms":105.7132410351187},{"cpu_ms":67.8459999999994,"wall_ms":121.51541584171355},{"cpu_ms":73.89000000000046,"wall_ms":124.75142301991582},{"cpu_ms":71.50899999999982,"wall_ms":126.02993100881577},{"cpu_ms":75.06799999999991,"wall_ms":108.71885693632066},{"cpu_ms":71.1240000000002,"wall_ms":127.39883898757398},{"cpu_ms":75.10700000000003,"wall_ms":127.33779498375952},{"cpu_ms":77.44000000000106,"wall_ms":106.34339810349047},{"cpu_ms":78.59199999999866,"wall_ms":114.2860280815512},{"cpu_ms":77.09600000000094,"wall_ms":108.48448798060417},{"cpu_ms":76.34899999999867,"wall_ms":103.55339501984417},{"cpu_ms":72.40000000000002,"wall_ms":100.7846319116652},{"cpu_ms":74.73300000000016,"wall_ms":97.3661239258945}],"after_median_cpu_ms":74.73300000000016}, + {"name":"date_format_parse","node_exit":0,"before":[{"cpu_ms":49.85199999999956,"wall_ms":61.39920186251402},{"cpu_ms":50.17300000000091,"wall_ms":69.57080005668104},{"cpu_ms":47.96299999999931,"wall_ms":58.45732497982681},{"cpu_ms":41.47500000000015,"wall_ms":51.78620992228389},{"cpu_ms":46.07900000000065,"wall_ms":60.656273970380425},{"cpu_ms":46.07599999999934,"wall_ms":64.63092984631658},{"cpu_ms":50.37699999999923,"wall_ms":62.70115403458476},{"cpu_ms":48.15000000000147,"wall_ms":58.5210039280355},{"cpu_ms":48.9259999999998,"wall_ms":73.19184602238238},{"cpu_ms":48.73199999999933,"wall_ms":73.67278402671218},{"cpu_ms":40.55800000000076,"wall_ms":50.63703306950629},{"cpu_ms":41.194999999998316,"wall_ms":62.5855082180351},{"cpu_ms":47.95200000000044,"wall_ms":71.35451305657625},{"cpu_ms":46.61800000000049,"wall_ms":66.96124421432614},{"cpu_ms":48.40599999999995,"wall_ms":59.77572803385556}],"before_median_cpu_ms":47.96299999999931,"after":[{"cpu_ms":51.36800000000008,"wall_ms":60.76185102574527},{"cpu_ms":48.75700000000016,"wall_ms":59.1799549292773},{"cpu_ms":46.70400000000008,"wall_ms":58.14773589372635},{"cpu_ms":46.509000000000356,"wall_ms":57.88449407555163},{"cpu_ms":44.85599999999934,"wall_ms":58.25957586057484},{"cpu_ms":47.34700000000025,"wall_ms":61.37639912776649},{"cpu_ms":48.60500000000023,"wall_ms":61.17911008186638},{"cpu_ms":51.17599999999989,"wall_ms":59.652648866176605},{"cpu_ms":49.79900000000015,"wall_ms":61.600456945598125},{"cpu_ms":44.470999999999705,"wall_ms":61.056600883603096},{"cpu_ms":40.28300000000051,"wall_ms":62.755014980211854},{"cpu_ms":47.0000000000006,"wall_ms":66.48001610301435},{"cpu_ms":48.318999999999335,"wall_ms":68.6823099385947},{"cpu_ms":47.266000000000474,"wall_ms":58.82501206360757},{"cpu_ms":49.19699999999949,"wall_ms":60.736623127013445}],"after_median_cpu_ms":47.34700000000025}, + {"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":82.19900000000102,"wall_ms":101.69708682224154},{"cpu_ms":86.27399999999952,"wall_ms":98.78999483771622},{"cpu_ms":88.78100000000089,"wall_ms":103.18105900660157},{"cpu_ms":88.99499999999883,"wall_ms":96.43168817274272},{"cpu_ms":91.74499999999952,"wall_ms":107.19818598590791},{"cpu_ms":95.03400000000006,"wall_ms":119.50334697030485},{"cpu_ms":89.283,"wall_ms":109.30180596187711},{"cpu_ms":89.3170000000012,"wall_ms":117.27229808457196},{"cpu_ms":84.62600000000009,"wall_ms":98.95113506354392},{"cpu_ms":88.07900000000046,"wall_ms":100.30208993703127},{"cpu_ms":79.5199999999987,"wall_ms":99.74746406078339},{"cpu_ms":81.13499999999974,"wall_ms":106.0477348510176},{"cpu_ms":88.70699999999943,"wall_ms":116.7424579616636},{"cpu_ms":87.6330000000003,"wall_ms":111.96659388951957},{"cpu_ms":88.98099999999687,"wall_ms":116.7723941616714}],"before_median_cpu_ms":88.70699999999943,"after":[{"cpu_ms":80.95100000000066,"wall_ms":105.15527799725533},{"cpu_ms":82.09599999999995,"wall_ms":89.7034069057554},{"cpu_ms":82.94399999999946,"wall_ms":104.61158282123506},{"cpu_ms":93.75100000000103,"wall_ms":105.76175199821591},{"cpu_ms":86.4849999999997,"wall_ms":98.03556487895548},{"cpu_ms":87.75600000000061,"wall_ms":97.21269807778299},{"cpu_ms":89.01000000000003,"wall_ms":107.38620697520673},{"cpu_ms":94.17099999999934,"wall_ms":107.68984397873282},{"cpu_ms":89.3499999999996,"wall_ms":107.5189639814198},{"cpu_ms":83.80599999999916,"wall_ms":97.79544686898589},{"cpu_ms":83.61400000000074,"wall_ms":102.41685202345252},{"cpu_ms":80.33900000000216,"wall_ms":112.48489306308329},{"cpu_ms":86.84699999999879,"wall_ms":110.71356292814016},{"cpu_ms":90.21200000000107,"wall_ms":118.27434110455215},{"cpu_ms":84.41900000000047,"wall_ms":124.5867449324578}],"after_median_cpu_ms":86.4849999999997}, + {"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":141.3499999999992,"wall_ms":162.9224440548569},{"cpu_ms":131.3120000000012,"wall_ms":143.54762202128768},{"cpu_ms":135.48899999999975,"wall_ms":153.96358398720622},{"cpu_ms":139.66900000000138,"wall_ms":159.35110091231763},{"cpu_ms":136.22000000000156,"wall_ms":159.39567307941616},{"cpu_ms":131.8570000000001,"wall_ms":161.048331996426},{"cpu_ms":136.16400000000084,"wall_ms":143.32698984071612},{"cpu_ms":130.74100000000044,"wall_ms":142.26445509120822},{"cpu_ms":126.1650000000003,"wall_ms":145.22557682357728},{"cpu_ms":144.93199999999717,"wall_ms":165.325412992388},{"cpu_ms":142.55599999999902,"wall_ms":164.17044587433338},{"cpu_ms":142.1270000000021,"wall_ms":178.70133509859443},{"cpu_ms":145.97900000000053,"wall_ms":152.95301587320864},{"cpu_ms":127.62899999999888,"wall_ms":139.3774908501655},{"cpu_ms":133.0730000000031,"wall_ms":140.59060695581138}],"before_median_cpu_ms":136.16400000000084,"after":[{"cpu_ms":136.28699999999938,"wall_ms":158.6002770345658},{"cpu_ms":143.12500000000128,"wall_ms":163.8899310491979},{"cpu_ms":138.73399999999947,"wall_ms":151.55078587122262},{"cpu_ms":129.70799999999727,"wall_ms":153.19487801752985},{"cpu_ms":135.53399999999982,"wall_ms":154.11282307468355},{"cpu_ms":132.2359999999989,"wall_ms":150.90390807017684},{"cpu_ms":127.00500000000048,"wall_ms":144.94784898124635},{"cpu_ms":135.23099999999744,"wall_ms":156.22542006894946},{"cpu_ms":132.9550000000026,"wall_ms":154.15823785588145},{"cpu_ms":142.6360000000031,"wall_ms":155.14095476828516},{"cpu_ms":141.70199999999866,"wall_ms":165.07013607770205},{"cpu_ms":142.24399999999804,"wall_ms":158.99708005599678},{"cpu_ms":144.06699999999972,"wall_ms":158.96985796280205},{"cpu_ms":148.23700000000173,"wall_ms":172.66768193803728},{"cpu_ms":145.94599999999858,"wall_ms":170.34811805933714}],"after_median_cpu_ms":138.73399999999947}, + {"name":"map_1m","node_exit":0,"before":[{"cpu_ms":350.9610000000016,"wall_ms":392.5805799663067},{"cpu_ms":338.0359999999989,"wall_ms":404.6619029249996},{"cpu_ms":339.30100000000255,"wall_ms":366.13246286287904},{"cpu_ms":345.9749999999992,"wall_ms":385.489400010556},{"cpu_ms":336.47600000000114,"wall_ms":459.83968093059957},{"cpu_ms":334.3510000000016,"wall_ms":375.359513098374},{"cpu_ms":339.85799999999955,"wall_ms":351.272847969085},{"cpu_ms":333.0750000000009,"wall_ms":359.61913294158876},{"cpu_ms":332.6769999999968,"wall_ms":393.22944288142025},{"cpu_ms":337.7480000000048,"wall_ms":355.30480090528727},{"cpu_ms":337.5959999999978,"wall_ms":366.0864969715476},{"cpu_ms":332.05300000000193,"wall_ms":354.55542197450995},{"cpu_ms":316.8100000000038,"wall_ms":343.4769921004772},{"cpu_ms":319.54499999999797,"wall_ms":340.0400399696082},{"cpu_ms":337.45299999999645,"wall_ms":358.2100400235504}],"before_median_cpu_ms":337.45299999999645,"after":[{"cpu_ms":336.3820000000004,"wall_ms":381.949775852263},{"cpu_ms":335.06200000000064,"wall_ms":348.5753578133881},{"cpu_ms":344.3189999999987,"wall_ms":354.06577680259943},{"cpu_ms":343.24299999999755,"wall_ms":357.98744414933026},{"cpu_ms":326.8360000000001,"wall_ms":361.12834280356765},{"cpu_ms":343.16099999999847,"wall_ms":384.9579568486661},{"cpu_ms":333.1839999999993,"wall_ms":373.41305683366954},{"cpu_ms":323.36099999999846,"wall_ms":346.0431268904358},{"cpu_ms":340.9150000000025,"wall_ms":357.55031695589423},{"cpu_ms":329.8859999999948,"wall_ms":355.288322083652},{"cpu_ms":333.28800000000314,"wall_ms":349.84878706745803},{"cpu_ms":332.59900000000187,"wall_ms":344.3773139733821},{"cpu_ms":333.18599999999776,"wall_ms":372.8089169599116},{"cpu_ms":320.36599999999993,"wall_ms":353.6348408088088},{"cpu_ms":319.95799999999974,"wall_ms":355.8961870148778}],"after_median_cpu_ms":333.18599999999776}, + {"name":"object_deep_clone","node_exit":0,"before":[{"cpu_ms":29.125999999997987,"wall_ms":41.71629506163299},{"cpu_ms":30.312000000002115,"wall_ms":38.805386051535606},{"cpu_ms":27.64100000000269,"wall_ms":59.164826991036534},{"cpu_ms":27.09600000000023,"wall_ms":44.56210299395025},{"cpu_ms":26.74999999999983,"wall_ms":39.15144316852093},{"cpu_ms":28.726999999996394,"wall_ms":39.12141709588468},{"cpu_ms":30.7200000000023,"wall_ms":39.091581013053656},{"cpu_ms":27.217999999997744,"wall_ms":37.691524950787425},{"cpu_ms":29.958000000000595,"wall_ms":40.439789881929755},{"cpu_ms":32.3449999999994,"wall_ms":42.753083165735006},{"cpu_ms":30.71400000000324,"wall_ms":39.22666306607425},{"cpu_ms":30.394000000001142,"wall_ms":39.69861497171223},{"cpu_ms":31.992999999999938,"wall_ms":39.51752698048949},{"cpu_ms":27.330000000006294,"wall_ms":34.87635403871536},{"cpu_ms":32.06000000000131,"wall_ms":40.22198403254151}],"before_median_cpu_ms":29.958000000000595,"after":[{"cpu_ms":26.05100000000249,"wall_ms":36.56894783489406},{"cpu_ms":27.28599999999659,"wall_ms":38.15474081784487},{"cpu_ms":27.66799999999847,"wall_ms":33.0309069249779},{"cpu_ms":29.259000000003255,"wall_ms":42.22503490746021},{"cpu_ms":27.352999999997962,"wall_ms":37.06782986409962},{"cpu_ms":30.599000000002263,"wall_ms":37.0517389383167},{"cpu_ms":27.842999999997176,"wall_ms":38.20146806538105},{"cpu_ms":29.322000000000514,"wall_ms":45.62639119103551},{"cpu_ms":30.73100000000295,"wall_ms":42.258207919076085},{"cpu_ms":31.584000000002277,"wall_ms":40.107318898662925},{"cpu_ms":29.86699999999587,"wall_ms":41.337416972965},{"cpu_ms":28.16999999999581,"wall_ms":40.12611391954124},{"cpu_ms":28.790999999998235,"wall_ms":42.141899932175875},{"cpu_ms":29.684999999993522,"wall_ms":39.03780994005501},{"cpu_ms":28.379000000001042,"wall_ms":40.81384907476604}],"after_median_cpu_ms":28.790999999998235}, + {"name":"promise_all_chains","node_exit":0,"before":[{"cpu_ms":199.44200000000478,"wall_ms":221.82042198255658},{"cpu_ms":193.73899999999367,"wall_ms":249.5327009819448},{"cpu_ms":186.38099999999724,"wall_ms":228.23514696210623},{"cpu_ms":185.18900000000116,"wall_ms":209.74014000967145},{"cpu_ms":188.74199999999774,"wall_ms":222.0710599794984},{"cpu_ms":185.53500000000156,"wall_ms":215.04326886497438},{"cpu_ms":189.20599999999865,"wall_ms":198.56635201722383},{"cpu_ms":192.18200000000252,"wall_ms":202.74196495302022},{"cpu_ms":190.16500000000036,"wall_ms":196.99006783775985},{"cpu_ms":190.25800000000004,"wall_ms":199.57048911601305},{"cpu_ms":187.37600000000043,"wall_ms":193.16874793730676},{"cpu_ms":193.22799999999773,"wall_ms":201.18635799735785},{"cpu_ms":184.7799999999964,"wall_ms":211.3211138639599},{"cpu_ms":198.58500000000134,"wall_ms":215.4369750060141},{"cpu_ms":182.60499999999524,"wall_ms":199.13187995553017}],"before_median_cpu_ms":189.20599999999865,"after":[{"cpu_ms":186.0759999999999,"wall_ms":198.90679907985032},{"cpu_ms":194.1850000000045,"wall_ms":235.87791807949543},{"cpu_ms":192.2089999999983,"wall_ms":221.76111093722284},{"cpu_ms":185.2000000000018,"wall_ms":201.1477560736239},{"cpu_ms":189.7960000000012,"wall_ms":203.9035949856043},{"cpu_ms":187.88699999999636,"wall_ms":193.38657497428358},{"cpu_ms":189.1600000000011,"wall_ms":211.34951803833246},{"cpu_ms":191.7230000000032,"wall_ms":200.2666098996997},{"cpu_ms":187.79499999999416,"wall_ms":194.35278000310063},{"cpu_ms":163.82200000000324,"wall_ms":174.3830570485443},{"cpu_ms":161.95100000000195,"wall_ms":168.79254183731973},{"cpu_ms":194.7279999999978,"wall_ms":203.55096692219377},{"cpu_ms":189.33300000000486,"wall_ms":193.89010616578162},{"cpu_ms":177.2020000000012,"wall_ms":200.2562100533396},{"cpu_ms":177.937,"wall_ms":205.25060291402042}],"after_median_cpu_ms":187.88699999999636}, + {"name":"regex_replace","node_exit":0,"before":[{"cpu_ms":5087.9580000000005,"wall_ms":5120.895009953529},{"cpu_ms":5275.942000000001,"wall_ms":5307.463165838271},{"cpu_ms":5479.681,"wall_ms":5523.566068150103},{"cpu_ms":5520.763000000003,"wall_ms":5571.400436107069},{"cpu_ms":5535.753,"wall_ms":5560.4019379243255},{"cpu_ms":5575.169000000003,"wall_ms":5620.984534965828},{"cpu_ms":5377.650000000002,"wall_ms":5444.869714090601},{"cpu_ms":5396.515999999991,"wall_ms":5508.975584991276},{"cpu_ms":5375.9630000000125,"wall_ms":5484.00834784843},{"cpu_ms":5378.5239999999985,"wall_ms":5433.583691017702},{"cpu_ms":5325.925000000012,"wall_ms":5460.780640132725},{"cpu_ms":5244.403000000006,"wall_ms":5446.781413862482},{"cpu_ms":5416.969999999992,"wall_ms":5444.087613141164},{"cpu_ms":5392.09600000001,"wall_ms":5416.55068192631},{"cpu_ms":5462.243999999999,"wall_ms":5494.837640086189}],"before_median_cpu_ms":5392.09600000001,"after":[{"cpu_ms":4432.502999999997,"wall_ms":4487.847696989775},{"cpu_ms":4316.944000000007,"wall_ms":4381.882878020406},{"cpu_ms":4391.768999999997,"wall_ms":4428.276133025065},{"cpu_ms":4388.143999999997,"wall_ms":4452.338655013591},{"cpu_ms":4287.930000000003,"wall_ms":4329.373961081728},{"cpu_ms":4543.256999999997,"wall_ms":4572.452455991879},{"cpu_ms":4566.316999999998,"wall_ms":4614.3961059860885},{"cpu_ms":4407.387999999997,"wall_ms":4490.473503945395},{"cpu_ms":4472.262000000001,"wall_ms":4578.180162934586},{"cpu_ms":4577.4000000000115,"wall_ms":4776.562663028017},{"cpu_ms":4534.092999999984,"wall_ms":4638.807184062898},{"cpu_ms":4424.200999999983,"wall_ms":4597.365191904828},{"cpu_ms":4510.06000000001,"wall_ms":4588.708036812022},{"cpu_ms":4547.702999999984,"wall_ms":4614.208966959268},{"cpu_ms":4489.362999999998,"wall_ms":4579.895153874531}],"after_median_cpu_ms":4472.262000000001}, + {"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":48.612999999988915,"wall_ms":54.11574291065335},{"cpu_ms":47.139000000015585,"wall_ms":54.54803095199168},{"cpu_ms":47.77999999998883,"wall_ms":54.20821602456272},{"cpu_ms":48.657000000019934,"wall_ms":53.401980083435774},{"cpu_ms":49.56599999999867,"wall_ms":54.282143944874406},{"cpu_ms":46.877999999992426,"wall_ms":54.41140593029559},{"cpu_ms":49.419000000000324,"wall_ms":53.737637121230364},{"cpu_ms":46.81899999999928,"wall_ms":53.153875982388854},{"cpu_ms":49.087000000014314,"wall_ms":53.42332902364433},{"cpu_ms":45.42399999999702,"wall_ms":53.661945974454284},{"cpu_ms":46.2970000000098,"wall_ms":54.95171481743455},{"cpu_ms":47.765999999995756,"wall_ms":54.09257998690009},{"cpu_ms":47.33799999999633,"wall_ms":50.82715908065438},{"cpu_ms":44.693999999992684,"wall_ms":52.325227996334434},{"cpu_ms":41.767000000021426,"wall_ms":49.2585280444473}],"before_median_cpu_ms":47.33799999999633,"after":[{"cpu_ms":51.79100000000858,"wall_ms":54.235337069258094},{"cpu_ms":47.30899999998428,"wall_ms":55.64909800887108},{"cpu_ms":45.61100000000806,"wall_ms":54.940203903242946},{"cpu_ms":52.449999999993224,"wall_ms":54.86097512766719},{"cpu_ms":47.22399999999993,"wall_ms":54.76633901707828},{"cpu_ms":50.12100000001851,"wall_ms":55.52520719356835},{"cpu_ms":47.648999999978514,"wall_ms":56.022406090050936},{"cpu_ms":51.20300000001521,"wall_ms":55.57223502546549},{"cpu_ms":50.19899999999211,"wall_ms":55.6179010309279},{"cpu_ms":51.322999999996455,"wall_ms":55.73891592212021},{"cpu_ms":50.664999999980864,"wall_ms":56.150245014578104},{"cpu_ms":48.372000000000526,"wall_ms":55.831469129770994},{"cpu_ms":42.35400000001732,"wall_ms":50.69549195468426},{"cpu_ms":44.79699999998843,"wall_ms":52.654653089120984},{"cpu_ms":45.19600000000423,"wall_ms":52.632762119174004}],"after_median_cpu_ms":48.372000000000526}, + {"name":"string_split_map_join","node_exit":0,"before":[{"cpu_ms":298.00999999997657,"wall_ms":303.3527438528836},{"cpu_ms":306.65400000000886,"wall_ms":313.0193550605327},{"cpu_ms":302.57600000001617,"wall_ms":308.02331981249154},{"cpu_ms":302.6360000000068,"wall_ms":309.103038161993},{"cpu_ms":307.3980000000063,"wall_ms":311.8100459687412},{"cpu_ms":302.9940000000124,"wall_ms":308.3367052022368},{"cpu_ms":277.58099999999786,"wall_ms":285.07564309984446},{"cpu_ms":286.8970000000104,"wall_ms":292.29627503082156},{"cpu_ms":297.6769999999931,"wall_ms":305.78314512968063},{"cpu_ms":308.03399999999215,"wall_ms":314.43106289952993},{"cpu_ms":287.2739999999965,"wall_ms":295.9850770421326},{"cpu_ms":272.7859999999964,"wall_ms":282.37881418317556},{"cpu_ms":304.65200000000436,"wall_ms":309.0216349810362},{"cpu_ms":302.78200000000766,"wall_ms":309.18305716477334},{"cpu_ms":304.5060000000035,"wall_ms":311.8810490705073}],"before_median_cpu_ms":302.6360000000068,"after":[{"cpu_ms":296.4860000000158,"wall_ms":306.9166820496321},{"cpu_ms":306.8360000000041,"wall_ms":312.24955688230693},{"cpu_ms":301.78699999999026,"wall_ms":305.14720594510436},{"cpu_ms":296.5599999999995,"wall_ms":300.89754587970674},{"cpu_ms":306.63099999998167,"wall_ms":312.05996312201023},{"cpu_ms":303.7159999999801,"wall_ms":312.0380418840796},{"cpu_ms":304.0450000000021,"wall_ms":309.4758039806038},{"cpu_ms":308.1499999999835,"wall_ms":312.54794518463314},{"cpu_ms":304.7920000000204,"wall_ms":310.1986050605774},{"cpu_ms":307.3480000000188,"wall_ms":314.8040489759296},{"cpu_ms":298.71699999998214,"wall_ms":307.12045286782086},{"cpu_ms":256.25700000000506,"wall_ms":263.63108586519957},{"cpu_ms":272.32100000000514,"wall_ms":279.7204770613462},{"cpu_ms":304.36499999999,"wall_ms":307.7577950898558},{"cpu_ms":303.0679999999961,"wall_ms":307.47250095009804}],"after_median_cpu_ms":303.7159999999801}, + {"name":"string_template_interp","node_exit":0,"before":[{"cpu_ms":69.11699999997722,"wall_ms":77.51281000673771},{"cpu_ms":81.22099999999932,"wall_ms":85.61347494833171},{"cpu_ms":72.27000000000317,"wall_ms":80.6620039511472},{"cpu_ms":71.72699999998144,"wall_ms":83.08990998193622},{"cpu_ms":78.7300000000073,"wall_ms":86.39058889821172},{"cpu_ms":79.31800000000067,"wall_ms":86.77616016939282},{"cpu_ms":75.49699999998438,"wall_ms":86.25578600913286},{"cpu_ms":78.35000000000036,"wall_ms":85.78268205747008},{"cpu_ms":79.13800000000037,"wall_ms":85.54316405206919},{"cpu_ms":76.7099999999914,"wall_ms":86.07585984282196},{"cpu_ms":79.46400000000153,"wall_ms":86.81054203771055},{"cpu_ms":83.57899999998608,"wall_ms":86.97336702607572},{"cpu_ms":78.60299999998688,"wall_ms":85.99478797987103},{"cpu_ms":74.87700000001496,"wall_ms":85.26508510112762},{"cpu_ms":79.62000000000558,"wall_ms":87.6463430467993}],"before_median_cpu_ms":78.60299999998688,"after":[{"cpu_ms":79.5750000000055,"wall_ms":82.98996416851878},{"cpu_ms":83.43100000001868,"wall_ms":88.8263089582324},{"cpu_ms":81.6949999999963,"wall_ms":87.01946283690631},{"cpu_ms":81.89500000000294,"wall_ms":87.35351799987257},{"cpu_ms":77.85100000000966,"wall_ms":87.21888507716358},{"cpu_ms":81.5669999999784,"wall_ms":89.5991229917854},{"cpu_ms":86.93000000002371,"wall_ms":91.31975821219385},{"cpu_ms":82.34200000001124,"wall_ms":87.77435310184956},{"cpu_ms":75.89999999999009,"wall_ms":83.61816802062094},{"cpu_ms":84.81300000002534,"wall_ms":88.18484004586935},{"cpu_ms":81.45799999999781,"wall_ms":88.88911595568061},{"cpu_ms":83.26500000001147,"wall_ms":88.70787709020078},{"cpu_ms":81.88599999999724,"wall_ms":89.30005296133459},{"cpu_ms":80.60299999999643,"wall_ms":88.08217803016305},{"cpu_ms":77.73799999998232,"wall_ms":88.08444207534194}],"after_median_cpu_ms":81.6949999999963} +] diff --git a/benchmarks/regexp-construction/merged-control-samples.json b/benchmarks/regexp-construction/merged-control-samples.json new file mode 100644 index 0000000000..17e4c545e8 --- /dev/null +++ b/benchmarks/regexp-construction/merged-control-samples.json @@ -0,0 +1,12 @@ +[ + {"name":"json-paired","rows":[{"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":122.56399999999968,"wall_ms":173.5689640045166},{"cpu_ms":136.20799999999988,"wall_ms":188.58993495814502},{"cpu_ms":139.7400000000002,"wall_ms":186.3040740136057},{"cpu_ms":139.98200000000028,"wall_ms":176.58833716996014},{"cpu_ms":116.05799999999977,"wall_ms":134.2680191155523},{"cpu_ms":111.09900000000027,"wall_ms":161.38196899555624},{"cpu_ms":115.97100000000005,"wall_ms":159.268279094249},{"cpu_ms":115.77299999999991,"wall_ms":148.67256698198617},{"cpu_ms":143.94300000000015,"wall_ms":168.19554194808006},{"cpu_ms":145.75899999999996,"wall_ms":168.50249585695565},{"cpu_ms":144.66000000000003,"wall_ms":198.24258401058614},{"cpu_ms":144.85700000000003,"wall_ms":150.98352590575814},{"cpu_ms":143.5530000000007,"wall_ms":176.19966994971037},{"cpu_ms":142.2460000000001,"wall_ms":147.75251899845898},{"cpu_ms":134.01200000000023,"wall_ms":180.19459606148303},{"cpu_ms":136.9019999999992,"wall_ms":193.7614989001304},{"cpu_ms":132.83700000000033,"wall_ms":143.49839789792895},{"cpu_ms":144.3000000000012,"wall_ms":199.91853483952582},{"cpu_ms":140.31300000000078,"wall_ms":165.05461209453642},{"cpu_ms":138.99999999999935,"wall_ms":166.5174060035497},{"cpu_ms":145.6300000000006,"wall_ms":191.34833896532655},{"cpu_ms":138.54599999999982,"wall_ms":171.06358101591468},{"cpu_ms":144.44800000000058,"wall_ms":199.67736396938562},{"cpu_ms":143.930000000001,"wall_ms":197.63770303688943},{"cpu_ms":132.75400000000025,"wall_ms":195.1111708767712},{"cpu_ms":131.52399999999886,"wall_ms":183.8901611045003},{"cpu_ms":140.17899999999983,"wall_ms":232.48691810294986},{"cpu_ms":141.91999999999894,"wall_ms":161.3992010243237},{"cpu_ms":135.5189999999986,"wall_ms":228.9470329415053},{"cpu_ms":141.44999999999897,"wall_ms":156.8751661106944},{"cpu_ms":130.57299999999827,"wall_ms":149.13523104041815},{"cpu_ms":143.42699999999908,"wall_ms":194.8984928894788},{"cpu_ms":139.07700000000034,"wall_ms":184.60381380282342},{"cpu_ms":143.4110000000004,"wall_ms":194.67035587877035},{"cpu_ms":142.00799999999703,"wall_ms":194.38202702440321},{"cpu_ms":143.7659999999994,"wall_ms":172.70931601524353},{"cpu_ms":137.98900000000103,"wall_ms":161.48371691815555},{"cpu_ms":126.19399999999814,"wall_ms":143.00330192781985},{"cpu_ms":123.49299999999985,"wall_ms":147.269754903391},{"cpu_ms":130.61499999999882,"wall_ms":179.9180069938302},{"cpu_ms":126.0140000000014,"wall_ms":177.50295414589345},{"cpu_ms":130.7229999999997,"wall_ms":215.0113491807133},{"cpu_ms":129.18699999999816,"wall_ms":184.31851104833186},{"cpu_ms":129.67300000000037,"wall_ms":270.89007408358157},{"cpu_ms":125.29599999999874,"wall_ms":207.6816309709102},{"cpu_ms":126.23800000000074,"wall_ms":134.42670414224267},{"cpu_ms":129.02899999999917,"wall_ms":164.03039684519172},{"cpu_ms":118.94999999999811,"wall_ms":170.20667786709964},{"cpu_ms":137.7190000000006,"wall_ms":187.04258115030825},{"cpu_ms":134.4709999999978,"wall_ms":158.88364892452955},{"cpu_ms":142.7259999999997,"wall_ms":149.24658904783428},{"cpu_ms":139.22600000000074,"wall_ms":150.44116089120507},{"cpu_ms":135.86899999999957,"wall_ms":167.11179516278207},{"cpu_ms":146.34200000000064,"wall_ms":200.6914981175214},{"cpu_ms":130.7210000000012,"wall_ms":184.1100708115846},{"cpu_ms":143.70300000000213,"wall_ms":207.0259649772197},{"cpu_ms":139.48399999999594,"wall_ms":147.2006649710238},{"cpu_ms":138.98299999999608,"wall_ms":176.4522090088576},{"cpu_ms":125.53199999999975,"wall_ms":147.0108600333333},{"cpu_ms":143.45699999999795,"wall_ms":200.263838050887},{"cpu_ms":144.06099999999356,"wall_ms":173.11769793741405},{"cpu_ms":134.88799999999657,"wall_ms":190.89603098109365},{"cpu_ms":135.46300000000144,"wall_ms":183.15012590028346},{"cpu_ms":140.38099999999787,"wall_ms":162.8732040990144},{"cpu_ms":123.37600000000037,"wall_ms":138.98902805522084},{"cpu_ms":130.86799999999954,"wall_ms":142.01265899464488},{"cpu_ms":136.85100000000006,"wall_ms":207.06550916656852},{"cpu_ms":136.85199999999753,"wall_ms":147.3033670336008},{"cpu_ms":142.8900000000013,"wall_ms":198.39817099273205},{"cpu_ms":142.0790000000025,"wall_ms":198.55048600584269},{"cpu_ms":122.19400000000036,"wall_ms":170.67389003932476},{"cpu_ms":128.0929999999998,"wall_ms":165.75077013112605},{"cpu_ms":138.50699999999705,"wall_ms":194.985494017601},{"cpu_ms":140.26700000000147,"wall_ms":199.4954920373857},{"cpu_ms":212.68500000000046,"wall_ms":284.99618195928633},{"cpu_ms":138.1939999999986,"wall_ms":172.85241093486547},{"cpu_ms":140.56899999999928,"wall_ms":164.04568287543952},{"cpu_ms":130.56399999999968,"wall_ms":187.92417692020535},{"cpu_ms":137.95500000000516,"wall_ms":199.10772587172687},{"cpu_ms":139.00499999999738,"wall_ms":192.5072220619768},{"cpu_ms":137.30699999999985,"wall_ms":193.79545911215246},{"cpu_ms":144.92500000000064,"wall_ms":188.00772307440639},{"cpu_ms":141.9660000000036,"wall_ms":159.27782282233238},{"cpu_ms":137.05699999999865,"wall_ms":196.98965805582702},{"cpu_ms":145.45900000000245,"wall_ms":197.87672581151128},{"cpu_ms":139.43900000000298,"wall_ms":191.91334210336208},{"cpu_ms":145.14400000000194,"wall_ms":156.58113406971097},{"cpu_ms":130.822000000002,"wall_ms":182.6223989482969},{"cpu_ms":138.56899999999683,"wall_ms":195.90819696895778},{"cpu_ms":141.76499999999947,"wall_ms":214.12150305695832},{"cpu_ms":144.9349999999967,"wall_ms":206.31362195126712},{"cpu_ms":141.3129999999967,"wall_ms":294.6763411164284},{"cpu_ms":139.95700000000255,"wall_ms":188.602453796193},{"cpu_ms":129.09499999999952,"wall_ms":165.56870890781283},{"cpu_ms":131.36799999999482,"wall_ms":182.01485299505293},{"cpu_ms":128.21399999999983,"wall_ms":166.71447013504803},{"cpu_ms":130.38500000000397,"wall_ms":181.45561800338328},{"cpu_ms":139.69900000000024,"wall_ms":192.66916322521865},{"cpu_ms":128.69200000000092,"wall_ms":169.04405294917524},{"cpu_ms":146.53700000000214,"wall_ms":201.84890809468925},{"cpu_ms":128.7410000000051,"wall_ms":146.05950098484755}],"before_median_cpu_ms":138.50699999999705,"after":[{"cpu_ms":134.71199999999993,"wall_ms":161.58787487074733},{"cpu_ms":135.60700000000026,"wall_ms":193.87864018790424},{"cpu_ms":139.2850000000001,"wall_ms":154.59885308519006},{"cpu_ms":116.7359999999995,"wall_ms":142.1432059723884},{"cpu_ms":118.3930000000002,"wall_ms":163.95243303850293},{"cpu_ms":117.49299999999963,"wall_ms":154.74836202338338},{"cpu_ms":114.64699999999972,"wall_ms":164.0727980993688},{"cpu_ms":133.4439999999999,"wall_ms":189.6224149968475},{"cpu_ms":140.0300000000003,"wall_ms":193.15189099870622},{"cpu_ms":143.4350000000002,"wall_ms":167.0098768081516},{"cpu_ms":129.9960000000002,"wall_ms":181.87351315282285},{"cpu_ms":142.339999999999,"wall_ms":193.29542806372046},{"cpu_ms":141.8210000000002,"wall_ms":173.26153884641826},{"cpu_ms":138.7969999999985,"wall_ms":153.39032490737736},{"cpu_ms":137.11800000000096,"wall_ms":163.43765100464225},{"cpu_ms":146.59600000000063,"wall_ms":200.9230931289494},{"cpu_ms":132.3989999999995,"wall_ms":168.54720911942422},{"cpu_ms":132.22899999999882,"wall_ms":181.67317798361182},{"cpu_ms":146.1439999999996,"wall_ms":175.06767413578928},{"cpu_ms":145.66500000000104,"wall_ms":221.11916402354836},{"cpu_ms":145.97399999999894,"wall_ms":219.09648505970836},{"cpu_ms":134.26399999999995,"wall_ms":210.65540984272957},{"cpu_ms":141.53899999999987,"wall_ms":189.01436706073582},{"cpu_ms":143.55799999999874,"wall_ms":146.6786409728229},{"cpu_ms":137.671000000001,"wall_ms":176.57150398008525},{"cpu_ms":136.58500000000018,"wall_ms":192.718590144068},{"cpu_ms":139.91899999999902,"wall_ms":182.34690790995955},{"cpu_ms":131.0910000000014,"wall_ms":181.388305965811},{"cpu_ms":141.93500000000014,"wall_ms":187.4804210383445},{"cpu_ms":145.63500000000218,"wall_ms":191.74293614923954},{"cpu_ms":135.16800000000018,"wall_ms":190.69991703145206},{"cpu_ms":145.36600000000277,"wall_ms":202.69016386009753},{"cpu_ms":144.20699999999798,"wall_ms":188.20497393608093},{"cpu_ms":139.88099999999903,"wall_ms":185.33835699781775},{"cpu_ms":136.98200000000327,"wall_ms":197.58501299656928},{"cpu_ms":139.70399999999827,"wall_ms":197.12284998968244},{"cpu_ms":141.14100000000107,"wall_ms":196.59339007921517},{"cpu_ms":133.37999999999894,"wall_ms":190.83458790555596},{"cpu_ms":217.21900000000005,"wall_ms":260.7709940057248},{"cpu_ms":126.40900000000244,"wall_ms":178.80516801960766},{"cpu_ms":130.04000000000104,"wall_ms":137.13152799755335},{"cpu_ms":129.1979999999988,"wall_ms":167.43750404566526},{"cpu_ms":125.76400000000021,"wall_ms":141.0061391070485},{"cpu_ms":139.74999999999937,"wall_ms":167.6840940490365},{"cpu_ms":138.94799999999918,"wall_ms":160.4074260685593},{"cpu_ms":124.96200000000002,"wall_ms":159.33692594990134},{"cpu_ms":126.94700000000125,"wall_ms":183.179902844131},{"cpu_ms":130.43400000000105,"wall_ms":161.93263698369265},{"cpu_ms":139.44100000000148,"wall_ms":183.3689061459154},{"cpu_ms":141.90500000000128,"wall_ms":159.88531010225415},{"cpu_ms":145.36999999999978,"wall_ms":190.88791706599295},{"cpu_ms":150.38900000000055,"wall_ms":168.79829694516957},{"cpu_ms":145.03799999999956,"wall_ms":184.48371905833483},{"cpu_ms":144.5609999999995,"wall_ms":199.92044707760215},{"cpu_ms":137.4969999999962,"wall_ms":192.33576185069978},{"cpu_ms":135.24699999999967,"wall_ms":165.93234986066818},{"cpu_ms":141.95800000000247,"wall_ms":187.46954901143909},{"cpu_ms":140.50500000000454,"wall_ms":170.38408992812037},{"cpu_ms":136.01799999999997,"wall_ms":196.99223502539098},{"cpu_ms":144.8910000000012,"wall_ms":192.8412369452417},{"cpu_ms":144.10100000000625,"wall_ms":196.02428609505296},{"cpu_ms":139.41799999999915,"wall_ms":189.87523391842842},{"cpu_ms":143.86100000000113,"wall_ms":195.36787900142372},{"cpu_ms":136.12700000000189,"wall_ms":159.51885399408638},{"cpu_ms":128.969000000005,"wall_ms":165.35601299256086},{"cpu_ms":133.9139999999972,"wall_ms":178.19086811505258},{"cpu_ms":127.55700000000303,"wall_ms":180.87155697867274},{"cpu_ms":141.5410000000037,"wall_ms":196.95266894996166},{"cpu_ms":143.2360000000017,"wall_ms":150.7145520299673},{"cpu_ms":143.14099999999996,"wall_ms":200.88271494023502},{"cpu_ms":133.51300000000066,"wall_ms":180.95008400268853},{"cpu_ms":140.02200000000187,"wall_ms":183.09289892204106},{"cpu_ms":145.83700000000022,"wall_ms":199.30301187559962},{"cpu_ms":140.49200000000184,"wall_ms":193.97401297464967},{"cpu_ms":134.43799999999584,"wall_ms":170.27431400492787},{"cpu_ms":136.30900000000423,"wall_ms":191.40110490843654},{"cpu_ms":133.74000000000308,"wall_ms":186.15822703577578},{"cpu_ms":143.48199999999878,"wall_ms":197.99365499056876},{"cpu_ms":138.7849999999986,"wall_ms":200.0027489848435},{"cpu_ms":139.77200000000067,"wall_ms":190.2320198714733},{"cpu_ms":147.41599999999977,"wall_ms":203.19600612856448},{"cpu_ms":137.6029999999986,"wall_ms":158.21451717056334},{"cpu_ms":142.98799999999545,"wall_ms":199.3273359257728},{"cpu_ms":142.75200000000154,"wall_ms":198.30228108912706},{"cpu_ms":142.10399999999623,"wall_ms":199.77611512877047},{"cpu_ms":145.0229999999948,"wall_ms":150.65814484842122},{"cpu_ms":138.38899999999654,"wall_ms":173.0348919518292},{"cpu_ms":147.05299999999966,"wall_ms":201.06581505388021},{"cpu_ms":144.15199999999828,"wall_ms":198.86475196108222},{"cpu_ms":144.40400000000153,"wall_ms":199.6570930350572},{"cpu_ms":141.32100000000491,"wall_ms":286.6376480087638},{"cpu_ms":145.350999999998,"wall_ms":206.7520108539611},{"cpu_ms":133.90400000000113,"wall_ms":162.30618208646774},{"cpu_ms":130.71000000000055,"wall_ms":173.24130702763796},{"cpu_ms":131.16700000000492,"wall_ms":181.75402493216097},{"cpu_ms":130.5759999999978,"wall_ms":143.26117211021483},{"cpu_ms":134.92099999999851,"wall_ms":155.37714399397373},{"cpu_ms":139.25300000000362,"wall_ms":191.71538087539375},{"cpu_ms":139.94399999999985,"wall_ms":198.94765713252127},{"cpu_ms":141.09999999999445,"wall_ms":177.9389758594334},{"cpu_ms":126.12299999999976,"wall_ms":146.62562892772257}],"after_median_cpu_ms":139.44100000000148,"before_control":[{"cpu_ms":131.66100000000026,"wall_ms":161.26825683750212},{"cpu_ms":127.76100000000001,"wall_ms":174.8227581847459},{"cpu_ms":136.8999999999998,"wall_ms":147.41196180693805},{"cpu_ms":113.09799999999993,"wall_ms":136.6274489555508},{"cpu_ms":118.24800000000036,"wall_ms":189.46067313663661},{"cpu_ms":113.51400000000034,"wall_ms":155.93534102663398},{"cpu_ms":115.29300000000032,"wall_ms":150.16954601742327},{"cpu_ms":135.7179999999998,"wall_ms":189.01658290997148},{"cpu_ms":141.90499999999952,"wall_ms":214.58814991638064},{"cpu_ms":139.14100000000042,"wall_ms":174.67487091198564},{"cpu_ms":140.87600000000043,"wall_ms":190.91454916633666},{"cpu_ms":145.79499999999967,"wall_ms":167.26474300958216},{"cpu_ms":139.98599999999863,"wall_ms":206.1586871277541},{"cpu_ms":140.49400000000034,"wall_ms":193.40884988196194},{"cpu_ms":140.03499999999923,"wall_ms":186.5786670241505},{"cpu_ms":133.74499999999932,"wall_ms":176.28794512711465},{"cpu_ms":130.40500000000145,"wall_ms":152.51371590420604},{"cpu_ms":121.73999999999907,"wall_ms":173.8721199799329},{"cpu_ms":140.9880000000001,"wall_ms":194.8449038900435},{"cpu_ms":145.8969999999997,"wall_ms":194.27711213938892},{"cpu_ms":139.01699999999906,"wall_ms":195.80141198821366},{"cpu_ms":135.90199999999976,"wall_ms":172.17859509401023},{"cpu_ms":139.18299999999917,"wall_ms":157.49942301772535},{"cpu_ms":140.01699999999852,"wall_ms":180.8521340135485},{"cpu_ms":140.97600000000023,"wall_ms":192.5106809940189},{"cpu_ms":138.68800000000016,"wall_ms":148.17338506691158},{"cpu_ms":145.5130000000011,"wall_ms":200.01633698120713},{"cpu_ms":144.44100000000049,"wall_ms":197.87994492799044},{"cpu_ms":138.68099999999828,"wall_ms":197.62740307487547},{"cpu_ms":143.24900000000085,"wall_ms":195.5416752025485},{"cpu_ms":140.2990000000024,"wall_ms":183.08937409892678},{"cpu_ms":141.5779999999991,"wall_ms":199.72842000424862},{"cpu_ms":136.59800000000288,"wall_ms":192.91879306547344},{"cpu_ms":138.7550000000033,"wall_ms":197.11655797436833},{"cpu_ms":141.31200000000277,"wall_ms":196.88995415344834},{"cpu_ms":138.2589999999979,"wall_ms":208.74179294332862},{"cpu_ms":140.03499999999747,"wall_ms":175.39778002537787},{"cpu_ms":117.69499999999766,"wall_ms":164.15142291225493},{"cpu_ms":122.54599999999982,"wall_ms":188.8837618753314},{"cpu_ms":126.50499999999809,"wall_ms":160.95429891720414},{"cpu_ms":125.12100000000004,"wall_ms":173.6234740819782},{"cpu_ms":124.49300000000108,"wall_ms":173.02778083831072},{"cpu_ms":125.4010000000001,"wall_ms":176.57660297118127},{"cpu_ms":142.53199999999921,"wall_ms":193.9045749604702},{"cpu_ms":128.21200000000132,"wall_ms":176.3471739832312},{"cpu_ms":133.36599999999876,"wall_ms":146.7142659239471},{"cpu_ms":127.63800000000103,"wall_ms":177.72838496603072},{"cpu_ms":139.1139999999993,"wall_ms":216.2317701149732},{"cpu_ms":135.40499999999867,"wall_ms":187.06711707636714},{"cpu_ms":136.19299999999868,"wall_ms":185.1790591608733},{"cpu_ms":144.47099999999935,"wall_ms":156.35470184497535},{"cpu_ms":145.07700000000057,"wall_ms":199.52102121897042},{"cpu_ms":137.10899999999882,"wall_ms":186.6267540026456},{"cpu_ms":130.11399999999895,"wall_ms":188.29257716424763},{"cpu_ms":141.7880000000018,"wall_ms":168.0865860544145},{"cpu_ms":140.54000000000144,"wall_ms":197.54746207036078},{"cpu_ms":125.30800000000397,"wall_ms":176.7175649292767},{"cpu_ms":132.64099999999956,"wall_ms":163.0684279371053},{"cpu_ms":142.56800000000425,"wall_ms":166.98708105832338},{"cpu_ms":142.68999999999465,"wall_ms":192.2193851787597},{"cpu_ms":136.51700000000488,"wall_ms":169.02404790744185},{"cpu_ms":132.87400000000105,"wall_ms":164.61256402544677},{"cpu_ms":137.042000000001,"wall_ms":165.7362838741392},{"cpu_ms":126.76899999999591,"wall_ms":171.14737606607378},{"cpu_ms":127.65100000000018,"wall_ms":176.92759702913463},{"cpu_ms":128.00599999999918,"wall_ms":178.29618509858847},{"cpu_ms":134.58499999999418,"wall_ms":184.25601185299456},{"cpu_ms":145.60699999999827,"wall_ms":190.9463859628886},{"cpu_ms":143.36600000000033,"wall_ms":209.56410793587565},{"cpu_ms":145.11600000000158,"wall_ms":198.62285093404353},{"cpu_ms":141.05699999999644,"wall_ms":196.40181004069746},{"cpu_ms":143.39999999999975,"wall_ms":199.92301100865006},{"cpu_ms":140.32399999999967,"wall_ms":231.26027290709317},{"cpu_ms":142.11699999999894,"wall_ms":197.73907912895083},{"cpu_ms":223.18400000000338,"wall_ms":310.78001693822443},{"cpu_ms":141.23899999999878,"wall_ms":197.88793614134192},{"cpu_ms":141.68000000000092,"wall_ms":194.18186089023948},{"cpu_ms":145.3650000000053,"wall_ms":192.75103718973696},{"cpu_ms":136.36599999999532,"wall_ms":195.37383900023997},{"cpu_ms":142.3070000000024,"wall_ms":183.83477395400405},{"cpu_ms":141.2690000000012,"wall_ms":190.2844370342791},{"cpu_ms":135.5879999999985,"wall_ms":192.03941710293293},{"cpu_ms":143.73199999999997,"wall_ms":203.44963995739818},{"cpu_ms":137.590000000003,"wall_ms":179.46403683163226},{"cpu_ms":140.02399999999682,"wall_ms":190.8917729742825},{"cpu_ms":139.5040000000023,"wall_ms":213.9027640223503},{"cpu_ms":139.02499999999662,"wall_ms":161.36021399870515},{"cpu_ms":144.5320000000052,"wall_ms":200.11213305406272},{"cpu_ms":140.10200000000594,"wall_ms":177.01718397438526},{"cpu_ms":136.52299999999684,"wall_ms":191.76479405723512},{"cpu_ms":165.26600000000258,"wall_ms":228.32830203697085},{"cpu_ms":142.53699999999725,"wall_ms":163.46980701200664},{"cpu_ms":141.01399999999842,"wall_ms":196.4828099589795},{"cpu_ms":137.7510000000015,"wall_ms":147.4001770839095},{"cpu_ms":131.48999999999944,"wall_ms":161.6681299638003},{"cpu_ms":133.85600000000153,"wall_ms":158.61028502695262},{"cpu_ms":127.96199999999658,"wall_ms":177.59215808473527},{"cpu_ms":140.98700000000264,"wall_ms":194.7605221066624},{"cpu_ms":114.47299999999672,"wall_ms":162.09836420603096},{"cpu_ms":135.88599999999929,"wall_ms":182.41793592460454},{"cpu_ms":126.01399999999785,"wall_ms":158.5684372112155}],"before_control_median_cpu_ms":139.01699999999906,"after_control":[{"cpu_ms":126.26899999999975,"wall_ms":188.90464399009943},{"cpu_ms":130.12500000000006,"wall_ms":139.89538489840925},{"cpu_ms":139.2929999999999,"wall_ms":182.7533778268844},{"cpu_ms":120.12300000000042,"wall_ms":137.53183814696968},{"cpu_ms":115.25299999999916,"wall_ms":138.08427099138498},{"cpu_ms":114.68699999999998,"wall_ms":162.62425086461008},{"cpu_ms":115.0190000000002,"wall_ms":161.57192387618124},{"cpu_ms":140.11200000000025,"wall_ms":157.63279306702316},{"cpu_ms":144.7750000000001,"wall_ms":204.87210317514837},{"cpu_ms":133.17399999999947,"wall_ms":156.541442964226},{"cpu_ms":149.68499999999983,"wall_ms":199.04171698726714},{"cpu_ms":148.3100000000004,"wall_ms":178.0447990167886},{"cpu_ms":137.07899999999995,"wall_ms":160.51075095310807},{"cpu_ms":140.28400000000119,"wall_ms":167.12916898541152},{"cpu_ms":138.16500000000076,"wall_ms":165.27057602070272},{"cpu_ms":140.66299999999998,"wall_ms":195.38357108831406},{"cpu_ms":132.66899999999993,"wall_ms":182.0095470175147},{"cpu_ms":125.18900000000066,"wall_ms":172.1218388993293},{"cpu_ms":140.56499999999872,"wall_ms":168.64168597385287},{"cpu_ms":143.82600000000068,"wall_ms":204.78333695791662},{"cpu_ms":136.9700000000016,"wall_ms":186.14211003296077},{"cpu_ms":137.23799999999997,"wall_ms":189.61539096198976},{"cpu_ms":140.54199999999994,"wall_ms":188.9630409423262},{"cpu_ms":137.65400000000128,"wall_ms":153.8056198041886},{"cpu_ms":142.27299999999943,"wall_ms":195.59626700356603},{"cpu_ms":140.9509999999994,"wall_ms":194.2789158783853},{"cpu_ms":143.28100000000177,"wall_ms":198.67794611491263},{"cpu_ms":131.0380000000002,"wall_ms":178.78464004024863},{"cpu_ms":145.75400000000016,"wall_ms":187.02298612333834},{"cpu_ms":140.85199999999887,"wall_ms":225.23638792335987},{"cpu_ms":141.13199999999892,"wall_ms":195.64532884396613},{"cpu_ms":141.17099999999994,"wall_ms":197.52464117482305},{"cpu_ms":137.43899999999698,"wall_ms":195.84421114996076},{"cpu_ms":134.48700000000002,"wall_ms":173.31928596831858},{"cpu_ms":148.33299999999738,"wall_ms":202.5917898863554},{"cpu_ms":142.22700000000188,"wall_ms":194.54055419191718},{"cpu_ms":120.3850000000024,"wall_ms":140.77741210348904},{"cpu_ms":127.11800000000295,"wall_ms":187.58791079744697},{"cpu_ms":127.75600000000153,"wall_ms":168.1705939117819},{"cpu_ms":132.22999999999985,"wall_ms":179.47986791841686},{"cpu_ms":127.7419999999978,"wall_ms":171.09873495064676},{"cpu_ms":126.25099999999989,"wall_ms":179.76774601265788},{"cpu_ms":130.48000000000215,"wall_ms":175.91600795276463},{"cpu_ms":141.7710000000021,"wall_ms":174.60402590222657},{"cpu_ms":118.63500000000116,"wall_ms":152.4704040493816},{"cpu_ms":124.15299999999974,"wall_ms":144.2028540186584},{"cpu_ms":121.66099999999958,"wall_ms":166.31576721556485},{"cpu_ms":132.89299999999926,"wall_ms":277.2497679106891},{"cpu_ms":134.15600000000083,"wall_ms":184.72322588786483},{"cpu_ms":141.6409999999999,"wall_ms":162.81763999722898},{"cpu_ms":137.1750000000027,"wall_ms":157.84091013483703},{"cpu_ms":137.66299999999987,"wall_ms":187.19391315244138},{"cpu_ms":126.60400000000038,"wall_ms":173.00060880370438},{"cpu_ms":142.89600000000036,"wall_ms":192.07669887691736},{"cpu_ms":148.05899999999994,"wall_ms":180.0875139888376},{"cpu_ms":131.95499999999782,"wall_ms":174.25604606978595},{"cpu_ms":127.22300000000075,"wall_ms":178.58685902319849},{"cpu_ms":142.27899999999494,"wall_ms":188.76480916514993},{"cpu_ms":126.58199999999908,"wall_ms":157.4974770192057},{"cpu_ms":143.50600000000213,"wall_ms":193.89869319275022},{"cpu_ms":133.3139999999986,"wall_ms":191.63941103033721},{"cpu_ms":137.90600000000097,"wall_ms":193.64812411367893},{"cpu_ms":140.96800000000087,"wall_ms":196.26213982701302},{"cpu_ms":138.06399999999996,"wall_ms":154.53707403503358},{"cpu_ms":129.71399999999988,"wall_ms":177.110789809376},{"cpu_ms":126.77599999999956,"wall_ms":168.7240768224001},{"cpu_ms":130.21900000000386,"wall_ms":181.5515491180122},{"cpu_ms":146.14699999999914,"wall_ms":165.6860092189163},{"cpu_ms":143.45399999999842,"wall_ms":197.26955192163587},{"cpu_ms":138.69499999999846,"wall_ms":196.3685480877757},{"cpu_ms":145.93299999999942,"wall_ms":198.51066102273762},{"cpu_ms":140.45300000000083,"wall_ms":197.99943477846682},{"cpu_ms":143.4290000000047,"wall_ms":199.04887699522078},{"cpu_ms":144.20099999999536,"wall_ms":198.74352705664933},{"cpu_ms":138.74899999999712,"wall_ms":193.4757619164884},{"cpu_ms":142.5300000000007,"wall_ms":198.88186501339078},{"cpu_ms":143.1829999999934,"wall_ms":197.82781414687634},{"cpu_ms":144.80999999999966,"wall_ms":197.11471209302545},{"cpu_ms":138.6800000000008,"wall_ms":191.82795216329396},{"cpu_ms":142.5389999999993,"wall_ms":196.9776761252433},{"cpu_ms":143.31200000000166,"wall_ms":199.72276594489813},{"cpu_ms":155.4050000000018,"wall_ms":197.20647297799587},{"cpu_ms":133.79299999999716,"wall_ms":182.4595951475203},{"cpu_ms":142.68500000000017,"wall_ms":153.17083103582263},{"cpu_ms":144.63200000000143,"wall_ms":199.90884303115308},{"cpu_ms":135.60600000000278,"wall_ms":153.97107601165771},{"cpu_ms":137.1430000000018,"wall_ms":233.90860087238252},{"cpu_ms":139.29499999999706,"wall_ms":197.8201000019908},{"cpu_ms":147.21099999999865,"wall_ms":181.2618258409202},{"cpu_ms":142.31300000000147,"wall_ms":173.01994399167597},{"cpu_ms":140.2879999999982,"wall_ms":196.8112839385867},{"cpu_ms":142.7230000000037,"wall_ms":179.89045288413763},{"cpu_ms":135.12999999999664,"wall_ms":170.38285615853965},{"cpu_ms":132.54599999999783,"wall_ms":156.556055881083},{"cpu_ms":140.98300000000563,"wall_ms":191.4915330708027},{"cpu_ms":135.3769999999983,"wall_ms":156.2810419127345},{"cpu_ms":140.55400000000162,"wall_ms":147.8320430032909},{"cpu_ms":142.68099999999606,"wall_ms":195.25807118043303},{"cpu_ms":135.351,"wall_ms":161.10018803738058},{"cpu_ms":143.43600000000123,"wall_ms":161.04258014820516},{"cpu_ms":140.0819999999996,"wall_ms":157.7043819706887}],"after_control_median_cpu_ms":138.74899999999712}]}, + {"name":"eaw-paired","rows":[{"mode":"eastAsianWidth","scale":100,"samples":{"before":[{"eastAsianWidth":0.826},{"eastAsianWidth":1.012},{"eastAsianWidth":1.114},{"eastAsianWidth":1.091},{"eastAsianWidth":1.164},{"eastAsianWidth":0.885},{"eastAsianWidth":0.905},{"eastAsianWidth":1.221},{"eastAsianWidth":1.069},{"eastAsianWidth":1.261},{"eastAsianWidth":1.237},{"eastAsianWidth":1.124},{"eastAsianWidth":0.907},{"eastAsianWidth":1.214},{"eastAsianWidth":1.233},{"eastAsianWidth":1.223},{"eastAsianWidth":0.864},{"eastAsianWidth":0.914},{"eastAsianWidth":1.209},{"eastAsianWidth":1.237},{"eastAsianWidth":1.053},{"eastAsianWidth":1.093},{"eastAsianWidth":0.816},{"eastAsianWidth":0.973},{"eastAsianWidth":1.065},{"eastAsianWidth":1.092},{"eastAsianWidth":1.094},{"eastAsianWidth":1.237},{"eastAsianWidth":1.004},{"eastAsianWidth":1.182},{"eastAsianWidth":0.943},{"eastAsianWidth":1.027},{"eastAsianWidth":1.231},{"eastAsianWidth":1.133},{"eastAsianWidth":1.187},{"eastAsianWidth":1.221},{"eastAsianWidth":1.114},{"eastAsianWidth":1.256},{"eastAsianWidth":0.834},{"eastAsianWidth":0.923},{"eastAsianWidth":1.074},{"eastAsianWidth":1.026},{"eastAsianWidth":1.125},{"eastAsianWidth":1.015},{"eastAsianWidth":1.061},{"eastAsianWidth":1.36},{"eastAsianWidth":1.148},{"eastAsianWidth":1.044},{"eastAsianWidth":0.867},{"eastAsianWidth":0.947},{"eastAsianWidth":0.798},{"eastAsianWidth":0.899},{"eastAsianWidth":1.046},{"eastAsianWidth":1.405},{"eastAsianWidth":0.859},{"eastAsianWidth":0.889},{"eastAsianWidth":1.002},{"eastAsianWidth":0.858},{"eastAsianWidth":0.922},{"eastAsianWidth":1.061},{"eastAsianWidth":0.973},{"eastAsianWidth":0.815},{"eastAsianWidth":1.149},{"eastAsianWidth":0.969},{"eastAsianWidth":0.792},{"eastAsianWidth":1.161},{"eastAsianWidth":0.9},{"eastAsianWidth":0.88},{"eastAsianWidth":0.838},{"eastAsianWidth":0.924},{"eastAsianWidth":0.835},{"eastAsianWidth":0.838},{"eastAsianWidth":1.519},{"eastAsianWidth":1.034},{"eastAsianWidth":0.972},{"eastAsianWidth":0.939},{"eastAsianWidth":0.993},{"eastAsianWidth":1.074},{"eastAsianWidth":0.986},{"eastAsianWidth":0.93},{"eastAsianWidth":0.913},{"eastAsianWidth":1.305},{"eastAsianWidth":0.98},{"eastAsianWidth":0.816},{"eastAsianWidth":0.873},{"eastAsianWidth":0.956},{"eastAsianWidth":0.938},{"eastAsianWidth":1.158},{"eastAsianWidth":0.994},{"eastAsianWidth":0.929},{"eastAsianWidth":0.921},{"eastAsianWidth":0.982},{"eastAsianWidth":1.14},{"eastAsianWidth":0.879},{"eastAsianWidth":0.983},{"eastAsianWidth":1.021},{"eastAsianWidth":1.086},{"eastAsianWidth":0.907},{"eastAsianWidth":0.942},{"eastAsianWidth":0.957},{"eastAsianWidth":0.973}],"after":[{"eastAsianWidth":0.97},{"eastAsianWidth":1.235},{"eastAsianWidth":1.333},{"eastAsianWidth":1.241},{"eastAsianWidth":1.153},{"eastAsianWidth":1.097},{"eastAsianWidth":1.141},{"eastAsianWidth":0.983},{"eastAsianWidth":1.103},{"eastAsianWidth":1.04},{"eastAsianWidth":1.233},{"eastAsianWidth":1.06},{"eastAsianWidth":1.085},{"eastAsianWidth":1.168},{"eastAsianWidth":1.066},{"eastAsianWidth":1.031},{"eastAsianWidth":1.21},{"eastAsianWidth":1.231},{"eastAsianWidth":0.945},{"eastAsianWidth":0.988},{"eastAsianWidth":0.933},{"eastAsianWidth":1.023},{"eastAsianWidth":1.861},{"eastAsianWidth":0.982},{"eastAsianWidth":1.34},{"eastAsianWidth":0.912},{"eastAsianWidth":0.851},{"eastAsianWidth":1.021},{"eastAsianWidth":1.136},{"eastAsianWidth":1.175},{"eastAsianWidth":1.089},{"eastAsianWidth":0.989},{"eastAsianWidth":1.098},{"eastAsianWidth":1.141},{"eastAsianWidth":0.983},{"eastAsianWidth":0.924},{"eastAsianWidth":1.258},{"eastAsianWidth":1.073},{"eastAsianWidth":1.021},{"eastAsianWidth":1.071},{"eastAsianWidth":0.937},{"eastAsianWidth":0.971},{"eastAsianWidth":1.067},{"eastAsianWidth":0.923},{"eastAsianWidth":0.944},{"eastAsianWidth":1.325},{"eastAsianWidth":1.243},{"eastAsianWidth":0.874},{"eastAsianWidth":0.909},{"eastAsianWidth":0.847},{"eastAsianWidth":0.873},{"eastAsianWidth":0.984},{"eastAsianWidth":0.879},{"eastAsianWidth":1.071},{"eastAsianWidth":1.032},{"eastAsianWidth":0.913},{"eastAsianWidth":0.995},{"eastAsianWidth":0.917},{"eastAsianWidth":0.799},{"eastAsianWidth":0.936},{"eastAsianWidth":0.886},{"eastAsianWidth":0.863},{"eastAsianWidth":1.072},{"eastAsianWidth":1.329},{"eastAsianWidth":1.245},{"eastAsianWidth":0.913},{"eastAsianWidth":0.742},{"eastAsianWidth":0.859},{"eastAsianWidth":0.846},{"eastAsianWidth":0.998},{"eastAsianWidth":0.896},{"eastAsianWidth":0.802},{"eastAsianWidth":1.365},{"eastAsianWidth":0.96},{"eastAsianWidth":0.915},{"eastAsianWidth":1.177},{"eastAsianWidth":1.094},{"eastAsianWidth":1.176},{"eastAsianWidth":1.02},{"eastAsianWidth":0.915},{"eastAsianWidth":1.057},{"eastAsianWidth":1.295},{"eastAsianWidth":0.872},{"eastAsianWidth":1.068},{"eastAsianWidth":0.917},{"eastAsianWidth":1.013},{"eastAsianWidth":0.881},{"eastAsianWidth":1.222},{"eastAsianWidth":1.014},{"eastAsianWidth":1.108},{"eastAsianWidth":0.952},{"eastAsianWidth":0.929},{"eastAsianWidth":1.005},{"eastAsianWidth":0.91},{"eastAsianWidth":0.953},{"eastAsianWidth":0.97},{"eastAsianWidth":1.222},{"eastAsianWidth":1.024},{"eastAsianWidth":1.047},{"eastAsianWidth":1.006},{"eastAsianWidth":1.033}],"before_control":[{"eastAsianWidth":0.922},{"eastAsianWidth":1.077},{"eastAsianWidth":1.217},{"eastAsianWidth":1.17},{"eastAsianWidth":1.012},{"eastAsianWidth":0.993},{"eastAsianWidth":0.776},{"eastAsianWidth":1.273},{"eastAsianWidth":1.005},{"eastAsianWidth":0.98},{"eastAsianWidth":0.931},{"eastAsianWidth":1.198},{"eastAsianWidth":1.167},{"eastAsianWidth":1.022},{"eastAsianWidth":0.973},{"eastAsianWidth":1.14},{"eastAsianWidth":1.079},{"eastAsianWidth":1.073},{"eastAsianWidth":1.205},{"eastAsianWidth":0.872},{"eastAsianWidth":1.021},{"eastAsianWidth":1.012},{"eastAsianWidth":0.955},{"eastAsianWidth":0.835},{"eastAsianWidth":1.187},{"eastAsianWidth":1.177},{"eastAsianWidth":0.971},{"eastAsianWidth":0.971},{"eastAsianWidth":0.99},{"eastAsianWidth":1.092},{"eastAsianWidth":1.13},{"eastAsianWidth":1.019},{"eastAsianWidth":0.929},{"eastAsianWidth":1.104},{"eastAsianWidth":1.292},{"eastAsianWidth":1.154},{"eastAsianWidth":1.045},{"eastAsianWidth":0.924},{"eastAsianWidth":0.808},{"eastAsianWidth":1.012},{"eastAsianWidth":0.944},{"eastAsianWidth":1.001},{"eastAsianWidth":0.909},{"eastAsianWidth":1.107},{"eastAsianWidth":1.252},{"eastAsianWidth":1.157},{"eastAsianWidth":1.152},{"eastAsianWidth":1.144},{"eastAsianWidth":0.909},{"eastAsianWidth":0.883},{"eastAsianWidth":1.021},{"eastAsianWidth":0.916},{"eastAsianWidth":0.824},{"eastAsianWidth":1.332},{"eastAsianWidth":0.929},{"eastAsianWidth":0.987},{"eastAsianWidth":0.888},{"eastAsianWidth":0.898},{"eastAsianWidth":0.923},{"eastAsianWidth":0.837},{"eastAsianWidth":0.835},{"eastAsianWidth":0.878},{"eastAsianWidth":1.116},{"eastAsianWidth":1.059},{"eastAsianWidth":1.208},{"eastAsianWidth":1.085},{"eastAsianWidth":1.049},{"eastAsianWidth":0.861},{"eastAsianWidth":0.791},{"eastAsianWidth":0.837},{"eastAsianWidth":0.849},{"eastAsianWidth":0.83},{"eastAsianWidth":1.282},{"eastAsianWidth":1.241},{"eastAsianWidth":0.952},{"eastAsianWidth":1.178},{"eastAsianWidth":1.231},{"eastAsianWidth":0.989},{"eastAsianWidth":1.006},{"eastAsianWidth":1.015},{"eastAsianWidth":1.313},{"eastAsianWidth":1.291},{"eastAsianWidth":1.04},{"eastAsianWidth":0.884},{"eastAsianWidth":1.092},{"eastAsianWidth":1.056},{"eastAsianWidth":0.869},{"eastAsianWidth":0.894},{"eastAsianWidth":0.923},{"eastAsianWidth":0.979},{"eastAsianWidth":0.967},{"eastAsianWidth":0.963},{"eastAsianWidth":0.915},{"eastAsianWidth":0.953},{"eastAsianWidth":0.971},{"eastAsianWidth":0.998},{"eastAsianWidth":1.052},{"eastAsianWidth":1.206},{"eastAsianWidth":0.903},{"eastAsianWidth":0.968},{"eastAsianWidth":0.978}],"after_control":[{"eastAsianWidth":1.093},{"eastAsianWidth":0.957},{"eastAsianWidth":1.218},{"eastAsianWidth":1.082},{"eastAsianWidth":0.964},{"eastAsianWidth":0.896},{"eastAsianWidth":1.046},{"eastAsianWidth":0.891},{"eastAsianWidth":1.066},{"eastAsianWidth":0.945},{"eastAsianWidth":1.06},{"eastAsianWidth":1.116},{"eastAsianWidth":1.146},{"eastAsianWidth":1.235},{"eastAsianWidth":0.974},{"eastAsianWidth":0.911},{"eastAsianWidth":0.996},{"eastAsianWidth":1.227},{"eastAsianWidth":1.456},{"eastAsianWidth":1.169},{"eastAsianWidth":0.859},{"eastAsianWidth":0.971},{"eastAsianWidth":1.216},{"eastAsianWidth":1.356},{"eastAsianWidth":1.263},{"eastAsianWidth":1.179},{"eastAsianWidth":0.972},{"eastAsianWidth":1.057},{"eastAsianWidth":0.907},{"eastAsianWidth":1.209},{"eastAsianWidth":1.149},{"eastAsianWidth":1.058},{"eastAsianWidth":1.191},{"eastAsianWidth":1.058},{"eastAsianWidth":1.178},{"eastAsianWidth":1.218},{"eastAsianWidth":0.843},{"eastAsianWidth":0.838},{"eastAsianWidth":0.953},{"eastAsianWidth":0.912},{"eastAsianWidth":0.963},{"eastAsianWidth":1.059},{"eastAsianWidth":1.083},{"eastAsianWidth":1.146},{"eastAsianWidth":1.213},{"eastAsianWidth":1.192},{"eastAsianWidth":1.171},{"eastAsianWidth":0.916},{"eastAsianWidth":1.001},{"eastAsianWidth":0.883},{"eastAsianWidth":1.086},{"eastAsianWidth":1.201},{"eastAsianWidth":0.901},{"eastAsianWidth":0.967},{"eastAsianWidth":0.793},{"eastAsianWidth":1.179},{"eastAsianWidth":1.206},{"eastAsianWidth":1.167},{"eastAsianWidth":0.903},{"eastAsianWidth":0.947},{"eastAsianWidth":0.883},{"eastAsianWidth":0.928},{"eastAsianWidth":1.033},{"eastAsianWidth":1.125},{"eastAsianWidth":1.013},{"eastAsianWidth":0.977},{"eastAsianWidth":1.013},{"eastAsianWidth":0.818},{"eastAsianWidth":0.827},{"eastAsianWidth":0.844},{"eastAsianWidth":0.895},{"eastAsianWidth":0.836},{"eastAsianWidth":1.192},{"eastAsianWidth":1.151},{"eastAsianWidth":0.96},{"eastAsianWidth":0.942},{"eastAsianWidth":1.071},{"eastAsianWidth":1.025},{"eastAsianWidth":0.987},{"eastAsianWidth":1.019},{"eastAsianWidth":1.209},{"eastAsianWidth":1.318},{"eastAsianWidth":0.997},{"eastAsianWidth":0.997},{"eastAsianWidth":0.969},{"eastAsianWidth":0.899},{"eastAsianWidth":0.868},{"eastAsianWidth":1.139},{"eastAsianWidth":0.838},{"eastAsianWidth":0.984},{"eastAsianWidth":1.013},{"eastAsianWidth":1.177},{"eastAsianWidth":0.962},{"eastAsianWidth":0.983},{"eastAsianWidth":1.01},{"eastAsianWidth":1.262},{"eastAsianWidth":0.991},{"eastAsianWidth":1.053},{"eastAsianWidth":1},{"eastAsianWidth":0.93},{"eastAsianWidth":0.997}]},"output":["checksum 200000"],"medians_us":{"before":{"eastAsianWidth":1.002},"after":{"eastAsianWidth":1.02},"before_control":{"eastAsianWidth":1.001},"after_control":{"eastAsianWidth":1.013}}}]}, + {"name":"merged-aa-json_stringify_1mb","rows":[{"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":143.00700000000032,"wall_ms":153.5114508587867},{"cpu_ms":142.563,"wall_ms":164.13066303357482},{"cpu_ms":141.60199999999978,"wall_ms":157.2066459339112},{"cpu_ms":137.22400000000025,"wall_ms":206.75445394590497},{"cpu_ms":135.45500000000033,"wall_ms":165.84843304008245},{"cpu_ms":138.34199999999976,"wall_ms":161.74917295575142},{"cpu_ms":133.95000000000044,"wall_ms":163.0121530033648},{"cpu_ms":136.23099999999954,"wall_ms":168.12335303984582},{"cpu_ms":131.53599999999966,"wall_ms":158.2248869817704},{"cpu_ms":135.3010000000001,"wall_ms":201.33727206848562},{"cpu_ms":142.99399999999986,"wall_ms":296.604725997895},{"cpu_ms":142.38600000000014,"wall_ms":196.28449087031186},{"cpu_ms":140.38500000000022,"wall_ms":161.87537787482142},{"cpu_ms":139.39400000000023,"wall_ms":150.27184691280127},{"cpu_ms":145.02799999999993,"wall_ms":182.00554093346},{"cpu_ms":140.36700000000036,"wall_ms":164.57471204921603},{"cpu_ms":135.81299999999973,"wall_ms":158.3155170083046},{"cpu_ms":136.72099999999966,"wall_ms":170.66237679682672},{"cpu_ms":134.31899999999962,"wall_ms":144.17409501038492},{"cpu_ms":131.64500000000066,"wall_ms":144.1124591510743},{"cpu_ms":133.5639999999998,"wall_ms":155.98086500540376},{"cpu_ms":132.59100000000058,"wall_ms":159.00609781965613},{"cpu_ms":128.13599999999957,"wall_ms":160.27393587864935},{"cpu_ms":137.24200000000053,"wall_ms":257.1358699351549},{"cpu_ms":138.3840000000003,"wall_ms":168.32976788282394},{"cpu_ms":134.26199999999966,"wall_ms":201.27152907662094},{"cpu_ms":134.9159999999987,"wall_ms":159.64061301201582},{"cpu_ms":144.0720000000013,"wall_ms":251.81691208854318},{"cpu_ms":136.83099999999905,"wall_ms":183.574483031407},{"cpu_ms":140.80600000000132,"wall_ms":180.2048860117793},{"cpu_ms":148.458999999999,"wall_ms":191.3571150507778},{"cpu_ms":145.40000000000043,"wall_ms":198.09671700932086},{"cpu_ms":144.49299999999886,"wall_ms":171.25450284220278},{"cpu_ms":145.90500000000083,"wall_ms":222.55325294099748},{"cpu_ms":137.97800000000038,"wall_ms":167.1946479473263},{"cpu_ms":136.30699999999862,"wall_ms":156.4894150942564},{"cpu_ms":137.0749999999994,"wall_ms":156.86846408061683},{"cpu_ms":131.29400000000047,"wall_ms":185.23066793568432},{"cpu_ms":138.4710000000009,"wall_ms":161.6364128421992},{"cpu_ms":135.24899999999997,"wall_ms":166.62042494863272},{"cpu_ms":139.4200000000012,"wall_ms":182.19960387796164},{"cpu_ms":130.2430000000001,"wall_ms":177.80873901210725},{"cpu_ms":148.00499999999948,"wall_ms":169.5059968624264},{"cpu_ms":146.59699999999987,"wall_ms":187.43168911896646},{"cpu_ms":136.37100000000046,"wall_ms":166.25718586146832},{"cpu_ms":138.5070000000006,"wall_ms":206.5253658220172},{"cpu_ms":134.51400000000115,"wall_ms":219.4316911045462},{"cpu_ms":131.19899999999873,"wall_ms":209.70978494733572},{"cpu_ms":142.18999999999937,"wall_ms":155.65203013829887},{"cpu_ms":142.90000000000092,"wall_ms":154.43190094083548},{"cpu_ms":127.37800000000021,"wall_ms":160.28949501924217},{"cpu_ms":119.04600000000087,"wall_ms":131.11967290751636},{"cpu_ms":88.67999999999654,"wall_ms":95.1140879187733},{"cpu_ms":134.8060000000011,"wall_ms":139.86423215828836},{"cpu_ms":133.26899999999853,"wall_ms":139.01230902411044},{"cpu_ms":131.25400000000198,"wall_ms":136.68200583197176},{"cpu_ms":127.6699999999984,"wall_ms":135.0396468769759},{"cpu_ms":130.431999999999,"wall_ms":135.31916891224682},{"cpu_ms":133.95599999999774,"wall_ms":140.35394601523876},{"cpu_ms":129.05800000000056,"wall_ms":134.426049888134},{"cpu_ms":131.84799999999797,"wall_ms":137.85654003731906},{"cpu_ms":131.4300000000017,"wall_ms":137.8702560905367},{"cpu_ms":131.50500000000065,"wall_ms":137.87451409734786},{"cpu_ms":134.82899999999987,"wall_ms":148.21668807417154},{"cpu_ms":133.4920000000004,"wall_ms":148.82921217940748},{"cpu_ms":126.88300000000297,"wall_ms":142.2754169907421},{"cpu_ms":127.12700000000154,"wall_ms":143.7688369769603},{"cpu_ms":129.13400000000053,"wall_ms":139.5002610515803},{"cpu_ms":148.44499999999883,"wall_ms":169.76184397935867},{"cpu_ms":147.0470000000006,"wall_ms":162.17509820125997},{"cpu_ms":148.4570000000005,"wall_ms":161.88952582888305},{"cpu_ms":142.06499999999878,"wall_ms":145.480734994635},{"cpu_ms":145.5939999999991,"wall_ms":155.96194006502628},{"cpu_ms":142.64400000000066,"wall_ms":152.11007301695645},{"cpu_ms":135.0219999999993,"wall_ms":140.4292369261384},{"cpu_ms":89.96000000000137,"wall_ms":98.36596297100186},{"cpu_ms":140.01400000000075,"wall_ms":154.0669589303434},{"cpu_ms":133.88100000000236,"wall_ms":151.296071941033},{"cpu_ms":135.193000000001,"wall_ms":153.64549099467695},{"cpu_ms":147.03899999999948,"wall_ms":157.60155394673347},{"cpu_ms":147.1429999999998,"wall_ms":157.47305285185575},{"cpu_ms":145.0870000000002,"wall_ms":160.52608820609748},{"cpu_ms":146.7559999999999,"wall_ms":154.74404417909682},{"cpu_ms":143.3150000000012,"wall_ms":160.61515407636762},{"cpu_ms":146.90099999999973,"wall_ms":158.47602696157992},{"cpu_ms":148.33300000000094,"wall_ms":159.75015796720982},{"cpu_ms":134.41100000000006,"wall_ms":210.75231418944895},{"cpu_ms":131.7889999999977,"wall_ms":184.11107501015067},{"cpu_ms":143.0410000000002,"wall_ms":148.58151879161596},{"cpu_ms":139.82000000000028,"wall_ms":146.23301196843386},{"cpu_ms":118.75599999999764,"wall_ms":135.30341000296175},{"cpu_ms":116.52600000000035,"wall_ms":124.94818191044033},{"cpu_ms":150.10800000000302,"wall_ms":155.81364301033318},{"cpu_ms":147.13199999999915,"wall_ms":154.626073082909},{"cpu_ms":141.4289999999987,"wall_ms":150.78286197967827},{"cpu_ms":145.1910000000005,"wall_ms":153.5825040191412},{"cpu_ms":140.23000000000252,"wall_ms":176.69958691112697},{"cpu_ms":148.01299999999884,"wall_ms":153.42797501944005},{"cpu_ms":138.04199999999867,"wall_ms":155.55457794107497},{"cpu_ms":137.18500000000233,"wall_ms":153.64545118063688},{"cpu_ms":143.05099999999982,"wall_ms":151.5215029940009}],"before_median_cpu_ms":137.22400000000025,"after":[{"cpu_ms":146.15699999999964,"wall_ms":280.0037369597703},{"cpu_ms":140.07499999999996,"wall_ms":152.82471803948283},{"cpu_ms":135.56999999999996,"wall_ms":152.29181200265884},{"cpu_ms":134.53099999999995,"wall_ms":232.39978100173175},{"cpu_ms":134.61599999999984,"wall_ms":179.1517490055412},{"cpu_ms":134.06200000000013,"wall_ms":184.86252916045487},{"cpu_ms":136.0319999999997,"wall_ms":219.56826583482325},{"cpu_ms":130.4780000000001,"wall_ms":141.96052798070014},{"cpu_ms":134.53300000000024,"wall_ms":147.98060688190162},{"cpu_ms":137.78700000000032,"wall_ms":300.6477099843323},{"cpu_ms":136.03900000000024,"wall_ms":155.853436794132},{"cpu_ms":137.7189999999997,"wall_ms":152.24434295669198},{"cpu_ms":143.41899999999976,"wall_ms":194.51138703152537},{"cpu_ms":136.32899999999992,"wall_ms":162.8238100092858},{"cpu_ms":142.25099999999992,"wall_ms":169.6661759633571},{"cpu_ms":134.62399999999963,"wall_ms":196.05346908792853},{"cpu_ms":141.47900000000035,"wall_ms":171.1009058635682},{"cpu_ms":145.68700000000058,"wall_ms":210.79593594186008},{"cpu_ms":132.6799999999997,"wall_ms":174.15070487186313},{"cpu_ms":134.41299999999944,"wall_ms":163.23829488828778},{"cpu_ms":133.1920000000002,"wall_ms":164.99013896100223},{"cpu_ms":130.77499999999898,"wall_ms":170.67603184841573},{"cpu_ms":130.22500000000116,"wall_ms":193.35704087279737},{"cpu_ms":147.34099999999904,"wall_ms":179.86029293388128},{"cpu_ms":141.6389999999996,"wall_ms":176.4809179585427},{"cpu_ms":131.590000000001,"wall_ms":146.27985912375152},{"cpu_ms":137.1710000000004,"wall_ms":162.28446317836642},{"cpu_ms":134.00299999999987,"wall_ms":190.53674093447626},{"cpu_ms":136.10100000000003,"wall_ms":158.83103106170893},{"cpu_ms":146.31499999999954,"wall_ms":168.58845204114914},{"cpu_ms":140.70800000000006,"wall_ms":147.7682499680668},{"cpu_ms":154.16500000000076,"wall_ms":184.1659469064325},{"cpu_ms":147.80000000000015,"wall_ms":194.95426607318223},{"cpu_ms":146.6630000000002,"wall_ms":276.8578799441457},{"cpu_ms":133.57599999999968,"wall_ms":212.80401689000428},{"cpu_ms":136.29800000000003,"wall_ms":154.97530600987375},{"cpu_ms":132.5320000000012,"wall_ms":160.11105198413134},{"cpu_ms":134.9539999999987,"wall_ms":153.02811795845628},{"cpu_ms":136.5280000000002,"wall_ms":166.38936288654804},{"cpu_ms":135.508999999999,"wall_ms":164.32501608505845},{"cpu_ms":141.07800000000026,"wall_ms":197.3876419942826},{"cpu_ms":134.20199999999838,"wall_ms":157.63716003857553},{"cpu_ms":143.44300000000132,"wall_ms":158.27155485749245},{"cpu_ms":138.5930000000002,"wall_ms":162.62384713627398},{"cpu_ms":140.62599999999924,"wall_ms":161.8679550010711},{"cpu_ms":133.34899999999905,"wall_ms":260.45430987142026},{"cpu_ms":136.9410000000002,"wall_ms":236.82800494134426},{"cpu_ms":143.52600000000137,"wall_ms":185.85712811909616},{"cpu_ms":133.86599999999936,"wall_ms":201.4575859066099},{"cpu_ms":145.79299999999938,"wall_ms":160.09934106841683},{"cpu_ms":115.73300000000053,"wall_ms":148.97989411838353},{"cpu_ms":117.33299999999858,"wall_ms":121.82227009907365},{"cpu_ms":127.83900000000159,"wall_ms":133.23759008198977},{"cpu_ms":79.23100000000005,"wall_ms":85.62355488538742},{"cpu_ms":125.8410000000012,"wall_ms":135.6282769702375},{"cpu_ms":132.10600000000028,"wall_ms":136.543327011168},{"cpu_ms":133.5329999999999,"wall_ms":136.47521915845573},{"cpu_ms":131.87800000000038,"wall_ms":137.2944200411439},{"cpu_ms":129.64400000000253,"wall_ms":136.04445499368012},{"cpu_ms":131.8009999999994,"wall_ms":138.23715201579034},{"cpu_ms":128.344000000002,"wall_ms":134.35176201164722},{"cpu_ms":93.41299999999819,"wall_ms":99.80811807326972},{"cpu_ms":130.32300000000063,"wall_ms":138.75642092898488},{"cpu_ms":126.83900000000037,"wall_ms":134.7837378270924},{"cpu_ms":131.97899999999763,"wall_ms":136.42487488687038},{"cpu_ms":125.55599999999956,"wall_ms":156.0810231603682},{"cpu_ms":129.16199999999733,"wall_ms":143.42002500779927},{"cpu_ms":129.70599999999877,"wall_ms":142.1010009944439},{"cpu_ms":134.16100000000242,"wall_ms":142.58854207582772},{"cpu_ms":138.07899999999762,"wall_ms":153.67678017355502},{"cpu_ms":144.92800000000017,"wall_ms":157.71149890497327},{"cpu_ms":141.10700000000165,"wall_ms":158.40712818317115},{"cpu_ms":143.16200000000023,"wall_ms":158.5553651675582},{"cpu_ms":145.36999999999978,"wall_ms":156.50303103029728},{"cpu_ms":139.50900000000033,"wall_ms":143.93769297748804},{"cpu_ms":102.98999999999836,"wall_ms":118.37840708903968},{"cpu_ms":124.63999999999942,"wall_ms":136.15437992848456},{"cpu_ms":129.4829999999969,"wall_ms":138.83512886241078},{"cpu_ms":123.7320000000004,"wall_ms":138.44242598861456},{"cpu_ms":142.50200000000035,"wall_ms":154.6779100317508},{"cpu_ms":147.60300000000015,"wall_ms":156.9770558271557},{"cpu_ms":141.6220000000017,"wall_ms":166.98642820119858},{"cpu_ms":148.93499999999804,"wall_ms":166.3519130088389},{"cpu_ms":141.9429999999977,"wall_ms":153.57042104005814},{"cpu_ms":137.74700000000095,"wall_ms":147.67134911380708},{"cpu_ms":145.86299999999852,"wall_ms":162.26975596509874},{"cpu_ms":142.98100000000247,"wall_ms":153.4335259348154},{"cpu_ms":103.60200000000219,"wall_ms":115.07294001057744},{"cpu_ms":139.02699999999868,"wall_ms":147.52119802869856},{"cpu_ms":121.56099999999981,"wall_ms":127.20017903484404},{"cpu_ms":116.85500000000104,"wall_ms":123.81058582104743},{"cpu_ms":131.33499999999998,"wall_ms":134.73221310414374},{"cpu_ms":132.3059999999998,"wall_ms":136.7296848911792},{"cpu_ms":148.1110000000001,"wall_ms":152.524096891284},{"cpu_ms":146.62699999999873,"wall_ms":152.05082204192877},{"cpu_ms":141.75300000000135,"wall_ms":160.13484681025147},{"cpu_ms":142.96999999999827,"wall_ms":162.37732698209584},{"cpu_ms":149.43500000000043,"wall_ms":164.9129439610988},{"cpu_ms":145.29800000000037,"wall_ms":156.73432289622724},{"cpu_ms":141.73599999999809,"wall_ms":156.1403728555888},{"cpu_ms":142.84900000000178,"wall_ms":157.72643708623946}],"after_median_cpu_ms":136.03900000000024}]}, + {"name":"merged-ab-json_stringify_1mb","rows":[{"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":130.95699999999954,"wall_ms":142.37765804864466},{"cpu_ms":145.86200000000017,"wall_ms":150.25206096470356},{"cpu_ms":141.36499999999995,"wall_ms":148.80220196209848},{"cpu_ms":140.7919999999998,"wall_ms":152.177028125152},{"cpu_ms":124.94799999999984,"wall_ms":136.31878700107336},{"cpu_ms":106.24500000000037,"wall_ms":133.60248203389347},{"cpu_ms":141.7550000000003,"wall_ms":253.0060529243201},{"cpu_ms":144.1110000000001,"wall_ms":159.53793190419674},{"cpu_ms":88.65000000000035,"wall_ms":104.93756202049553},{"cpu_ms":79.94499999999948,"wall_ms":94.2769730463624},{"cpu_ms":124.76399999999987,"wall_ms":135.2779120206833},{"cpu_ms":124.96699999999983,"wall_ms":133.3980900235474},{"cpu_ms":136.8280000000004,"wall_ms":153.2286019064486},{"cpu_ms":143.23900000000035,"wall_ms":155.6566699873656},{"cpu_ms":141.69699999999975,"wall_ms":147.22308097407222},{"cpu_ms":143.3200000000001,"wall_ms":148.7571878824383},{"cpu_ms":146.0140000000001,"wall_ms":154.3855839408934},{"cpu_ms":140.18499999999977,"wall_ms":146.59054903313518},{"cpu_ms":145.0439999999995,"wall_ms":149.41878407262266},{"cpu_ms":141.953,"wall_ms":148.42629013583064},{"cpu_ms":123.97900000000028,"wall_ms":131.3754201401025},{"cpu_ms":143.13899999999967,"wall_ms":153.57454889453948},{"cpu_ms":138.44099999999938,"wall_ms":142.84760621376336},{"cpu_ms":145.55700000000016,"wall_ms":148.93970894627273},{"cpu_ms":102.71599999999914,"wall_ms":110.12764903716743},{"cpu_ms":133.7540000000015,"wall_ms":140.14648902229965},{"cpu_ms":126.81200000000104,"wall_ms":133.17914004437625},{"cpu_ms":128.84700000000038,"wall_ms":134.2258071526885},{"cpu_ms":129.5219999999997,"wall_ms":135.87617012672126},{"cpu_ms":138.52999999999938,"wall_ms":146.9095761422068},{"cpu_ms":143.20400000000078,"wall_ms":147.55841786973178},{"cpu_ms":108.70099999999994,"wall_ms":113.0658402107656},{"cpu_ms":100.49099999999989,"wall_ms":105.62376398593187},{"cpu_ms":141.88199999999895,"wall_ms":147.29573694057763},{"cpu_ms":142.28899999999987,"wall_ms":147.71623210981488},{"cpu_ms":83.72199999999985,"wall_ms":96.04823286645114},{"cpu_ms":138.27200000000062,"wall_ms":146.6778030153364},{"cpu_ms":131.69199999999927,"wall_ms":141.0923358052969},{"cpu_ms":144.23599999999936,"wall_ms":150.63029783777893},{"cpu_ms":147.6140000000008,"wall_ms":156.067768111825},{"cpu_ms":140.06099999999932,"wall_ms":145.45370498672128},{"cpu_ms":143.98900000000125,"wall_ms":150.4275188781321},{"cpu_ms":106.41999999999996,"wall_ms":123.86111076921225},{"cpu_ms":139.91799999999978,"wall_ms":154.3511301279068},{"cpu_ms":134.81499999999969,"wall_ms":145.2895188704133},{"cpu_ms":129.111,"wall_ms":132.48902093619108},{"cpu_ms":136.88199999999995,"wall_ms":141.25212491489947},{"cpu_ms":146.53700000000038,"wall_ms":153.95862702280283},{"cpu_ms":145.6729999999986,"wall_ms":151.10111702233553},{"cpu_ms":141.73900000000117,"wall_ms":152.11829799227417},{"cpu_ms":144.11100000000056,"wall_ms":159.60785211063921},{"cpu_ms":141.3569999999993,"wall_ms":151.82006102986634},{"cpu_ms":141.39800000000236,"wall_ms":154.7997579909861},{"cpu_ms":128.7169999999982,"wall_ms":139.09150823019445},{"cpu_ms":135.57600000000036,"wall_ms":137.97018211334944},{"cpu_ms":122.87099999999995,"wall_ms":138.27310898341238},{"cpu_ms":143.0080000000018,"wall_ms":155.42398393154144},{"cpu_ms":114.79299999999881,"wall_ms":193.79797484725714},{"cpu_ms":142.12500000000006,"wall_ms":149.49178090319037},{"cpu_ms":138.69899999999902,"wall_ms":144.12995195016265},{"cpu_ms":145.61700000000144,"wall_ms":150.0361179932952},{"cpu_ms":140.58199999999843,"wall_ms":142.99244596622884},{"cpu_ms":78.202000000001,"wall_ms":85.10713907890022},{"cpu_ms":141.3259999999994,"wall_ms":155.78369703143835},{"cpu_ms":129.5099999999998,"wall_ms":150.940396822989},{"cpu_ms":129.34600000000174,"wall_ms":182.86378495395184},{"cpu_ms":142.2080000000001,"wall_ms":148.66793085820973},{"cpu_ms":142.99899999999965,"wall_ms":150.34225885756314},{"cpu_ms":101.14100000000192,"wall_ms":110.59146700426936},{"cpu_ms":132.9589999999996,"wall_ms":136.34431501850486},{"cpu_ms":142.3330000000007,"wall_ms":160.69459193386137},{"cpu_ms":144.24200000000198,"wall_ms":158.6679860483855},{"cpu_ms":78.16799999999802,"wall_ms":90.40069999173284},{"cpu_ms":134.78600000000185,"wall_ms":138.19492212496698},{"cpu_ms":145.31900000000064,"wall_ms":155.67703684791923},{"cpu_ms":141.17900000000105,"wall_ms":155.59255983680487},{"cpu_ms":141.22000000000057,"wall_ms":151.61438682116568},{"cpu_ms":145.60000000000173,"wall_ms":155.96510586328804},{"cpu_ms":96.09900000000238,"wall_ms":107.38624795340002},{"cpu_ms":141.09900000000053,"wall_ms":153.53538608178496},{"cpu_ms":147.1039999999988,"wall_ms":152.53529697656631},{"cpu_ms":146.5960000000024,"wall_ms":156.99753491207957},{"cpu_ms":144.52499999999802,"wall_ms":148.96899391897023},{"cpu_ms":134.23400000000285,"wall_ms":139.69570701010525},{"cpu_ms":79.74099999999851,"wall_ms":91.40680101700127},{"cpu_ms":134.50900000000132,"wall_ms":148.9651668816805},{"cpu_ms":142.04499999999953,"wall_ms":147.48256700113416},{"cpu_ms":139.23499999999933,"wall_ms":157.7369871083647},{"cpu_ms":142.9229999999997,"wall_ms":149.31869716383517},{"cpu_ms":130.8700000000016,"wall_ms":139.24001599662006},{"cpu_ms":78.2650000000018,"wall_ms":80.59242414310575},{"cpu_ms":90.24199999999993,"wall_ms":94.678072957322},{"cpu_ms":132.41000000000014,"wall_ms":142.73115899413824},{"cpu_ms":132.96300000000016,"wall_ms":144.34197801165283},{"cpu_ms":77.64199999999732,"wall_ms":83.97361193783581},{"cpu_ms":144.8960000000028,"wall_ms":154.30304105393589},{"cpu_ms":132.07200000000086,"wall_ms":142.47755403630435},{"cpu_ms":131.40399999999985,"wall_ms":139.76153987459838},{"cpu_ms":143.18199999999948,"wall_ms":152.60075917467475},{"cpu_ms":143.2840000000013,"wall_ms":151.71750984154642},{"cpu_ms":74.01499999999928,"wall_ms":87.36930717714131}],"before_median_cpu_ms":139.23499999999933,"after":[{"cpu_ms":148.85300000000035,"wall_ms":154.2585368733853},{"cpu_ms":145.03900000000013,"wall_ms":155.44017520733178},{"cpu_ms":133.7839999999999,"wall_ms":146.15849312394857},{"cpu_ms":144.97800000000007,"wall_ms":152.45910501107574},{"cpu_ms":141.16000000000017,"wall_ms":155.62420897185802},{"cpu_ms":143.02100000000007,"wall_ms":178.4341181628406},{"cpu_ms":139.9859999999995,"wall_ms":155.38855898194015},{"cpu_ms":142.544,"wall_ms":158.0094948876649},{"cpu_ms":152.77299999999983,"wall_ms":163.28626521863043},{"cpu_ms":143.94300000000015,"wall_ms":159.36151100322604},{"cpu_ms":146.3910000000004,"wall_ms":153.84787996299565},{"cpu_ms":138.10500000000036,"wall_ms":155.55535000748932},{"cpu_ms":137.75999999999922,"wall_ms":171.11664591357112},{"cpu_ms":104.64999999999947,"wall_ms":121.02161510847509},{"cpu_ms":143.86100000000025,"wall_ms":162.390521960333},{"cpu_ms":141.95699999999965,"wall_ms":147.36280194483697},{"cpu_ms":138.14600000000075,"wall_ms":160.57776403613389},{"cpu_ms":140.94099999999975,"wall_ms":149.34946410357952},{"cpu_ms":116.92500000000017,"wall_ms":136.4872318226844},{"cpu_ms":144.16600000000022,"wall_ms":148.5690160188824},{"cpu_ms":148.81999999999974,"wall_ms":152.28264615871012},{"cpu_ms":131.7830000000004,"wall_ms":168.17510989494622},{"cpu_ms":141.29300000000012,"wall_ms":145.70119697600603},{"cpu_ms":145.2290000000005,"wall_ms":153.67967495694757},{"cpu_ms":145.74499999999978,"wall_ms":149.16593208909035},{"cpu_ms":143.95199999999875,"wall_ms":149.40014900639653},{"cpu_ms":136.51899999999983,"wall_ms":143.9511280041188},{"cpu_ms":133.29799999999992,"wall_ms":140.65645192749798},{"cpu_ms":129.1960000000003,"wall_ms":136.54579198919237},{"cpu_ms":142.18800000000087,"wall_ms":146.62151783704758},{"cpu_ms":141.46699999999868,"wall_ms":146.87862806022167},{"cpu_ms":80.29299999999928,"wall_ms":82.62139512225986},{"cpu_ms":145.88300000000132,"wall_ms":151.16893406957388},{"cpu_ms":142.62500000000068,"wall_ms":152.06183306872845},{"cpu_ms":139.72099999999978,"wall_ms":154.4094190467149},{"cpu_ms":145.2770000000001,"wall_ms":154.68269889242947},{"cpu_ms":142.97000000000003,"wall_ms":145.38841391913593},{"cpu_ms":84.88000000000007,"wall_ms":94.79293809272349},{"cpu_ms":135.4760000000006,"wall_ms":144.82701499946415},{"cpu_ms":136.00899999999962,"wall_ms":143.36679596453905},{"cpu_ms":148.45299999999995,"wall_ms":150.88572492823005},{"cpu_ms":145.26499999999842,"wall_ms":149.8017399571836},{"cpu_ms":138.3870000000016,"wall_ms":156.86189103871584},{"cpu_ms":136.317,"wall_ms":145.74991911649704},{"cpu_ms":134.91199999999992,"wall_ms":140.29385382309556},{"cpu_ms":77.63699999999929,"wall_ms":84.86935496330261},{"cpu_ms":140.47000000000054,"wall_ms":145.86836006492376},{"cpu_ms":137.88599999999997,"wall_ms":146.27481903880835},{"cpu_ms":140.04500000000064,"wall_ms":147.44909410364926},{"cpu_ms":143.29899999999895,"wall_ms":151.67727414518595},{"cpu_ms":145.58800000000005,"wall_ms":152.0136429462582},{"cpu_ms":140.8149999999999,"wall_ms":146.28175203688443},{"cpu_ms":143.59699999999975,"wall_ms":153.06469704955816},{"cpu_ms":137.55100000000198,"wall_ms":147.95277500525117},{"cpu_ms":118.8539999999989,"wall_ms":134.226679103449},{"cpu_ms":145.53899999999942,"wall_ms":151.98332606814802},{"cpu_ms":143.47099999999813,"wall_ms":156.8236700259149},{"cpu_ms":143.43600000000123,"wall_ms":149.88072705455124},{"cpu_ms":143.98100000000014,"wall_ms":223.41264900751412},{"cpu_ms":126.37199999999993,"wall_ms":134.8416970577091},{"cpu_ms":140.56899999999928,"wall_ms":146.92303095944226},{"cpu_ms":147.3089999999999,"wall_ms":152.68852282315493},{"cpu_ms":90.13300000000157,"wall_ms":94.53800111077726},{"cpu_ms":107.44899999999902,"wall_ms":171.93769989535213},{"cpu_ms":82.94599999999974,"wall_ms":93.29324495047331},{"cpu_ms":142.30200000000082,"wall_ms":151.7320559360087},{"cpu_ms":144.46599999999776,"wall_ms":150.90044192038476},{"cpu_ms":145.68799999999982,"wall_ms":150.10841307230294},{"cpu_ms":145.40500000000023,"wall_ms":152.82928687520325},{"cpu_ms":135.8789999999992,"wall_ms":144.32993601076305},{"cpu_ms":149.42299999999875,"wall_ms":156.87311184592545},{"cpu_ms":143.69100000000046,"wall_ms":160.1088480092585},{"cpu_ms":127.46699999999933,"wall_ms":137.7837141044438},{"cpu_ms":127.70499999999885,"wall_ms":141.18005987256765},{"cpu_ms":148.34199999999953,"wall_ms":152.74858614429832},{"cpu_ms":142.948999999998,"wall_ms":153.36554893292487},{"cpu_ms":141.98999999999984,"wall_ms":153.39171700179577},{"cpu_ms":79.51199999999758,"wall_ms":82.86811597645283},{"cpu_ms":76.9289999999998,"wall_ms":82.32226711697876},{"cpu_ms":148.5999999999983,"wall_ms":154.00163712911308},{"cpu_ms":130.93800000000044,"wall_ms":142.29141711257398},{"cpu_ms":132.6679999999989,"wall_ms":147.90893299505115},{"cpu_ms":146.03400000000022,"wall_ms":153.53195904754102},{"cpu_ms":145.62199999999947,"wall_ms":153.00251101143658},{"cpu_ms":101.68600000000083,"wall_ms":112.05394985154271},{"cpu_ms":142.05699999999766,"wall_ms":148.47347792237997},{"cpu_ms":147.48600000000067,"wall_ms":150.85271303541958},{"cpu_ms":143.77300000000304,"wall_ms":149.16106197051704},{"cpu_ms":140.2879999999982,"wall_ms":151.720454916358},{"cpu_ms":101.61399999999787,"wall_ms":112.96603293158114},{"cpu_ms":78.99600000000007,"wall_ms":82.31971086934209},{"cpu_ms":133.58200000000053,"wall_ms":138.99530796334147},{"cpu_ms":136.91299999999984,"wall_ms":143.27746909111738},{"cpu_ms":137.57299999999972,"wall_ms":142.96056795865297},{"cpu_ms":134.23799999999986,"wall_ms":144.57614603452384},{"cpu_ms":139.10199999999762,"wall_ms":153.54234888218343},{"cpu_ms":113.36700000000022,"wall_ms":118.74327785335481},{"cpu_ms":138.77700000000104,"wall_ms":149.16045195423067},{"cpu_ms":143.95299999999978,"wall_ms":155.3273240569979},{"cpu_ms":142.25799999999822,"wall_ms":156.6654748748988},{"cpu_ms":142.1840000000003,"wall_ms":163.5303411167115}],"after_median_cpu_ms":141.16000000000017}]}, + {"name":"merged-aa-string_concat_csv","rows":[{"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":44.003999999999934,"wall_ms":48.37801307439804},{"cpu_ms":45.24600000000012,"wall_ms":48.64195501431823},{"cpu_ms":45.00799999999994,"wall_ms":49.37642812728882},{"cpu_ms":43.27700000000001,"wall_ms":49.71357807517052},{"cpu_ms":45.69499999999982,"wall_ms":50.018347101286054},{"cpu_ms":43.14499999999999,"wall_ms":48.54718898423016},{"cpu_ms":43.07400000000028,"wall_ms":48.463643062859774},{"cpu_ms":45.67299999999985,"wall_ms":50.106422044336796},{"cpu_ms":41.88800000000015,"wall_ms":49.26532995887101},{"cpu_ms":44.37999999999987,"wall_ms":49.82241103425622},{"cpu_ms":44.52299999999987,"wall_ms":48.860704991966486},{"cpu_ms":48.515000000000086,"wall_ms":52.00496991164982},{"cpu_ms":44.25800000000013,"wall_ms":49.66224287636578},{"cpu_ms":42.47999999999985,"wall_ms":49.916707910597324},{"cpu_ms":43.93700000000011,"wall_ms":51.66861112229526},{"cpu_ms":46.3969999999998,"wall_ms":49.83464512042701},{"cpu_ms":42.06499999999957,"wall_ms":49.50924590229988},{"cpu_ms":41.32500000000006,"wall_ms":47.744879964739084},{"cpu_ms":46.345000000000084,"wall_ms":51.98889994062483},{"cpu_ms":44.175000000000075,"wall_ms":48.58502885326743},{"cpu_ms":40.49300000000011,"wall_ms":47.18924197368324},{"cpu_ms":43.96699999999987,"wall_ms":49.36601803638041},{"cpu_ms":39.60800000000031,"wall_ms":47.08251194097102},{"cpu_ms":42.926999999999715,"wall_ms":48.28492901287973},{"cpu_ms":30.629999999999935,"wall_ms":35.9837138094008},{"cpu_ms":45.698999999999934,"wall_ms":50.0433340203017},{"cpu_ms":42.89100000000001,"wall_ms":48.392919823527336},{"cpu_ms":46.23200000000027,"wall_ms":51.517459098249674},{"cpu_ms":37.062999999999846,"wall_ms":44.39343698322773},{"cpu_ms":42.4319999999998,"wall_ms":47.73502191528678},{"cpu_ms":44.20599999999997,"wall_ms":49.64115307666361},{"cpu_ms":43.242000000000225,"wall_ms":50.640991888940334},{"cpu_ms":43.66400000000059,"wall_ms":49.002198968082666},{"cpu_ms":41.22699999999924,"wall_ms":48.65329712629318},{"cpu_ms":46.46600000000056,"wall_ms":49.87220396287739},{"cpu_ms":31.473999999999336,"wall_ms":35.88877688162029},{"cpu_ms":34.9459999999997,"wall_ms":39.342841133475304},{"cpu_ms":49.002000000000656,"wall_ms":53.37277497164905},{"cpu_ms":46.52100000000026,"wall_ms":53.78515599295497},{"cpu_ms":51.33299999999963,"wall_ms":54.746462032198906},{"cpu_ms":46.87900000000056,"wall_ms":54.37843408435583},{"cpu_ms":47.5209999999997,"wall_ms":53.8018171209842},{"cpu_ms":48.47300000000043,"wall_ms":54.80782617814839},{"cpu_ms":49.593999999999916,"wall_ms":53.92672214657068},{"cpu_ms":49.00799999999972,"wall_ms":55.41183613240719},{"cpu_ms":48.805999999999905,"wall_ms":55.18942908383906},{"cpu_ms":49.33600000000027,"wall_ms":54.75335381925106},{"cpu_ms":49.30799999999991,"wall_ms":54.72671496681869},{"cpu_ms":49.5000000000001,"wall_ms":54.91948500275612},{"cpu_ms":47.54099999999983,"wall_ms":53.98083105683327},{"cpu_ms":51.616000000000106,"wall_ms":54.00839191861451},{"cpu_ms":47.423000000000215,"wall_ms":53.719763876870275},{"cpu_ms":50.14500000000055,"wall_ms":54.61250199005008},{"cpu_ms":49.6080000000001,"wall_ms":55.07574696093798},{"cpu_ms":50.669000000000075,"wall_ms":55.05413608625531},{"cpu_ms":50.517000000000145,"wall_ms":53.92329394817352},{"cpu_ms":51.45399999999967,"wall_ms":54.87575405277312},{"cpu_ms":49.450000000000216,"wall_ms":54.89417817443609},{"cpu_ms":48.58000000000029,"wall_ms":54.0105770342052},{"cpu_ms":51.70099999999955,"wall_ms":55.125781102105975},{"cpu_ms":45.75599999999991,"wall_ms":51.20125785470009},{"cpu_ms":43.63700000000037,"wall_ms":49.975247122347355},{"cpu_ms":44.68300000000003,"wall_ms":53.060211008414626},{"cpu_ms":49.38199999999959,"wall_ms":53.73594397678971},{"cpu_ms":47.82799999999998,"wall_ms":54.18591387569904},{"cpu_ms":45.63400000000062,"wall_ms":54.005598183721304},{"cpu_ms":47.45500000000025,"wall_ms":52.9007138684392},{"cpu_ms":48.664999999999736,"wall_ms":54.01009600609541},{"cpu_ms":48.61499999999985,"wall_ms":54.02398179285228},{"cpu_ms":50.658000000000314,"wall_ms":54.19397004880011},{"cpu_ms":48.72699999999952,"wall_ms":52.097042091190815},{"cpu_ms":49.67700000000086,"wall_ms":54.04900899156928},{"cpu_ms":47.39000000000004,"wall_ms":52.82433098182082},{"cpu_ms":50.53299999999972,"wall_ms":74.24623682163656},{"cpu_ms":48.77600000000015,"wall_ms":53.17500582896173},{"cpu_ms":47.55100000000034,"wall_ms":52.900293143466115},{"cpu_ms":45.83300000000001,"wall_ms":52.20457189716399},{"cpu_ms":49.636000000001346,"wall_ms":52.99538001418114},{"cpu_ms":50.368000000000634,"wall_ms":53.77565789967775},{"cpu_ms":48.657000000000394,"wall_ms":54.00672019459307},{"cpu_ms":49.04600000000059,"wall_ms":54.47668791748583},{"cpu_ms":45.06600000000027,"wall_ms":53.465638775378466},{"cpu_ms":48.231000000001245,"wall_ms":54.693922866135836},{"cpu_ms":48.99999999999949,"wall_ms":52.440613973885775},{"cpu_ms":47.08300000000065,"wall_ms":53.626138949766755},{"cpu_ms":49.071999999998894,"wall_ms":52.44658514857292},{"cpu_ms":47.420000000000684,"wall_ms":54.07390487380326},{"cpu_ms":45.861999999999625,"wall_ms":53.26761887408793},{"cpu_ms":47.02699999999993,"wall_ms":53.43150510452688},{"cpu_ms":44.24200000000056,"wall_ms":50.803914899006486},{"cpu_ms":41.48700000000005,"wall_ms":46.91664199344814},{"cpu_ms":27.931000000000594,"wall_ms":33.227103063836694},{"cpu_ms":41.82400000000008,"wall_ms":51.21287005022168},{"cpu_ms":44.18899999999937,"wall_ms":49.597050063312054},{"cpu_ms":46.948999999999685,"wall_ms":53.322091000154614},{"cpu_ms":48.514000000000834,"wall_ms":52.949744975194335},{"cpu_ms":47.16599999999893,"wall_ms":52.55886493250728},{"cpu_ms":46.188000000000784,"wall_ms":53.45002980902791},{"cpu_ms":46.99399999999976,"wall_ms":53.42946108430624},{"cpu_ms":45.877000000000834,"wall_ms":53.31186205148697},{"cpu_ms":45.77100000000023,"wall_ms":53.17482608370483}],"before_median_cpu_ms":46.46600000000056,"after":[{"cpu_ms":44.456999999999965,"wall_ms":48.84390393272042},{"cpu_ms":48.13599999999995,"wall_ms":50.50827283412218},{"cpu_ms":46.44400000000015,"wall_ms":49.84304099343717},{"cpu_ms":44.756999999999934,"wall_ms":51.180188078433275},{"cpu_ms":45.419000000000096,"wall_ms":49.83747913502157},{"cpu_ms":37.018000000000214,"wall_ms":48.399943159893155},{"cpu_ms":43.35299999999975,"wall_ms":48.77482377924025},{"cpu_ms":42.308999999999926,"wall_ms":49.81894511729479},{"cpu_ms":43.43200000000014,"wall_ms":47.84740088507533},{"cpu_ms":43.89599999999972,"wall_ms":49.268266186118126},{"cpu_ms":36.133000000000415,"wall_ms":41.89841612242162},{"cpu_ms":42.603999999999864,"wall_ms":51.56648997217417},{"cpu_ms":42.97899999999988,"wall_ms":49.40905002877116},{"cpu_ms":46.180000000000106,"wall_ms":49.56403886899352},{"cpu_ms":46.78199999999988,"wall_ms":51.16785503923893},{"cpu_ms":42.60800000000042,"wall_ms":48.9282610360533},{"cpu_ms":43.49800000000003,"wall_ms":48.814959125593305},{"cpu_ms":44.2800000000001,"wall_ms":48.66897617466748},{"cpu_ms":46.09100000000011,"wall_ms":50.498484168201685},{"cpu_ms":44.459999999999944,"wall_ms":48.857840010896325},{"cpu_ms":44.799999999999955,"wall_ms":48.195782117545605},{"cpu_ms":47.552999999999734,"wall_ms":49.943548161536455},{"cpu_ms":42.62699999999997,"wall_ms":49.98639808036387},{"cpu_ms":42.678,"wall_ms":49.0171869751066},{"cpu_ms":49.95700000000003,"wall_ms":54.34899893589318},{"cpu_ms":36.726000000000255,"wall_ms":42.06074890680611},{"cpu_ms":43.232999999999855,"wall_ms":48.64384909160435},{"cpu_ms":51.08399999999946,"wall_ms":55.47674698755145},{"cpu_ms":49.63000000000051,"wall_ms":54.97787403874099},{"cpu_ms":43.49900000000062,"wall_ms":48.93407202325761},{"cpu_ms":44.56599999999966,"wall_ms":50.88986596092582},{"cpu_ms":47.82699999999984,"wall_ms":53.27923106960952},{"cpu_ms":42.9649999999997,"wall_ms":47.33197786845267},{"cpu_ms":43.08100000000081,"wall_ms":48.46906289458275},{"cpu_ms":44.7109999999995,"wall_ms":49.06254191882908},{"cpu_ms":38.1860000000005,"wall_ms":42.65631199814379},{"cpu_ms":50.944999999999574,"wall_ms":55.34349684603512},{"cpu_ms":47.699999999999854,"wall_ms":54.156349040567875},{"cpu_ms":47.95299999999969,"wall_ms":54.34706504456699},{"cpu_ms":45.62800000000067,"wall_ms":53.969870787113905},{"cpu_ms":45.73999999999945,"wall_ms":54.09122700802982},{"cpu_ms":50.62699999999953,"wall_ms":54.05089305713773},{"cpu_ms":47.748000000000346,"wall_ms":55.1077569834888},{"cpu_ms":49.25200000000007,"wall_ms":54.656754015013576},{"cpu_ms":47.02599999999979,"wall_ms":55.41970091871917},{"cpu_ms":47.64700000000044,"wall_ms":54.99436613172293},{"cpu_ms":48.40999999999962,"wall_ms":54.71762781962752},{"cpu_ms":48.23399999999989,"wall_ms":54.64218696579337},{"cpu_ms":48.72999999999994,"wall_ms":55.091155925765634},{"cpu_ms":49.24300000000059,"wall_ms":54.657124914228916},{"cpu_ms":48.28999999999972,"wall_ms":53.558040875941515},{"cpu_ms":51.20599999999964,"wall_ms":55.63225690275431},{"cpu_ms":48.37299999999978,"wall_ms":54.815190844237804},{"cpu_ms":47.558999999999685,"wall_ms":55.03465095534921},{"cpu_ms":50.63499999999976,"wall_ms":55.01441191881895},{"cpu_ms":48.465999999999454,"wall_ms":53.76868508756161},{"cpu_ms":49.5050000000008,"wall_ms":54.86913095228374},{"cpu_ms":47.45799999999978,"wall_ms":54.78827003389597},{"cpu_ms":50.29899999999987,"wall_ms":53.603517124429345},{"cpu_ms":31.933000000000433,"wall_ms":39.33171997778118},{"cpu_ms":48.86199999999974,"wall_ms":53.273208905011415},{"cpu_ms":44.19799999999974,"wall_ms":50.61022308655083},{"cpu_ms":43.998000000000204,"wall_ms":49.404019955545664},{"cpu_ms":48.30800000000046,"wall_ms":53.711838787421584},{"cpu_ms":48.001999999999434,"wall_ms":52.38831485621631},{"cpu_ms":46.39799999999994,"wall_ms":54.751030169427395},{"cpu_ms":47.77499999999968,"wall_ms":54.197265999391675},{"cpu_ms":47.15700000000034,"wall_ms":52.57214908488095},{"cpu_ms":50.62799999999967,"wall_ms":55.17556401900947},{"cpu_ms":46.642000000000294,"wall_ms":51.986555103212595},{"cpu_ms":47.37599999999986,"wall_ms":51.812910940498114},{"cpu_ms":50.015999999999394,"wall_ms":52.3890471085906},{"cpu_ms":45.96100000000014,"wall_ms":52.3432120680809},{"cpu_ms":47.956000000001,"wall_ms":53.96084417589009},{"cpu_ms":47.64099999999871,"wall_ms":54.04091393575072},{"cpu_ms":46.43699999999917,"wall_ms":53.78846195526421},{"cpu_ms":48.178000000000054,"wall_ms":52.58671683259308},{"cpu_ms":48.40199999999939,"wall_ms":53.84739185683429},{"cpu_ms":47.51899999999942,"wall_ms":53.969410015270114},{"cpu_ms":47.61099999999985,"wall_ms":55.047734873369336},{"cpu_ms":48.70599999999925,"wall_ms":54.13159285672009},{"cpu_ms":47.49599999999887,"wall_ms":53.820102009922266},{"cpu_ms":48.038000000000025,"wall_ms":53.43635403551161},{"cpu_ms":45.67399999999999,"wall_ms":53.004326997324824},{"cpu_ms":50.44499999999985,"wall_ms":52.849808940663934},{"cpu_ms":35.34800000000082,"wall_ms":39.793052012100816},{"cpu_ms":36.76499999999905,"wall_ms":41.05888796038926},{"cpu_ms":48.48800000000075,"wall_ms":52.91673308238387},{"cpu_ms":47.888999999999626,"wall_ms":54.30230195634067},{"cpu_ms":41.62999999999961,"wall_ms":48.45875408500433},{"cpu_ms":40.95300000000002,"wall_ms":47.19700594432652},{"cpu_ms":48.37500000000006,"wall_ms":50.770303001627326},{"cpu_ms":46.38899999999957,"wall_ms":51.79670010693371},{"cpu_ms":47.01299999999975,"wall_ms":53.44561114907265},{"cpu_ms":46.336000000000155,"wall_ms":52.760731894522905},{"cpu_ms":49.32400000000037,"wall_ms":54.760026978328824},{"cpu_ms":46.8510000000002,"wall_ms":54.25844993442297},{"cpu_ms":46.08499999999971,"wall_ms":53.476468892768025},{"cpu_ms":44.59400000000002,"wall_ms":52.925819996744394},{"cpu_ms":42.66399999999848,"wall_ms":49.988292157649994},{"cpu_ms":46.28600000000027,"wall_ms":51.60270817577839}],"after_median_cpu_ms":46.8510000000002}]}, + {"name":"merged-ab-string_concat_csv","rows":[{"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":50.71400000000015,"wall_ms":56.079663103446364},{"cpu_ms":34.362,"wall_ms":40.72563396766782},{"cpu_ms":40.53899999999988,"wall_ms":45.88083690032363},{"cpu_ms":41.245000000000196,"wall_ms":46.792239882051945},{"cpu_ms":33.800000000000054,"wall_ms":37.10968792438507},{"cpu_ms":46.27999999999987,"wall_ms":52.26340191438794},{"cpu_ms":40.80700000000004,"wall_ms":46.17745103314519},{"cpu_ms":44.953999999999716,"wall_ms":49.32470200583339},{"cpu_ms":45.83300000000001,"wall_ms":52.176509983837605},{"cpu_ms":37.33200000000014,"wall_ms":44.64527894742787},{"cpu_ms":35.76500000000004,"wall_ms":42.11576213128865},{"cpu_ms":39.12000000000005,"wall_ms":44.37965201213956},{"cpu_ms":30.59999999999974,"wall_ms":36.89129091799259},{"cpu_ms":40.19100000000009,"wall_ms":52.74730618111789},{"cpu_ms":41.61200000000021,"wall_ms":49.83491497114301},{"cpu_ms":48.7289999999998,"wall_ms":54.06402703374624},{"cpu_ms":28.3199999999999,"wall_ms":33.670682925730944},{"cpu_ms":40.25800000000012,"wall_ms":45.66161800175905},{"cpu_ms":26.55900000000022,"wall_ms":34.7985599655658},{"cpu_ms":34.40300000000018,"wall_ms":40.782971074804664},{"cpu_ms":35.60399999999974,"wall_ms":42.760757030919194},{"cpu_ms":31.146000000000118,"wall_ms":35.55794898420572},{"cpu_ms":37.29200000000033,"wall_ms":45.54270510561764},{"cpu_ms":33.78199999999998,"wall_ms":40.151581168174744},{"cpu_ms":45.82599999999992,"wall_ms":52.16491804458201},{"cpu_ms":49.36600000000002,"wall_ms":54.73849689587951},{"cpu_ms":38.83700000000001,"wall_ms":44.270827900618315},{"cpu_ms":40.37500000000005,"wall_ms":46.75952810794115},{"cpu_ms":29.98299999999965,"wall_ms":36.07318107970059},{"cpu_ms":36.52900000000025,"wall_ms":40.96496198326349},{"cpu_ms":49.35299999999998,"wall_ms":52.74479207582772},{"cpu_ms":48.45500000000014,"wall_ms":52.86634899675846},{"cpu_ms":34.16700000000006,"wall_ms":39.554968010634184},{"cpu_ms":30.91299999999997,"wall_ms":33.19883090443909},{"cpu_ms":39.60099999999933,"wall_ms":46.096600126475096},{"cpu_ms":37.044999999999995,"wall_ms":40.45896604657173},{"cpu_ms":39.89299999999929,"wall_ms":45.561240054666996},{"cpu_ms":44.97000000000018,"wall_ms":49.30715914815664},{"cpu_ms":44.53700000000005,"wall_ms":51.781082060188055},{"cpu_ms":47.94500000000035,"wall_ms":50.27639982290566},{"cpu_ms":47.81799999999947,"wall_ms":53.27511299401522},{"cpu_ms":45.253999999999905,"wall_ms":49.68328191898763},{"cpu_ms":47.84699999999997,"wall_ms":53.220130037516356},{"cpu_ms":37.836999999999676,"wall_ms":42.82230185344815},{"cpu_ms":44.08499999999993,"wall_ms":48.47611580044031},{"cpu_ms":45.90600000000045,"wall_ms":50.25105201639235},{"cpu_ms":32.99499999999966,"wall_ms":35.375777864828706},{"cpu_ms":31.0769999999998,"wall_ms":33.35755784064531},{"cpu_ms":39.40799999999989,"wall_ms":43.34598104469478},{"cpu_ms":31.21499999999955,"wall_ms":38.75011298805475},{"cpu_ms":32.47799999999934,"wall_ms":38.78220310434699},{"cpu_ms":50.1339999999999,"wall_ms":54.55914209596813},{"cpu_ms":43.01699999999986,"wall_ms":46.64057702757418},{"cpu_ms":35.74599999999961,"wall_ms":40.134229930117726},{"cpu_ms":35.59800000000024,"wall_ms":40.93264113180339},{"cpu_ms":41.03999999999974,"wall_ms":47.81063203699887},{"cpu_ms":30.349999999999433,"wall_ms":34.715964924544096},{"cpu_ms":36.40000000000043,"wall_ms":44.69328816048801},{"cpu_ms":38.84899999999991,"wall_ms":45.19398300908506},{"cpu_ms":29.888999999999832,"wall_ms":38.26034814119339},{"cpu_ms":30.45700000000018,"wall_ms":35.8918730635196},{"cpu_ms":38.441999999999865,"wall_ms":40.81774712540209},{"cpu_ms":34.30800000000023,"wall_ms":42.59635997004807},{"cpu_ms":36.86699999999998,"wall_ms":40.27530294843018},{"cpu_ms":37.93100000000038,"wall_ms":44.122371124103665},{"cpu_ms":38.31999999999969,"wall_ms":42.70254820585251},{"cpu_ms":34.02700000000003,"wall_ms":40.394344832748175},{"cpu_ms":39.544000000000246,"wall_ms":43.83592586964369},{"cpu_ms":33.59199999999962,"wall_ms":40.19438195973635},{"cpu_ms":48.51899999999976,"wall_ms":52.950104931369424},{"cpu_ms":50.59700000000067,"wall_ms":54.12612296640873},{"cpu_ms":46.72099999999979,"wall_ms":55.122825084254146},{"cpu_ms":37.061000000000455,"wall_ms":43.09154488146305},{"cpu_ms":37.74500000000014,"wall_ms":43.11452805995941},{"cpu_ms":45.37400000000069,"wall_ms":53.70892304927111},{"cpu_ms":28.688999999999965,"wall_ms":36.586860893294215},{"cpu_ms":47.754000000000296,"wall_ms":53.125292994081974},{"cpu_ms":46.71599999999998,"wall_ms":53.0528980307281},{"cpu_ms":44.60100000000011,"wall_ms":50.03116209991276},{"cpu_ms":42.74399999999989,"wall_ms":50.06226012483239},{"cpu_ms":44.75600000000046,"wall_ms":51.170880906283855},{"cpu_ms":45.840999999999354,"wall_ms":51.208210876211524},{"cpu_ms":36.6970000000002,"wall_ms":40.975712006911635},{"cpu_ms":43.891000000000346,"wall_ms":51.283109933137894},{"cpu_ms":30.31000000000006,"wall_ms":35.739979008212686},{"cpu_ms":48.316999999999055,"wall_ms":54.74621104076505},{"cpu_ms":33.999000000001445,"wall_ms":42.35150315798819},{"cpu_ms":34.5329999999997,"wall_ms":38.90475304797292},{"cpu_ms":33.799999999999386,"wall_ms":38.12351217493415},{"cpu_ms":30.834000000000472,"wall_ms":38.68436091579497},{"cpu_ms":29.772000000001242,"wall_ms":39.12220802158117},{"cpu_ms":40.00299999999868,"wall_ms":44.42471591755748},{"cpu_ms":48.620999999998915,"wall_ms":55.10850786231458},{"cpu_ms":53.895999999999944,"wall_ms":59.30740386247635},{"cpu_ms":47.35900000000015,"wall_ms":53.64994308911264},{"cpu_ms":47.76299999999978,"wall_ms":54.11094496957958},{"cpu_ms":49.22299999999957,"wall_ms":54.65954006649554},{"cpu_ms":38.878999999999664,"wall_ms":44.24778511747718},{"cpu_ms":37.22699999999968,"wall_ms":43.52996405214071},{"cpu_ms":40.900999999999854,"wall_ms":44.232417130842805},{"cpu_ms":47.92699999999961,"wall_ms":66.28686492331326}],"before_median_cpu_ms":39.40799999999989,"after":[{"cpu_ms":49.394999999999854,"wall_ms":56.36291205883026},{"cpu_ms":27.3779999999999,"wall_ms":38.811146980151534},{"cpu_ms":36.53700000000004,"wall_ms":38.82452310062945},{"cpu_ms":50.28999999999995,"wall_ms":54.705385118722916},{"cpu_ms":27.76500000000004,"wall_ms":33.031427999958396},{"cpu_ms":37.08700000000009,"wall_ms":45.6432830542326},{"cpu_ms":28.23600000000015,"wall_ms":38.61787705682218},{"cpu_ms":48.20000000000002,"wall_ms":53.65460109896958},{"cpu_ms":48.986999999999895,"wall_ms":53.38015896268189},{"cpu_ms":28.795000000000126,"wall_ms":37.73240209557116},{"cpu_ms":45.28799999999977,"wall_ms":52.7079941239208},{"cpu_ms":39.661999999999864,"wall_ms":45.03894387744367},{"cpu_ms":47.07100000000031,"wall_ms":54.507315158843994},{"cpu_ms":43.45400000000011,"wall_ms":51.57326301559806},{"cpu_ms":44.90399999999983,"wall_ms":52.26431507617235},{"cpu_ms":44.96900000000004,"wall_ms":50.378018990159035},{"cpu_ms":46.799000000000035,"wall_ms":54.18791901320219},{"cpu_ms":28.6249999999999,"wall_ms":32.86402486264706},{"cpu_ms":34.31499999999987,"wall_ms":37.63833502307534},{"cpu_ms":29.316999999999815,"wall_ms":35.5730471201241},{"cpu_ms":38.08800000000012,"wall_ms":42.47816884890199},{"cpu_ms":32.36999999999979,"wall_ms":37.99626510590315},{"cpu_ms":27.99700000000005,"wall_ms":34.27995112724602},{"cpu_ms":36.70799999999996,"wall_ms":41.03860002942383},{"cpu_ms":31.988000000000127,"wall_ms":38.38617308065295},{"cpu_ms":36.730999999999625,"wall_ms":41.03173688054085},{"cpu_ms":33.09300000000004,"wall_ms":39.83401786535978},{"cpu_ms":30.76900000000027,"wall_ms":38.011393044143915},{"cpu_ms":35.679000000000016,"wall_ms":41.125701973214746},{"cpu_ms":32.22999999999976,"wall_ms":40.12240702286363},{"cpu_ms":38.83100000000006,"wall_ms":41.15049820393324},{"cpu_ms":43.91399999999956,"wall_ms":51.27108818851411},{"cpu_ms":48.690000000000566,"wall_ms":54.164704866707325},{"cpu_ms":32.548999999999495,"wall_ms":38.08595286682248},{"cpu_ms":36.09000000000062,"wall_ms":45.553666073828936},{"cpu_ms":31.60500000000077,"wall_ms":38.21558388881385},{"cpu_ms":33.83599999999998,"wall_ms":39.21572398394346},{"cpu_ms":36.5650000000004,"wall_ms":41.81651305407286},{"cpu_ms":28.010999999999342,"wall_ms":33.29698508605361},{"cpu_ms":37.46999999999989,"wall_ms":42.72112296894193},{"cpu_ms":48.377000000000336,"wall_ms":54.726624861359596},{"cpu_ms":37.495999999999974,"wall_ms":46.447396045550704},{"cpu_ms":31.299000000000632,"wall_ms":36.50964703410864},{"cpu_ms":40.10599999999975,"wall_ms":45.46817601658404},{"cpu_ms":32.248000000000054,"wall_ms":35.51971702836454},{"cpu_ms":35.91199999999972,"wall_ms":39.39628088846803},{"cpu_ms":37.544000000000466,"wall_ms":43.87993807904422},{"cpu_ms":36.32500000000061,"wall_ms":42.133203940466046},{"cpu_ms":31.511000000000067,"wall_ms":35.7657871209085},{"cpu_ms":38.38499999999989,"wall_ms":44.68809789977968},{"cpu_ms":40.487000000000606,"wall_ms":44.83539308421314},{"cpu_ms":39.05100000000061,"wall_ms":43.78574201837182},{"cpu_ms":41.58200000000001,"wall_ms":47.8822560980916},{"cpu_ms":40.90500000000041,"wall_ms":47.29709401726723},{"cpu_ms":42.38900000000001,"wall_ms":52.64060711488128},{"cpu_ms":46.57599999999995,"wall_ms":51.93295516073704},{"cpu_ms":47.85000000000039,"wall_ms":52.26456397213042},{"cpu_ms":48.88199999999987,"wall_ms":54.276484064757824},{"cpu_ms":32.52699999999997,"wall_ms":40.37443804554641},{"cpu_ms":38.770000000000415,"wall_ms":47.16358403675258},{"cpu_ms":30.253999999999337,"wall_ms":35.67928611300886},{"cpu_ms":39.74100000000025,"wall_ms":44.00417092256248},{"cpu_ms":41.987999999999914,"wall_ms":48.11479104682803},{"cpu_ms":39.208000000000354,"wall_ms":45.59859004803002},{"cpu_ms":45.1119999999996,"wall_ms":49.48527202941477},{"cpu_ms":34.33499999999956,"wall_ms":39.539017947390676},{"cpu_ms":35.33100000000022,"wall_ms":38.66872098296881},{"cpu_ms":42.44000000000003,"wall_ms":45.79306300729513},{"cpu_ms":39.551000000000336,"wall_ms":42.937498074024916},{"cpu_ms":49.19000000000029,"wall_ms":56.638867827132344},{"cpu_ms":50.0929999999995,"wall_ms":57.55624198354781},{"cpu_ms":48.23700000000031,"wall_ms":54.673234932124615},{"cpu_ms":32.992999999999384,"wall_ms":39.30748486891389},{"cpu_ms":35.50899999999935,"wall_ms":42.773389955982566},{"cpu_ms":37.90899999999997,"wall_ms":45.23195396177471},{"cpu_ms":48.97899999999922,"wall_ms":52.34208004549146},{"cpu_ms":48.820000000000086,"wall_ms":54.17860089801252},{"cpu_ms":29.625000000000234,"wall_ms":35.10185703635216},{"cpu_ms":28.887000000000107,"wall_ms":34.167971927672625},{"cpu_ms":46.38299999999962,"wall_ms":52.674120059236884},{"cpu_ms":44.645000000000046,"wall_ms":51.94984679110348},{"cpu_ms":43.107000000000006,"wall_ms":49.57713303156197},{"cpu_ms":27.04799999999974,"wall_ms":33.38366583921015},{"cpu_ms":43.65499999999933,"wall_ms":48.01442404277623},{"cpu_ms":32.94800000000109,"wall_ms":39.38246495090425},{"cpu_ms":46.69900000000027,"wall_ms":52.08165291696787},{"cpu_ms":46.81399999999947,"wall_ms":56.200167862698436},{"cpu_ms":47.03299999999899,"wall_ms":53.406598046422005},{"cpu_ms":38.934000000001134,"wall_ms":46.32354388013482},{"cpu_ms":48.60500000000023,"wall_ms":54.98255300335586},{"cpu_ms":45.72999999999894,"wall_ms":55.1099709700793},{"cpu_ms":34.891000000000005,"wall_ms":40.218387031927705},{"cpu_ms":47.73500000000119,"wall_ms":53.08244191110134},{"cpu_ms":47.904000000000835,"wall_ms":55.317007936537266},{"cpu_ms":50.13199999999962,"wall_ms":54.533353773877025},{"cpu_ms":48.36799999999997,"wall_ms":55.6786039378494},{"cpu_ms":49.155000000000726,"wall_ms":54.54815202392638},{"cpu_ms":35.517000000000465,"wall_ms":39.88825995475054},{"cpu_ms":33.8390000000004,"wall_ms":39.11982383579016},{"cpu_ms":48.83699999999891,"wall_ms":54.19908883050084},{"cpu_ms":51.469000000000875,"wall_ms":53.85284195654094}],"after_median_cpu_ms":38.934000000001134}]}, + {"name":"merged-aa-string_template_interp","rows":[{"name":"string_template_interp","node_exit":0,"before":[{"cpu_ms":57.617999999999725,"wall_ms":67.96135404147208},{"cpu_ms":57.010000000000005,"wall_ms":62.39100615493953},{"cpu_ms":54.998000000000104,"wall_ms":61.32275890558958},{"cpu_ms":61.82899999999991,"wall_ms":69.15305997245014},{"cpu_ms":50.50100000000013,"wall_ms":56.73238285817206},{"cpu_ms":51.5810000000001,"wall_ms":59.823147021234035},{"cpu_ms":54.8949999999997,"wall_ms":58.19584708660841},{"cpu_ms":53.6970000000001,"wall_ms":64.15490200743079},{"cpu_ms":48.690000000000126,"wall_ms":57.15950089506805},{"cpu_ms":55.71499999999973,"wall_ms":60.04573288373649},{"cpu_ms":47.899999999999835,"wall_ms":52.17497795820236},{"cpu_ms":49.67200000000016,"wall_ms":56.04627006687224},{"cpu_ms":55.158000000000044,"wall_ms":60.527262976393104},{"cpu_ms":56.01400000000023,"wall_ms":61.3680649548769},{"cpu_ms":67.6720000000004,"wall_ms":72.14293722063303},{"cpu_ms":54.4579999999999,"wall_ms":64.71215304918587},{"cpu_ms":62.569000000000315,"wall_ms":66.98120199143887},{"cpu_ms":59.39700000000015,"wall_ms":66.73746719025075},{"cpu_ms":53.07499999999976,"wall_ms":60.36940892226994},{"cpu_ms":57.987000000000236,"wall_ms":65.37425913847983},{"cpu_ms":48.41599999999957,"wall_ms":53.7019909825176},{"cpu_ms":46.74200000000006,"wall_ms":53.08818304911256},{"cpu_ms":68.6239999999998,"wall_ms":73.00301408395171},{"cpu_ms":45.738000000000056,"wall_ms":53.01077896729112},{"cpu_ms":64.50099999999992,"wall_ms":67.85608711652458},{"cpu_ms":54.78399999999972,"wall_ms":60.38218201138079},{"cpu_ms":71.415,"wall_ms":79.85587790608406},{"cpu_ms":76.48599999999917,"wall_ms":82.82544696703553},{"cpu_ms":77.102,"wall_ms":85.52038203924894},{"cpu_ms":80.0029999999996,"wall_ms":85.38879593834281},{"cpu_ms":80.5290000000003,"wall_ms":85.92663099989295},{"cpu_ms":79.44499999999977,"wall_ms":85.96097491681576},{"cpu_ms":78.39400000000029,"wall_ms":86.80159598588943},{"cpu_ms":80.13099999999973,"wall_ms":86.53815393336117},{"cpu_ms":81.15200000000033,"wall_ms":88.5899979621172},{"cpu_ms":79.24400000000009,"wall_ms":87.60792296379805},{"cpu_ms":80.69900000000007,"wall_ms":87.14286400936544},{"cpu_ms":78.37499999999942,"wall_ms":87.69514597952366},{"cpu_ms":78.9439999999999,"wall_ms":87.35154406167567},{"cpu_ms":77.78200000000001,"wall_ms":88.17249699495733},{"cpu_ms":78.98199999999989,"wall_ms":88.41422898694873},{"cpu_ms":77.14100000000013,"wall_ms":87.75357506237924},{"cpu_ms":78.57800000000026,"wall_ms":86.91559894941747},{"cpu_ms":76.39700000000005,"wall_ms":86.8732500821352},{"cpu_ms":75.6199999999998,"wall_ms":86.99680119752884},{"cpu_ms":82.3830000000001,"wall_ms":88.90720992349088},{"cpu_ms":52.385000000000126,"wall_ms":57.63744399882853},{"cpu_ms":81.29299999999961,"wall_ms":86.72240795567632},{"cpu_ms":79.24799999999976,"wall_ms":86.65834902785718},{"cpu_ms":79.84999999999864,"wall_ms":86.25625702552497},{"cpu_ms":58.54700000000079,"wall_ms":65.90739591047168},{"cpu_ms":45.707999999999416,"wall_ms":51.97178805246949},{"cpu_ms":67.0699999999993,"wall_ms":73.41505400836468},{"cpu_ms":65.7110000000003,"wall_ms":73.1850741431117},{"cpu_ms":75.7670000000008,"wall_ms":85.1229599211365},{"cpu_ms":75.64400000000049,"wall_ms":83.99510197341442},{"cpu_ms":47.14300000000016,"wall_ms":52.48063895851374},{"cpu_ms":76.88700000000104,"wall_ms":82.18338689766824},{"cpu_ms":70.49199999999978,"wall_ms":80.6369879283011},{"cpu_ms":48.98799999999959,"wall_ms":52.36173700541258},{"cpu_ms":76.45100000000049,"wall_ms":82.79135287739336},{"cpu_ms":49.75999999999914,"wall_ms":55.82922603935003},{"cpu_ms":75.86300000000001,"wall_ms":86.38579910621047},{"cpu_ms":76.86400000000049,"wall_ms":84.24416813068092},{"cpu_ms":75.99200000000117,"wall_ms":82.41995912976563},{"cpu_ms":73.66399999999906,"wall_ms":81.02914993651211},{"cpu_ms":62.62399999999957,"wall_ms":67.99408397637308},{"cpu_ms":48.28399999999888,"wall_ms":53.492800099775195},{"cpu_ms":80.30499999999918,"wall_ms":83.69220606982708},{"cpu_ms":67.44499999999931,"wall_ms":70.92124386690557},{"cpu_ms":49.65600000000059,"wall_ms":53.966234205290675},{"cpu_ms":43.88099999999895,"wall_ms":53.16412588581443},{"cpu_ms":49.69299999999954,"wall_ms":53.981122095137835},{"cpu_ms":43.02700000000037,"wall_ms":52.371344063431025},{"cpu_ms":49.620000000000886,"wall_ms":54.01096795685589},{"cpu_ms":48.776999999999404,"wall_ms":54.117555962875485},{"cpu_ms":75.27999999999935,"wall_ms":85.74304892681539},{"cpu_ms":73.14299999999996,"wall_ms":81.54061599634588},{"cpu_ms":74.39999999999891,"wall_ms":81.81454800069332},{"cpu_ms":76.09200000000094,"wall_ms":82.53802894614637},{"cpu_ms":75.36400000000043,"wall_ms":82.87631091661751},{"cpu_ms":71.48399999999988,"wall_ms":79.80297016911209},{"cpu_ms":70.824,"wall_ms":81.23162901028991},{"cpu_ms":74.0230000000004,"wall_ms":81.44960599020123},{"cpu_ms":74.25499999999907,"wall_ms":80.82322590053082},{"cpu_ms":75.05700000000104,"wall_ms":84.49566783383489},{"cpu_ms":73.39299999999938,"wall_ms":80.76951513066888},{"cpu_ms":67.08600000000153,"wall_ms":79.46396595798433},{"cpu_ms":74.29600000000036,"wall_ms":80.69037809036672},{"cpu_ms":75.83200000000012,"wall_ms":82.22427405416965},{"cpu_ms":74.92700000000063,"wall_ms":83.29622587189078},{"cpu_ms":72.1740000000004,"wall_ms":80.51378699019551},{"cpu_ms":72.92900000000024,"wall_ms":80.31916385516524},{"cpu_ms":62.978999999998564,"wall_ms":70.02394692972302},{"cpu_ms":53.9670000000001,"wall_ms":57.27900378406048},{"cpu_ms":59.05199999999944,"wall_ms":64.38706605695188},{"cpu_ms":78.54299999999981,"wall_ms":85.02939506433904},{"cpu_ms":80.24800000000099,"wall_ms":85.67713503725827},{"cpu_ms":50.74700000000121,"wall_ms":55.03782699815929},{"cpu_ms":45.11099999999857,"wall_ms":51.42830195836723},{"cpu_ms":45.42799999999936,"wall_ms":51.65196000598371}],"before_median_cpu_ms":70.49199999999978,"after":[{"cpu_ms":47.91700000000021,"wall_ms":53.206023992970586},{"cpu_ms":72.73100000000011,"wall_ms":82.09686609916389},{"cpu_ms":59.74100000000004,"wall_ms":65.05089509300888},{"cpu_ms":50.64100000000016,"wall_ms":58.08617197908461},{"cpu_ms":46.68599999999978,"wall_ms":52.9585110489279},{"cpu_ms":60.67,"wall_ms":67.15205102227628},{"cpu_ms":47.62099999999991,"wall_ms":51.96504504419863},{"cpu_ms":65.46399999999997,"wall_ms":69.8422179557383},{"cpu_ms":61.342000000000226,"wall_ms":67.74563109502196},{"cpu_ms":49.84199999999994,"wall_ms":56.10883701592684},{"cpu_ms":49.77100000000023,"wall_ms":57.20447492785752},{"cpu_ms":53.53199999999969,"wall_ms":58.81737801246345},{"cpu_ms":74.01400000000002,"wall_ms":80.35912783816457},{"cpu_ms":61.70799999999987,"wall_ms":66.0332110710442},{"cpu_ms":48.188999999999815,"wall_ms":58.77654207870364},{"cpu_ms":57.481999999999815,"wall_ms":60.77516614459455},{"cpu_ms":53.79999999999985,"wall_ms":58.2202619407326},{"cpu_ms":58.98000000000003,"wall_ms":61.279399087652564},{"cpu_ms":59.035999999999866,"wall_ms":68.40295903384686},{"cpu_ms":48.048999999999786,"wall_ms":52.34681791625917},{"cpu_ms":58.864000000000246,"wall_ms":64.20067697763443},{"cpu_ms":80.6230000000001,"wall_ms":86.00899507291615},{"cpu_ms":64.18999999999997,"wall_ms":67.56667606532574},{"cpu_ms":77.57500000000039,"wall_ms":82.96340517699718},{"cpu_ms":62.46200000000002,"wall_ms":69.90195903927088},{"cpu_ms":75.95600000000059,"wall_ms":85.33164882101119},{"cpu_ms":79.7270000000001,"wall_ms":86.11632604151964},{"cpu_ms":81.30700000000068,"wall_ms":85.71343286894262},{"cpu_ms":78.26299999999975,"wall_ms":85.70846309885383},{"cpu_ms":82.17300000000006,"wall_ms":85.55528707802296},{"cpu_ms":82.45199999999997,"wall_ms":85.84218309260905},{"cpu_ms":74.39600000000013,"wall_ms":85.88214893825352},{"cpu_ms":76.38400000000001,"wall_ms":82.82979484647512},{"cpu_ms":81.53200000000015,"wall_ms":87.9091250244528},{"cpu_ms":83.3339999999998,"wall_ms":87.79523288831115},{"cpu_ms":81.05599999999936,"wall_ms":87.49767695553601},{"cpu_ms":81.60500000000059,"wall_ms":87.03351020812988},{"cpu_ms":78.76300000000036,"wall_ms":87.2434510383755},{"cpu_ms":81.23599999999965,"wall_ms":87.65937900170684},{"cpu_ms":81.83100000000022,"wall_ms":87.21755305305123},{"cpu_ms":78.99100000000026,"wall_ms":87.40592491813004},{"cpu_ms":77.98399999999984,"wall_ms":87.48757699504495},{"cpu_ms":79.91100000000006,"wall_ms":88.31428294070065},{"cpu_ms":77.25599999999932,"wall_ms":86.69203193858266},{"cpu_ms":79.51600000000082,"wall_ms":87.06079004332423},{"cpu_ms":75.48899999999925,"wall_ms":85.04968299530447},{"cpu_ms":47.93700000000012,"wall_ms":56.78719491697848},{"cpu_ms":78.29200000000114,"wall_ms":86.61492704413831},{"cpu_ms":81.3600000000001,"wall_ms":86.72732696868479},{"cpu_ms":79.26300000000097,"wall_ms":86.67241502553225},{"cpu_ms":76.3499999999997,"wall_ms":86.67320595122874},{"cpu_ms":46.87700000000028,"wall_ms":53.16478689201176},{"cpu_ms":45.13500000000015,"wall_ms":52.4520049802959},{"cpu_ms":74.37700000000014,"wall_ms":81.73286495730281},{"cpu_ms":75.94699999999932,"wall_ms":83.32211500965059},{"cpu_ms":46.72099999999979,"wall_ms":51.98495206423104},{"cpu_ms":47.8759999999987,"wall_ms":52.18658805824816},{"cpu_ms":48.28200000000038,"wall_ms":57.640769984573126},{"cpu_ms":44.306999999999874,"wall_ms":53.5372830927372},{"cpu_ms":47.61700000000069,"wall_ms":55.082630133256316},{"cpu_ms":74.88099999999953,"wall_ms":83.30149599350989},{"cpu_ms":81.25200000000099,"wall_ms":85.65560495480895},{"cpu_ms":80.55299999999832,"wall_ms":84.92124197073281},{"cpu_ms":77.98700000000025,"wall_ms":85.38766507990658},{"cpu_ms":72.00199999999946,"wall_ms":81.5264789853245},{"cpu_ms":55.71300000000079,"wall_ms":66.9757321011275},{"cpu_ms":76.41100000000023,"wall_ms":82.82457501627505},{"cpu_ms":48.91000000000112,"wall_ms":54.244272876530886},{"cpu_ms":76.14000000000054,"wall_ms":83.54679401963949},{"cpu_ms":46.835000000001514,"wall_ms":53.2511780038476},{"cpu_ms":48.945999999999046,"wall_ms":54.272175999358296},{"cpu_ms":49.18400000000034,"wall_ms":60.185373993590474},{"cpu_ms":51.18299999999998,"wall_ms":57.513612089678645},{"cpu_ms":42.813000000000656,"wall_ms":53.0741170514375},{"cpu_ms":48.52999999999952,"wall_ms":56.79254489950836},{"cpu_ms":76.39700000000005,"wall_ms":81.88602095469832},{"cpu_ms":74.43100000000058,"wall_ms":80.88471996597946},{"cpu_ms":78.36900000000036,"wall_ms":84.70402611419559},{"cpu_ms":76.91200000000009,"wall_ms":81.34794584475458},{"cpu_ms":75.8700000000001,"wall_ms":83.3000740967691},{"cpu_ms":75.3699999999995,"wall_ms":81.84729912318289},{"cpu_ms":78.65499999999948,"wall_ms":83.07632617652416},{"cpu_ms":75.80800000000032,"wall_ms":83.22309888899326},{"cpu_ms":74.74500000000006,"wall_ms":80.09603712707758},{"cpu_ms":72.10599999999978,"wall_ms":79.48046596720815},{"cpu_ms":71.55099999999948,"wall_ms":79.8593140207231},{"cpu_ms":72.89600000000007,"wall_ms":80.264882883057},{"cpu_ms":70.08799999999837,"wall_ms":79.61722300387919},{"cpu_ms":71.021,"wall_ms":80.38102905265987},{"cpu_ms":76.84399999999947,"wall_ms":81.26004203222692},{"cpu_ms":76.82900000000004,"wall_ms":84.21333017759025},{"cpu_ms":73.53099999999912,"wall_ms":81.8585401866585},{"cpu_ms":68.77400000000122,"wall_ms":75.1846400089562},{"cpu_ms":50.93300000000056,"wall_ms":56.25797598622739},{"cpu_ms":48.36599999999969,"wall_ms":52.7603009250015},{"cpu_ms":58.866000000000085,"wall_ms":68.27113195322454},{"cpu_ms":49.69400000000057,"wall_ms":58.026640908792615},{"cpu_ms":72.35499999999995,"wall_ms":78.74554302543402},{"cpu_ms":45.354999999998924,"wall_ms":51.83588317595422},{"cpu_ms":48.40700000000098,"wall_ms":53.743669064715505},{"cpu_ms":45.97300000000004,"wall_ms":52.288198843598366}],"after_median_cpu_ms":72.35499999999995}]}, + {"name":"merged-ab-string_template_interp","rows":[{"name":"string_template_interp","node_exit":0,"before":[{"cpu_ms":74.538,"wall_ms":81.94417995400727},{"cpu_ms":79.46300000000006,"wall_ms":84.80626810342073},{"cpu_ms":80.50000000000024,"wall_ms":84.90742719732225},{"cpu_ms":80.51299999999983,"wall_ms":84.86564899794757},{"cpu_ms":73.57799999999992,"wall_ms":84.9171350710094},{"cpu_ms":79.38499999999982,"wall_ms":86.76078007556498},{"cpu_ms":74.73099999999988,"wall_ms":85.23969794623554},{"cpu_ms":72.04800000000012,"wall_ms":81.43445709720254},{"cpu_ms":71.55899999999971,"wall_ms":80.91667992994189},{"cpu_ms":77.712,"wall_ms":82.09177595563233},{"cpu_ms":73.09500000000035,"wall_ms":78.46351596526802},{"cpu_ms":78.16499999999982,"wall_ms":84.48298391886055},{"cpu_ms":78.16599999999995,"wall_ms":85.59952001087368},{"cpu_ms":76.08499999999995,"wall_ms":84.47332610376179},{"cpu_ms":79.65299999999998,"wall_ms":85.01166198402643},{"cpu_ms":76.89699999999976,"wall_ms":83.34679086692631},{"cpu_ms":60.99000000000032,"wall_ms":66.36194512248039},{"cpu_ms":44.51999999999945,"wall_ms":51.75240780226886},{"cpu_ms":48.44699999999946,"wall_ms":51.78456800058484},{"cpu_ms":44.15100000000027,"wall_ms":51.48633103817701},{"cpu_ms":80.65000000000033,"wall_ms":86.05468086898327},{"cpu_ms":75.84699999999955,"wall_ms":84.25201196223497},{"cpu_ms":47.39800000000027,"wall_ms":56.83633708395064},{"cpu_ms":51.72299999999996,"wall_ms":58.00314689986408},{"cpu_ms":82.90400000000008,"wall_ms":85.33262112177908},{"cpu_ms":79.37299999999948,"wall_ms":85.79903305508196},{"cpu_ms":73.8379999999994,"wall_ms":85.21662489511073},{"cpu_ms":76.57700000000034,"wall_ms":81.00902289152145},{"cpu_ms":53.160000000000096,"wall_ms":60.26798882521689},{"cpu_ms":73.53699999999996,"wall_ms":83.87733297422528},{"cpu_ms":78.33899999999971,"wall_ms":84.76480981335044},{"cpu_ms":72.84600000000019,"wall_ms":82.77316903695464},{"cpu_ms":76.54300000000003,"wall_ms":83.88953492976725},{"cpu_ms":75.35000000000025,"wall_ms":84.83234606683254},{"cpu_ms":74.78300000000004,"wall_ms":84.14922002702951},{"cpu_ms":73.87799999999967,"wall_ms":83.26238300651312},{"cpu_ms":66.58700000000017,"wall_ms":75.78322803601623},{"cpu_ms":55.9529999999997,"wall_ms":63.34020895883441},{"cpu_ms":79.41999999999982,"wall_ms":85.8561999630183},{"cpu_ms":75.04600000000039,"wall_ms":81.33069309405982},{"cpu_ms":44.44400000000037,"wall_ms":52.72015603259206},{"cpu_ms":75.75899999999969,"wall_ms":82.14296190999448},{"cpu_ms":78.44500000000032,"wall_ms":83.79437704570591},{"cpu_ms":77.70399999999978,"wall_ms":85.06598300300539},{"cpu_ms":72.27400000000017,"wall_ms":80.68956597708166},{"cpu_ms":76.72499999999971,"wall_ms":84.124063141644},{"cpu_ms":43.81900000000094,"wall_ms":52.106409100815654},{"cpu_ms":43.7650000000005,"wall_ms":52.03046719543636},{"cpu_ms":45.65099999999944,"wall_ms":51.91596318036318},{"cpu_ms":49.96400000000101,"wall_ms":52.271979162469506},{"cpu_ms":80.93600000000123,"wall_ms":86.35644405148923},{"cpu_ms":78.56699999999961,"wall_ms":86.94813004694879},{"cpu_ms":66.97300000000084,"wall_ms":73.36659310385585},{"cpu_ms":74.93499999999997,"wall_ms":80.3143649827689},{"cpu_ms":52.491999999999095,"wall_ms":56.80340598337352},{"cpu_ms":78.11399999999935,"wall_ms":84.5371950417757},{"cpu_ms":79.8179999999995,"wall_ms":87.26207702420652},{"cpu_ms":77.95700000000139,"wall_ms":85.3435208555311},{"cpu_ms":77.67099999999871,"wall_ms":86.07985684648156},{"cpu_ms":77.7580000000011,"wall_ms":85.1400620304048},{"cpu_ms":74.88200000000056,"wall_ms":84.24916700460017},{"cpu_ms":60.18100000000004,"wall_ms":67.62299197725952},{"cpu_ms":72.52399999999959,"wall_ms":82.00381183996797},{"cpu_ms":79.13699999999935,"wall_ms":83.53772689588368},{"cpu_ms":80.31900000000114,"wall_ms":86.73162502236664},{"cpu_ms":78.38299999999876,"wall_ms":83.79057003185153},{"cpu_ms":65.1919999999997,"wall_ms":70.55096211843193},{"cpu_ms":76.14700000000063,"wall_ms":84.42706917412579},{"cpu_ms":77.47499999999974,"wall_ms":85.8459509909153},{"cpu_ms":78.82299999999987,"wall_ms":85.2358010597527},{"cpu_ms":73.48200000000027,"wall_ms":83.83766794577241},{"cpu_ms":73.8310000000002,"wall_ms":83.24243617244065},{"cpu_ms":82.97300000000085,"wall_ms":88.40903011150658},{"cpu_ms":76.90799999999953,"wall_ms":84.30387894622982},{"cpu_ms":76.60300000000042,"wall_ms":85.9807119704783},{"cpu_ms":76.59199999999977,"wall_ms":85.96648601815104},{"cpu_ms":80.59499999999886,"wall_ms":86.99465612880886},{"cpu_ms":79.31300000000086,"wall_ms":85.75069182552397},{"cpu_ms":82.68300000000117,"wall_ms":90.06662596948445},{"cpu_ms":47.720999999999236,"wall_ms":54.408670868724585},{"cpu_ms":77.07399999999964,"wall_ms":84.45801795460284},{"cpu_ms":78.72200000000085,"wall_ms":83.07532407343388},{"cpu_ms":74.6380000000002,"wall_ms":81.98507688939571},{"cpu_ms":79.8419999999993,"wall_ms":86.2734499387443},{"cpu_ms":74.67100000000038,"wall_ms":86.20046311989427},{"cpu_ms":76.87600000000039,"wall_ms":83.27741106040776},{"cpu_ms":75.66500000000076,"wall_ms":84.03968601487577},{"cpu_ms":50.62500000000014,"wall_ms":56.991206016391516},{"cpu_ms":75.82900000000059,"wall_ms":83.17301701754332},{"cpu_ms":77.51999999999981,"wall_ms":83.93477904610336},{"cpu_ms":78.19499999999913,"wall_ms":85.61787498183548},{"cpu_ms":77.32700000000037,"wall_ms":84.77130299434066},{"cpu_ms":81.43300000000053,"wall_ms":85.81742690876126},{"cpu_ms":78.18999999999932,"wall_ms":85.60523088090122},{"cpu_ms":76.17899999999977,"wall_ms":84.5683729276061},{"cpu_ms":78.92099999999935,"wall_ms":85.34377184696496},{"cpu_ms":78.22800000000107,"wall_ms":83.73711095191538},{"cpu_ms":66.18499999999905,"wall_ms":74.57789592444897},{"cpu_ms":76.6050000000007,"wall_ms":84.97712691314518},{"cpu_ms":69.74999999999909,"wall_ms":77.07541296258569},{"cpu_ms":76.27199999999945,"wall_ms":85.71873186156154}],"before_median_cpu_ms":76.27199999999945,"after":[{"cpu_ms":79.09199999999971,"wall_ms":83.4706521127373},{"cpu_ms":53.959000000000316,"wall_ms":61.66793406009674},{"cpu_ms":74.32599999999967,"wall_ms":82.70699484273791},{"cpu_ms":80.44000000000028,"wall_ms":86.81832696311176},{"cpu_ms":77.5999999999999,"wall_ms":85.95405193045735},{"cpu_ms":77.7490000000003,"wall_ms":85.23222315125167},{"cpu_ms":73.26299999999986,"wall_ms":82.73112098686397},{"cpu_ms":72.16500000000003,"wall_ms":79.54511814750731},{"cpu_ms":73.16000000000011,"wall_ms":79.57807905040681},{"cpu_ms":72.09700000000029,"wall_ms":82.43685984052718},{"cpu_ms":79.93999999999969,"wall_ms":84.36061511747539},{"cpu_ms":78.67299999999977,"wall_ms":86.89827704802155},{"cpu_ms":80.14500000000035,"wall_ms":86.55916294082999},{"cpu_ms":78.27099999999999,"wall_ms":86.69215091504157},{"cpu_ms":78.62999999999998,"wall_ms":87.01737900264561},{"cpu_ms":57.77000000000054,"wall_ms":63.287089811638},{"cpu_ms":76.38899999999981,"wall_ms":82.79696316458285},{"cpu_ms":44.795999999999836,"wall_ms":53.35754691623151},{"cpu_ms":49.65500000000045,"wall_ms":53.00010903738439},{"cpu_ms":48.76799999999992,"wall_ms":53.04875993169844},{"cpu_ms":47.86200000000029,"wall_ms":54.211883107200265},{"cpu_ms":77.45699999999988,"wall_ms":86.80874994024634},{"cpu_ms":59.11299999999997,"wall_ms":65.90578285977244},{"cpu_ms":45.67999999999994,"wall_ms":53.93043695949018},{"cpu_ms":50.39000000000016,"wall_ms":59.44969016127288},{"cpu_ms":83.40399999999981,"wall_ms":87.92055700905621},{"cpu_ms":84.01400000000069,"wall_ms":88.42784515582025},{"cpu_ms":76.31199999999971,"wall_ms":82.69488206133246},{"cpu_ms":83.29200000000014,"wall_ms":86.70085784979165},{"cpu_ms":73.0959999999996,"wall_ms":80.15248295851052},{"cpu_ms":81.01400000000058,"wall_ms":87.41834806278348},{"cpu_ms":70.11500000000038,"wall_ms":78.54271517135203},{"cpu_ms":76.46799999999931,"wall_ms":83.86699319817126},{"cpu_ms":78.73700000000028,"wall_ms":87.15441497042775},{"cpu_ms":80.3029999999998,"wall_ms":86.72071504406631},{"cpu_ms":77.81000000000037,"wall_ms":86.18883206509054},{"cpu_ms":81.23300000000012,"wall_ms":86.67667210102081},{"cpu_ms":57.86399999999947,"wall_ms":68.35239403881133},{"cpu_ms":61.041000000000345,"wall_ms":68.5629469808191},{"cpu_ms":66.59300000000013,"wall_ms":76.83174801059067},{"cpu_ms":46.89799999999966,"wall_ms":53.19840018637478},{"cpu_ms":76.43100000000035,"wall_ms":80.84033709019423},{"cpu_ms":78.55199999999928,"wall_ms":87.9308960866183},{"cpu_ms":79.52700000000057,"wall_ms":88.01299007609487},{"cpu_ms":79.84599999999898,"wall_ms":87.2650220990181},{"cpu_ms":49.71300000000056,"wall_ms":54.01174910366535},{"cpu_ms":45.683999999999614,"wall_ms":53.93897299654782},{"cpu_ms":45.62899999999992,"wall_ms":53.86625719256699},{"cpu_ms":50.35799999999924,"wall_ms":53.67514002136886},{"cpu_ms":49.79299999999931,"wall_ms":55.13726198114455},{"cpu_ms":77.58399999999988,"wall_ms":88.96496798843145},{"cpu_ms":83.42799999999961,"wall_ms":88.84117705747485},{"cpu_ms":75.85899999999945,"wall_ms":87.2516268864274},{"cpu_ms":75.685,"wall_ms":83.06738897226751},{"cpu_ms":55.05600000000044,"wall_ms":60.15265407040715},{"cpu_ms":79.68800000000087,"wall_ms":89.11824482493103},{"cpu_ms":82.67299999999977,"wall_ms":87.07001712173223},{"cpu_ms":78.69599999999899,"wall_ms":87.04784605652094},{"cpu_ms":80.41200000000082,"wall_ms":88.78313889726996},{"cpu_ms":81.40899999999895,"wall_ms":88.84851983748376},{"cpu_ms":82.45199999999997,"wall_ms":87.8965319134295},{"cpu_ms":78.39799999999997,"wall_ms":85.72619711048901},{"cpu_ms":74.75600000000071,"wall_ms":85.71090805344284},{"cpu_ms":77.21900000000126,"wall_ms":86.58359805122018},{"cpu_ms":78.06499999999872,"wall_ms":85.4762401431799},{"cpu_ms":81.32500000000142,"wall_ms":87.7702459692955},{"cpu_ms":76.07499999999945,"wall_ms":82.48962997458875},{"cpu_ms":47.81499999999994,"wall_ms":53.26887220144272},{"cpu_ms":82.84599999999998,"wall_ms":89.22378183342516},{"cpu_ms":78.88299999999937,"wall_ms":85.23573004640639},{"cpu_ms":80.3370000000001,"wall_ms":86.70616708695889},{"cpu_ms":82.27199999999968,"wall_ms":88.71491113677621},{"cpu_ms":79.09599999999983,"wall_ms":87.50432915985584},{"cpu_ms":78.1010000000002,"wall_ms":84.4942438416183},{"cpu_ms":82.09599999999995,"wall_ms":87.47608587145805},{"cpu_ms":79.93699999999926,"wall_ms":86.31599904038012},{"cpu_ms":81.6030000000012,"wall_ms":88.08254892937839},{"cpu_ms":81.26399999999911,"wall_ms":86.63938310928643},{"cpu_ms":76.76999999999978,"wall_ms":88.10857799835503},{"cpu_ms":58.48300000000073,"wall_ms":65.83929900079966},{"cpu_ms":79.82499999999959,"wall_ms":88.25055416673422},{"cpu_ms":72.39699999999871,"wall_ms":84.6994190942496},{"cpu_ms":75.40200000000041,"wall_ms":81.72487001866102},{"cpu_ms":73.25600000000065,"wall_ms":84.13193793967366},{"cpu_ms":78.09499999999936,"wall_ms":85.49657789990306},{"cpu_ms":50.28399999999955,"wall_ms":57.596547063440084},{"cpu_ms":45.44100000000029,"wall_ms":57.67591600306332},{"cpu_ms":81.4319999999995,"wall_ms":85.82581300288439},{"cpu_ms":81.52199999999965,"wall_ms":87.87858788855374},{"cpu_ms":77.53299999999896,"wall_ms":86.90843591466546},{"cpu_ms":80.69600000000143,"wall_ms":87.11014315485954},{"cpu_ms":76.13499999999895,"wall_ms":83.54220609180629},{"cpu_ms":81.88600000000079,"wall_ms":88.36805308237672},{"cpu_ms":79.79300000000045,"wall_ms":86.1466929782182},{"cpu_ms":80.44300000000071,"wall_ms":87.81207399442792},{"cpu_ms":68.27299999999958,"wall_ms":77.52366014756262},{"cpu_ms":57.10999999999977,"wall_ms":61.57899787649512},{"cpu_ms":80.88300000000004,"wall_ms":86.24868304468691},{"cpu_ms":77.66100000000087,"wall_ms":86.99962613172829},{"cpu_ms":77.66799999999918,"wall_ms":86.0162079334259},{"cpu_ms":67.46800000000164,"wall_ms":76.84306893497705}],"after_median_cpu_ms":77.5999999999999}]}, + {"name":"merged-isolated","rows":[{"mode":"segment","scale":100,"samples":{"before":[{"segment":45.938},{"segment":38.933},{"segment":38.647},{"segment":61.047},{"segment":43.925},{"segment":43.525},{"segment":42.04}],"after":[{"segment":47.466},{"segment":58.532},{"segment":37.84},{"segment":48.663},{"segment":37.944},{"segment":38.073},{"segment":43.429}],"bun":[{"segment":4.903},{"segment":4.175},{"segment":7.006},{"segment":6.848},{"segment":6.413},{"segment":4.339},{"segment":6.915}]},"output":["checksum 1340000"],"medians_us":{"before":{"segment":43.525},"after":{"segment":43.429},"bun":{"segment":6.413}}},{"mode":"eastAsianWidth","scale":1000,"samples":{"before":[{"eastAsianWidth":0.847},{"eastAsianWidth":0.607},{"eastAsianWidth":0.5},{"eastAsianWidth":0.489},{"eastAsianWidth":0.51},{"eastAsianWidth":0.545},{"eastAsianWidth":0.487}],"after":[{"eastAsianWidth":0.587},{"eastAsianWidth":0.693},{"eastAsianWidth":0.594},{"eastAsianWidth":0.521},{"eastAsianWidth":0.52},{"eastAsianWidth":0.579},{"eastAsianWidth":0.519}],"bun":[{"eastAsianWidth":0.013},{"eastAsianWidth":0.026},{"eastAsianWidth":0.019},{"eastAsianWidth":0.024},{"eastAsianWidth":0.011},{"eastAsianWidth":0.011},{"eastAsianWidth":0.01}]},"output":["checksum 2000000"],"medians_us":{"before":{"eastAsianWidth":0.51},"after":{"eastAsianWidth":0.579},"bun":{"eastAsianWidth":0.013}}}]}, + {"name":"isolated-final-fixed","rows":[{"mode":"segment","scale":100,"samples":{"before":[{"segment":69.449},{"segment":74.236},{"segment":70.424},{"segment":67.751},{"segment":67.382},{"segment":68.447},{"segment":68.163}],"after":[{"segment":70.255},{"segment":81.531},{"segment":71.369},{"segment":76.9},{"segment":66.448},{"segment":68.266},{"segment":70.403}],"bun":[{"segment":7.04},{"segment":7.168},{"segment":7.723},{"segment":9.088},{"segment":9.049},{"segment":6.826},{"segment":7.096}]},"output":["checksum 1340000"],"medians_us":{"before":{"segment":68.447},"after":{"segment":70.403},"bun":{"segment":7.168}}},{"mode":"eastAsianWidth","scale":1000,"samples":{"before":[{"eastAsianWidth":0.907},{"eastAsianWidth":0.807},{"eastAsianWidth":0.866},{"eastAsianWidth":0.839},{"eastAsianWidth":0.964},{"eastAsianWidth":0.866},{"eastAsianWidth":0.823}],"after":[{"eastAsianWidth":0.889},{"eastAsianWidth":0.924},{"eastAsianWidth":0.888},{"eastAsianWidth":0.772},{"eastAsianWidth":0.838},{"eastAsianWidth":0.858},{"eastAsianWidth":0.956}],"bun":[{"eastAsianWidth":0.025},{"eastAsianWidth":0.027},{"eastAsianWidth":0.011},{"eastAsianWidth":0.011},{"eastAsianWidth":0.025},{"eastAsianWidth":0.011},{"eastAsianWidth":0.028}]},"output":["checksum 2000000"],"medians_us":{"before":{"eastAsianWidth":0.866},"after":{"eastAsianWidth":0.888},"bun":{"eastAsianWidth":0.025}}}]} +] diff --git a/benchmarks/regexp-construction/merged-probe-samples.json b/benchmarks/regexp-construction/merged-probe-samples.json new file mode 100644 index 0000000000..b1efc8a13f --- /dev/null +++ b/benchmarks/regexp-construction/merged-probe-samples.json @@ -0,0 +1,8 @@ +[ + {"mode":"all","scale":1,"samples":{"before":[{"construct":707.812,"test-ascii":10.654,"test-emoji":12.133,"stripAnsi":14.184,"stripAnsi-match":29.73,"ignorable":1.958,"segment":48.5,"eastAsianWidth":0.75,"stringWidth":43262.696},{"construct":686.233,"test-ascii":11.238,"test-emoji":12.438,"stripAnsi":14.861,"stripAnsi-match":30.126,"ignorable":2.069,"segment":51.165,"eastAsianWidth":0.933,"stringWidth":44611.554},{"construct":679.466,"test-ascii":12.03,"test-emoji":13.253,"stripAnsi":15.33,"stripAnsi-match":31.129,"ignorable":1.926,"segment":45.812,"eastAsianWidth":0.846,"stringWidth":44034.762},{"construct":717.711,"test-ascii":11.54,"test-emoji":12.569,"stripAnsi":14.978,"stripAnsi-match":30.29,"ignorable":2.047,"segment":50.376,"eastAsianWidth":0.928,"stringWidth":42995.951},{"construct":610.663,"test-ascii":10.561,"test-emoji":11.871,"stripAnsi":14.047,"stripAnsi-match":28.931,"ignorable":2.01,"segment":47.398,"eastAsianWidth":0.859,"stringWidth":44875.761},{"construct":685.625,"test-ascii":12.17,"test-emoji":13.21,"stripAnsi":13.762,"stripAnsi-match":28.509,"ignorable":1.887,"segment":46.19,"eastAsianWidth":0.895,"stringWidth":42953.445},{"construct":542.416,"test-ascii":9.736,"test-emoji":10.982,"stripAnsi":12.579,"stripAnsi-match":26.041,"ignorable":1.824,"segment":43.648,"eastAsianWidth":0.8,"stringWidth":42379.151}],"after":[{"construct":5.036,"test-ascii":0.971,"test-emoji":1.832,"stripAnsi":0.714,"stripAnsi-match":3.559,"ignorable":0.429,"segment":51.554,"eastAsianWidth":2.445,"stringWidth":438.907},{"construct":4.948,"test-ascii":0.911,"test-emoji":1.738,"stripAnsi":0.626,"stripAnsi-match":5.226,"ignorable":0.455,"segment":52.135,"eastAsianWidth":0.941,"stringWidth":402.426},{"construct":4.457,"test-ascii":0.872,"test-emoji":1.632,"stripAnsi":0.581,"stripAnsi-match":4.351,"ignorable":0.336,"segment":51.59,"eastAsianWidth":2.376,"stringWidth":385.463},{"construct":4.752,"test-ascii":0.958,"test-emoji":1.785,"stripAnsi":0.682,"stripAnsi-match":3.652,"ignorable":0.43,"segment":61.254,"eastAsianWidth":0.912,"stringWidth":439.34},{"construct":4.534,"test-ascii":0.943,"test-emoji":1.717,"stripAnsi":0.628,"stripAnsi-match":3.463,"ignorable":0.409,"segment":52.77,"eastAsianWidth":0.851,"stringWidth":364.887},{"construct":4.937,"test-ascii":0.915,"test-emoji":1.643,"stripAnsi":0.625,"stripAnsi-match":4.91,"ignorable":0.359,"segment":48.096,"eastAsianWidth":0.91,"stringWidth":416.041},{"construct":4.681,"test-ascii":0.925,"test-emoji":1.713,"stripAnsi":0.701,"stripAnsi-match":3.544,"ignorable":0.392,"segment":51.052,"eastAsianWidth":0.882,"stringWidth":391.746}],"bun":[{"construct":3.688,"test-ascii":0.753,"test-emoji":0.568,"stripAnsi":0.622,"stripAnsi-match":0.482,"ignorable":0.304,"segment":18.885,"eastAsianWidth":1.293,"stringWidth":53.218},{"construct":3.401,"test-ascii":0.721,"test-emoji":0.55,"stripAnsi":0.642,"stripAnsi-match":0.682,"ignorable":0.33,"segment":20.445,"eastAsianWidth":3.246,"stringWidth":54.055},{"construct":4.015,"test-ascii":0.7,"test-emoji":0.599,"stripAnsi":0.61,"stripAnsi-match":0.466,"ignorable":0.3,"segment":11.192,"eastAsianWidth":0.755,"stringWidth":87.179},{"construct":3.516,"test-ascii":0.698,"test-emoji":0.623,"stripAnsi":0.603,"stripAnsi-match":0.46,"ignorable":0.33,"segment":20.31,"eastAsianWidth":1.355,"stringWidth":58.921},{"construct":3.176,"test-ascii":0.78,"test-emoji":0.492,"stripAnsi":0.543,"stripAnsi-match":0.577,"ignorable":0.75,"segment":19.049,"eastAsianWidth":1.196,"stringWidth":41.386},{"construct":3.309,"test-ascii":0.654,"test-emoji":0.48,"stripAnsi":0.562,"stripAnsi-match":0.406,"ignorable":0.28,"segment":16.346,"eastAsianWidth":1.454,"stringWidth":87.534},{"construct":3.681,"test-ascii":0.714,"test-emoji":0.585,"stripAnsi":0.574,"stripAnsi-match":0.65,"ignorable":0.294,"segment":17.349,"eastAsianWidth":0.768,"stringWidth":52.279}]},"output":["constructed g 14116","checksum 162430"],"medians_us":{"before":{"construct":685.625,"test-ascii":11.238,"test-emoji":12.438,"stripAnsi":14.184,"stripAnsi-match":29.73,"ignorable":1.958,"segment":47.398,"eastAsianWidth":0.859,"stringWidth":43262.696},"after":{"construct":4.752,"test-ascii":0.925,"test-emoji":1.717,"stripAnsi":0.628,"stripAnsi-match":3.652,"ignorable":0.409,"segment":51.59,"eastAsianWidth":0.912,"stringWidth":402.426},"bun":{"construct":3.516,"test-ascii":0.714,"test-emoji":0.568,"stripAnsi":0.603,"stripAnsi-match":0.482,"ignorable":0.304,"segment":18.885,"eastAsianWidth":1.293,"stringWidth":54.055}}}, + {"mode":"construct","scale":10,"samples":{"before":[{"construct":637.625},{"construct":526.835},{"construct":590.414},{"construct":513.052},{"construct":618.687},{"construct":619.67},{"construct":570.705}],"after":[{"construct":0.553},{"construct":0.639},{"construct":0.641},{"construct":0.448},{"construct":0.637},{"construct":0.625},{"construct":0.703}],"bun":[{"construct":0.648},{"construct":0.684},{"construct":0.713},{"construct":0.688},{"construct":0.718},{"construct":0.728},{"construct":0.72}]},"output":["constructed g 14116","checksum 0"],"medians_us":{"before":{"construct":590.414},"after":{"construct":0.637},"bun":{"construct":0.713}}}, + {"mode":"test-ascii","scale":100,"samples":{"before":[{"test-ascii":10.069},{"test-ascii":8.96},{"test-ascii":9.47},{"test-ascii":9.845},{"test-ascii":8.107},{"test-ascii":10.972},{"test-ascii":10.037}],"after":[{"test-ascii":0.686},{"test-ascii":0.76},{"test-ascii":0.739},{"test-ascii":0.894},{"test-ascii":0.931},{"test-ascii":0.911},{"test-ascii":0.914}],"bun":[{"test-ascii":0.039},{"test-ascii":0.045},{"test-ascii":0.043},{"test-ascii":0.055},{"test-ascii":0.055},{"test-ascii":0.053},{"test-ascii":0.058}]},"output":["checksum 0"],"medians_us":{"before":{"test-ascii":9.845},"after":{"test-ascii":0.894},"bun":{"test-ascii":0.053}}}, + {"mode":"test-emoji","scale":100,"samples":{"before":[{"test-emoji":11.996},{"test-emoji":12.072},{"test-emoji":12.901},{"test-emoji":11.382},{"test-emoji":12.682},{"test-emoji":12.848},{"test-emoji":12.554}],"after":[{"test-emoji":0.987},{"test-emoji":1.746},{"test-emoji":1.737},{"test-emoji":1.732},{"test-emoji":1.665},{"test-emoji":1.721},{"test-emoji":1.578}],"bun":[{"test-emoji":0.043},{"test-emoji":0.07},{"test-emoji":0.079},{"test-emoji":0.065},{"test-emoji":0.066},{"test-emoji":0.068},{"test-emoji":0.079}]},"output":["checksum 100000"],"medians_us":{"before":{"test-emoji":12.554},"after":{"test-emoji":1.721},"bun":{"test-emoji":0.068}}}, + {"mode":"stripAnsi","scale":100,"samples":{"before":[{"stripAnsi":13.444},{"stripAnsi":13.746},{"stripAnsi":10.031},{"stripAnsi":14.785},{"stripAnsi":14.991},{"stripAnsi":15.44},{"stripAnsi":13.988}],"after":[{"stripAnsi":0.651},{"stripAnsi":0.625},{"stripAnsi":0.348},{"stripAnsi":0.631},{"stripAnsi":0.649},{"stripAnsi":0.656},{"stripAnsi":0.616}],"bun":[{"stripAnsi":0.056},{"stripAnsi":0.068},{"stripAnsi":0.044},{"stripAnsi":0.056},{"stripAnsi":0.072},{"stripAnsi":0.077},{"stripAnsi":0.067}]},"output":["checksum 13400000"],"medians_us":{"before":{"stripAnsi":13.988},"after":{"stripAnsi":0.631},"bun":{"stripAnsi":0.067}}}, + {"mode":"stripAnsi-match","scale":100,"samples":{"before":[{"stripAnsi-match":20.324},{"stripAnsi-match":18.447},{"stripAnsi-match":20.871},{"stripAnsi-match":22.999},{"stripAnsi-match":25.843},{"stripAnsi-match":30.585},{"stripAnsi-match":30.497}],"after":[{"stripAnsi-match":2.11},{"stripAnsi-match":2.507},{"stripAnsi-match":2.885},{"stripAnsi-match":3.675},{"stripAnsi-match":3.472},{"stripAnsi-match":3.562},{"stripAnsi-match":4.014}],"bun":[{"stripAnsi-match":0.103},{"stripAnsi-match":0.151},{"stripAnsi-match":0.121},{"stripAnsi-match":0.092},{"stripAnsi-match":0.131},{"stripAnsi-match":0.128},{"stripAnsi-match":0.139}]},"output":["checksum 600000"],"medians_us":{"before":{"stripAnsi-match":22.999},"after":{"stripAnsi-match":3.472},"bun":{"stripAnsi-match":0.128}}} +] diff --git a/benchmarks/regexp-construction/merged-results.md b/benchmarks/regexp-construction/merged-results.md new file mode 100644 index 0000000000..eea5420f36 --- /dev/null +++ b/benchmarks/regexp-construction/merged-results.md @@ -0,0 +1,135 @@ +# Historical verification against main df886c6445 + +This intermediate revision predates upstream cross-call program witnesses and +Perex 0.1.4. Its binding cache has since been removed. +See [final-results.md](final-results.md) for the final implementation and evidence. + +The original measurements in [results.md](results.md) apply to commit +`62bddb2929` against `9b911855f8ca`. Before publishing the final revision, main +landed per-operation binding reuse, forward split search, and an unlimited +work policy. Merge `a33b82498a` integrates main `df886c644562dd745170b2d40c9fb7d3261112e3`. +Per-operation reuse now retains the same shared validated owner as standalone +calls; the current-receiver/current-program checks and subject reuse remain. +The main GC witness additionally checks that independent receivers incur no +second validation charge, across actual moving collections. + +Both fresh compiler/runtime/stdlib bundles use release builds and the same +explicit upstream flags: + +```sh +export RUSTFLAGS="-C force-unwind-tables=yes -C force-frame-pointers=yes" +cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static +``` + +The main bundle is `/tmp/regexp-current-main-libs`; the candidate bundle is +`/tmp/regexp-merged-libs`. The candidate build took 4m 56s. The lane's Mac Cargo +config remains untouched; the flags above are supplied on the build host. +All commands in this report run through `GIT_DIR=/tmp/perry-regexp-lane.git +./remote.sh ''`. + +## Fresh real-input probe + +Seven alternating rounds on CPU 15, identical counts, and checked checksums. +Values are median wall µs/operation. The original-count `all 1` probe uses 200 +constructions, 2,000 tests/replacements, 200 segmentations, and 90 width calls. +Raw data: [merged-probe-samples.json](merged-probe-samples.json). + +| Operation | Current main | Merged candidate | Bun 1.3.14 | +|---|---:|---:|---:| +| emoji construction | 685.625 | 4.752 | 3.516 | +| reused emoji test, ASCII | 11.238 | 0.925 | 0.714 | +| reused emoji test, rocket | 12.438 | 1.717 | 0.568 | +| stripAnsi, plain help line | 14.184 | 0.628 | 0.603 | +| stripAnsi, ANSI-colored text | 29.730 | 3.652 | 0.482 | +| Default_Ignorable test | 1.958 | 0.409 | 0.304 | +| Intl.Segmenter, mixed run | 47.398 | 51.590 | 18.885 | +| eastAsianWidth, mixed run | 0.859 | 0.912 | 1.293 | +| stringWidth | 43262.696 | 402.426 | 54.055 | + +Construction improves 144×, plain stripAnsi 23×, and stringWidth 108×. Both +reused tests meet 2 µs, construction meets 20 µs, and plain stripAnsi meets +1 µs. Replacement with actual ANSI matches still takes 3.652 µs. + +Longer isolated cases use 2,000 constructions and 200,000 calls otherwise: + +| Operation | Current main | Merged candidate | Bun 1.3.14 | +|---|---:|---:|---:| +| emoji construction | 590.414 | 0.637 | 0.713 | +| reused emoji test, ASCII | 9.845 | 0.894 | 0.053 | +| reused emoji test, rocket | 12.554 | 1.721 | 0.068 | +| stripAnsi, plain help line | 13.988 | 0.631 | 0.067 | +| stripAnsi, ANSI-colored text | 22.999 | 3.472 | 0.128 | + +The first compilation remains eager and contributes to the short construction +run. The longer run amortizes it. These values do not measure complete OpenCode +startup. Isolated controls for the slower mixed-run non-regex cases are below. + +## Existing app-pattern kernels + +All 12 kernels match Node output under both builds. Fifteen alternating rounds, +one warmup, a fixed executable pathname/inode, CPU 15; medians in child user CPU +milliseconds. Raw data: [merged-app-samples.json](merged-app-samples.json). + +| Kernel | Current main | Merged candidate | Change | +|---|---:|---:|---:| +| batch | 50.424 | 49.647 | -1.54% | +| buffer_transcode | 74.968 | 74.733 | -0.31% | +| date_format_parse | 47.963 | 47.347 | -1.28% | +| json_parse_1mb | 88.707 | 86.485 | -2.50% | +| json_stringify_1mb | 136.164 | 138.734 | 1.89% | +| map_1m | 337.453 | 333.186 | -1.26% | +| object_deep_clone | 29.958 | 28.791 | -3.90% | +| promise_all_chains | 189.206 | 187.887 | -0.70% | +| regex_replace | 5392.096 | 4472.262 | -17.06% | +| string_concat_csv | 47.338 | 48.372 | 2.18% | +| string_split_map_join | 302.636 | 303.716 | 0.36% | +| string_template_interp | 78.603 | 81.695 | 3.93% | + +Existing regex replacement improves 17.1%. The positive deltas are retained +here; follow-up 101-round comparisons and A/A controls are reported below. + + +## Correctness and repository checks + +- Regex unit slice: 103 passed. +- Full runtime lib: 3,708 passed, one failed, four ignored, 119.61 seconds. + The sole failure is `native_stack::tests::stack_top_respects_custom_thread_stack_sizes`. + It reproduces on current main with identical flags (seven pass, one fail). +- Regex parity slice, Node 26.5.1: 25 pass, zero differences, zero compile + failures, zero crashes/skips. The HTTP-extension case now passes too. +- Both full RegExp Test262 runs: 1,786 pass, 129 runtime failures, seven skips, + zero differences/compile failures. All 129 failing `(test, bucket)` pairs are + identical; no new or resolved failures. +- Formatting, the 2,000-line Rust cap, registration (294 files/four registries), + and GC root inventory (1,411 holders/155 scanners) pass. +- `pre-tag-check.sh --quick`: all checks pass except public benchmark evidence + freshness, also reproduced on the untouched baseline. GC store and address + classification audits pass. + +```sh +export PATH=/tmp/regexp-oracle/node-v26.5.1-linux-x64/bin:$PATH +RUST_TEST_THREADS=1 cargo test -p perry-runtime --lib regex +RUST_TEST_THREADS=1 cargo test -p perry-runtime --lib +RUST_TEST_THREADS=1 cargo test --manifest-path /tmp/regexp-current-main/Cargo.toml \ + -p perry-runtime --lib native_stack +PERRY_SKIP_BUILD=1 PERRY_BIN=/tmp/regexp-merged-libs/perry \ + PERRY_RUNTIME_DIR=/tmp/regexp-merged-libs ./run_parity_tests.sh --filter regex +cargo fmt --all -- --check +./scripts/check_file_size.sh +python3 scripts/check_test_registration.py +python3 scripts/gc_runtime_root_holders.py +./scripts/pre-tag-check.sh --quick +PERRY_RUNTIME_DIR=/tmp/regexp-current-main-libs python3 scripts/test262_subset.py \ + --root /tmp/regexp-test262 --dir built-ins/RegExp annexB/built-ins/RegExp \ + --all-features --jobs 4 --perry-bin /tmp/regexp-current-main-libs/perry \ + --report /tmp/regexp-test262-current-main.json --sample-cap 100000 +PERRY_RUNTIME_DIR=/tmp/regexp-merged-libs python3 scripts/test262_subset.py \ + --root /tmp/regexp-test262 --dir built-ins/RegExp annexB/built-ins/RegExp \ + --all-features --jobs 4 --perry-bin /tmp/regexp-merged-libs/perry \ + --report /tmp/regexp-test262-merged.json --sample-cap 100000 +``` + +For the HTTP extension rebuild, the host checked out the exact sync commit +used to build the preserved compiler, `125cb677813e7c5325e6c122583c3e8910b6cdcc`, +before invoking parity. This prevents the earlier archive/compiler build-ID +mismatch; the source is the merged candidate above. diff --git a/benchmarks/regexp-construction/pre-hint-app-samples.json b/benchmarks/regexp-construction/pre-hint-app-samples.json new file mode 100644 index 0000000000..1301dcb225 --- /dev/null +++ b/benchmarks/regexp-construction/pre-hint-app-samples.json @@ -0,0 +1,14 @@ +[ +{"name":"batch","node_exit":0,"before":[{"cpu_ms":44.83499999999996,"wall_ms":51.41478986479342},{"cpu_ms":39.83800000000004,"wall_ms":46.18555400520563},{"cpu_ms":44.8059999999999,"wall_ms":48.18046907894313},{"cpu_ms":44.45500000000013,"wall_ms":52.77406005188823},{"cpu_ms":44.938000000000144,"wall_ms":52.366757998242974},{"cpu_ms":43.715000000000174,"wall_ms":51.0642658919096},{"cpu_ms":37.510000000000154,"wall_ms":41.97398805990815},{"cpu_ms":27.24800000000016,"wall_ms":31.59291809424758},{"cpu_ms":28.821000000000208,"wall_ms":34.61068496108055},{"cpu_ms":26.91999999999961,"wall_ms":31.2097929418087},{"cpu_ms":28.920999999999975,"wall_ms":32.2345569729805},{"cpu_ms":26.034000000000113,"wall_ms":31.381193082779646},{"cpu_ms":44.97200000000001,"wall_ms":50.40619685314596},{"cpu_ms":39.082000000000065,"wall_ms":44.43316091783345},{"cpu_ms":25.9100000000001,"wall_ms":30.2346411626786}],"before_median_cpu_ms":39.082000000000065,"after":[{"cpu_ms":37.41100000000009,"wall_ms":43.901968048885465},{"cpu_ms":42.67299999999974,"wall_ms":47.13212116621435},{"cpu_ms":42.42900000000027,"wall_ms":48.82922093383968},{"cpu_ms":43.71400000000003,"wall_ms":48.04725106805563},{"cpu_ms":50.97499999999977,"wall_ms":56.46216799505055},{"cpu_ms":34.81999999999985,"wall_ms":40.123821003362536},{"cpu_ms":27.441999999999744,"wall_ms":33.860953990370035},{"cpu_ms":24.652999999999814,"wall_ms":30.322043923661113},{"cpu_ms":28.769000000000045,"wall_ms":31.06155595742166},{"cpu_ms":26.669000000000054,"wall_ms":32.00401505455375},{"cpu_ms":29.87200000000012,"wall_ms":32.280182000249624},{"cpu_ms":46.73499999999997,"wall_ms":50.09839194826782},{"cpu_ms":43.41799999999996,"wall_ms":47.73983685299754},{"cpu_ms":45.6669999999999,"wall_ms":47.027976950630546},{"cpu_ms":41.92499999999999,"wall_ms":46.29741283133626}],"after_median_cpu_ms":41.92499999999999}, +{"name":"buffer_transcode","node_exit":0,"before":[{"cpu_ms":44.26799999999975,"wall_ms":58.68580099195242},{"cpu_ms":42.292999999999914,"wall_ms":57.72925494238734},{"cpu_ms":46.91699999999965,"wall_ms":62.24167300388217},{"cpu_ms":45.17500000000041,"wall_ms":61.5936319809407},{"cpu_ms":44.140999999999764,"wall_ms":60.5728761292994},{"cpu_ms":48.976999999999826,"wall_ms":62.36551399342716},{"cpu_ms":45.02800000000029,"wall_ms":60.37428509443998},{"cpu_ms":43.91199999999973,"wall_ms":60.62115705572069},{"cpu_ms":51.3659999999998,"wall_ms":66.81471387855709},{"cpu_ms":42.044000000000636,"wall_ms":61.403818195685744},{"cpu_ms":43.998000000000204,"wall_ms":66.35006610304117},{"cpu_ms":41.64499999999993,"wall_ms":64.7618598304689},{"cpu_ms":67.47800000000038,"wall_ms":86.83097106404603},{"cpu_ms":52.654999999999674,"wall_ms":67.9192179813981},{"cpu_ms":72.17200000000012,"wall_ms":91.58552112057805}],"before_median_cpu_ms":45.02800000000029,"after":[{"cpu_ms":48.9139999999999,"wall_ms":60.24939101189375},{"cpu_ms":49.98700000000067,"wall_ms":64.38306299969554},{"cpu_ms":74.75699999999996,"wall_ms":90.10687889531255},{"cpu_ms":70.27599999999978,"wall_ms":88.78357708454132},{"cpu_ms":50.88000000000026,"wall_ms":65.2643779758364},{"cpu_ms":50.57500000000026,"wall_ms":72.84541800618172},{"cpu_ms":45.9429999999994,"wall_ms":66.26499793492258},{"cpu_ms":71.30400000000049,"wall_ms":88.75642693601549},{"cpu_ms":68.64899999999975,"wall_ms":101.83999384753406},{"cpu_ms":59.30400000000002,"wall_ms":74.7405078727752},{"cpu_ms":63.67699999999932,"wall_ms":82.93965202756226},{"cpu_ms":43.548000000000364,"wall_ms":63.12767812050879},{"cpu_ms":62.274999999999636,"wall_ms":75.72818198241293},{"cpu_ms":49.78000000000016,"wall_ms":65.0544969830662},{"cpu_ms":56.81199999999986,"wall_ms":70.44503511860967}],"after_median_cpu_ms":56.81199999999986}, +{"name":"date_format_parse","node_exit":0,"before":[{"cpu_ms":29.84200000000037,"wall_ms":31.141786137595773},{"cpu_ms":29.367000000000587,"wall_ms":31.781970988959074},{"cpu_ms":26.851000000000624,"wall_ms":33.20903703570366},{"cpu_ms":27.295999999999765,"wall_ms":31.592166982591152},{"cpu_ms":26.91699999999919,"wall_ms":33.27063214965165},{"cpu_ms":28.850000000000264,"wall_ms":33.24460284784436},{"cpu_ms":26.17600000000131,"wall_ms":30.50464508123696},{"cpu_ms":36.79299999999941,"wall_ms":45.10380304418504},{"cpu_ms":44.465999999999894,"wall_ms":50.815252121537924},{"cpu_ms":46.336999999999406,"wall_ms":49.62693201377988},{"cpu_ms":48.040999999999556,"wall_ms":50.44329701922834},{"cpu_ms":32.735000000000625,"wall_ms":38.230185164138675},{"cpu_ms":37.308999999998704,"wall_ms":41.756521910429},{"cpu_ms":25.482000000000227,"wall_ms":30.685554025694728},{"cpu_ms":30.113000000000056,"wall_ms":35.498322919011116}],"before_median_cpu_ms":29.84200000000037,"after":[{"cpu_ms":31.181000000000125,"wall_ms":34.57065997645259},{"cpu_ms":25.741000000000014,"wall_ms":31.1110180336982},{"cpu_ms":32.960999999998464,"wall_ms":37.38250187598169},{"cpu_ms":34.38900000000089,"wall_ms":35.72147898375988},{"cpu_ms":29.57400000000021,"wall_ms":30.957822920754552},{"cpu_ms":36.65499999999966,"wall_ms":41.982122929766774},{"cpu_ms":31.162999999999386,"wall_ms":36.581967025995255},{"cpu_ms":42.69299999999987,"wall_ms":47.04478895291686},{"cpu_ms":34.68800000000094,"wall_ms":38.049387047067285},{"cpu_ms":41.34200000000021,"wall_ms":46.71846888959408},{"cpu_ms":44.90499999999997,"wall_ms":49.24670094624162},{"cpu_ms":29.68199999999932,"wall_ms":31.00784495472908},{"cpu_ms":44.042000000001025,"wall_ms":47.37794096581638},{"cpu_ms":28.188000000000102,"wall_ms":32.43791707791388},{"cpu_ms":28.1520000000004,"wall_ms":31.52411011978984}],"after_median_cpu_ms":32.960999999998464}, +{"name":"json_parse_1mb","node_exit":0,"before":[{"cpu_ms":72.26700000000008,"wall_ms":80.76526108197868},{"cpu_ms":61.10999999999933,"wall_ms":65.42055099271238},{"cpu_ms":58.21000000000076,"wall_ms":67.61483917944133},{"cpu_ms":51.264999999999006,"wall_ms":54.60203508846462},{"cpu_ms":49.99399999999987,"wall_ms":54.300602059811354},{"cpu_ms":51.50599999999983,"wall_ms":55.86286913603544},{"cpu_ms":66.36199999999981,"wall_ms":71.78046903572977},{"cpu_ms":54.941999999998714,"wall_ms":59.29544102400541},{"cpu_ms":48.74000000000045,"wall_ms":54.01647090911865},{"cpu_ms":51.747999999999905,"wall_ms":62.1175121050328},{"cpu_ms":50.96100000000092,"wall_ms":56.28762277774513},{"cpu_ms":50.530000000000186,"wall_ms":59.80680603533983},{"cpu_ms":63.197999999999865,"wall_ms":76.44726615399122},{"cpu_ms":69.28399999999968,"wall_ms":89.43413314409554},{"cpu_ms":49.78000000000016,"wall_ms":55.065820924937725}],"before_median_cpu_ms":51.747999999999905,"after":[{"cpu_ms":53.596999999999895,"wall_ms":59.04590478166938},{"cpu_ms":58.725000000000804,"wall_ms":60.0950641091913},{"cpu_ms":51.32199999999898,"wall_ms":56.66340491734445},{"cpu_ms":49.783000000001465,"wall_ms":55.087871849536896},{"cpu_ms":58.099999999999596,"wall_ms":63.38694295845926},{"cpu_ms":53.96499999999982,"wall_ms":56.27871584147215},{"cpu_ms":62.66500000000086,"wall_ms":71.006994927302},{"cpu_ms":80.50700000000077,"wall_ms":87.81738299876451},{"cpu_ms":70.88699999999903,"wall_ms":76.2153030373156},{"cpu_ms":60.56900000000098,"wall_ms":66.88685016706586},{"cpu_ms":55.29999999999902,"wall_ms":62.72525689564645},{"cpu_ms":52.01399999999978,"wall_ms":54.34081703424454},{"cpu_ms":90.23000000000003,"wall_ms":94.64163985103369},{"cpu_ms":59.141999999999584,"wall_ms":63.56228096410632},{"cpu_ms":50.027000000000044,"wall_ms":55.429260013625026}],"after_median_cpu_ms":58.099999999999596}, +{"name":"json_stringify_1mb","node_exit":0,"before":[{"cpu_ms":104.99300000000034,"wall_ms":108.35419199429452},{"cpu_ms":79.83899999999977,"wall_ms":86.15909609943628},{"cpu_ms":96.96399999999983,"wall_ms":112.40367614664137},{"cpu_ms":134.66000000000022,"wall_ms":143.10982800088823},{"cpu_ms":77.79700000000034,"wall_ms":84.12412600591779},{"cpu_ms":80.66700000000004,"wall_ms":88.0689739715308},{"cpu_ms":79.7509999999999,"wall_ms":84.06334184110165},{"cpu_ms":84.7320000000007,"wall_ms":89.05862201936543},{"cpu_ms":104.00100000000023,"wall_ms":111.37423291802406},{"cpu_ms":89.69000000000094,"wall_ms":97.02269802801311},{"cpu_ms":91.71399999999963,"wall_ms":97.05106006003916},{"cpu_ms":87.06199999999953,"wall_ms":91.429912019521},{"cpu_ms":79.48600000000283,"wall_ms":83.80904700607061},{"cpu_ms":78.39299999999838,"wall_ms":83.69747898541391},{"cpu_ms":89.5469999999996,"wall_ms":92.03012310899794}],"before_median_cpu_ms":87.06199999999953,"after":[{"cpu_ms":105.65700000000078,"wall_ms":113.02840313874185},{"cpu_ms":91.58799999999978,"wall_ms":94.92643200792372},{"cpu_ms":74.63000000000086,"wall_ms":82.04428013414145},{"cpu_ms":76.00999999999836,"wall_ms":82.36296591348946},{"cpu_ms":117.84600000000012,"wall_ms":127.2149698343128},{"cpu_ms":87.34899999999968,"wall_ms":95.67971900105476},{"cpu_ms":88.8100000000005,"wall_ms":93.22022506967187},{"cpu_ms":84.47099999999885,"wall_ms":91.7716701515019},{"cpu_ms":76.93700000000092,"wall_ms":80.23561304435134},{"cpu_ms":77.88099999999787,"wall_ms":83.17940100096166},{"cpu_ms":76.37300000000025,"wall_ms":85.73683793656528},{"cpu_ms":74.05300000000281,"wall_ms":80.28689795173705},{"cpu_ms":71.50399999999735,"wall_ms":79.75655794143677},{"cpu_ms":123.39899999999915,"wall_ms":125.73554716072977},{"cpu_ms":105.91600000000057,"wall_ms":111.30522517487407}],"after_median_cpu_ms":84.47099999999885}, +{"name":"map_1m","node_exit":0,"before":[{"cpu_ms":225.39800000000199,"wall_ms":267.6245369948447},{"cpu_ms":214.7289999999984,"wall_ms":222.1562610939145},{"cpu_ms":271.83399999999835,"wall_ms":295.1926179230213},{"cpu_ms":255.89800000000196,"wall_ms":352.2706881631166},{"cpu_ms":307.36799999999675,"wall_ms":316.76628394052386},{"cpu_ms":249.42800000000176,"wall_ms":260.8563359826803},{"cpu_ms":232.20799999999997,"wall_ms":244.54334611073136},{"cpu_ms":232.78000000000176,"wall_ms":251.13897607661784},{"cpu_ms":223.00399999999954,"wall_ms":231.34088818915188},{"cpu_ms":220.8330000000025,"wall_ms":229.28507695905864},{"cpu_ms":277.5190000000016,"wall_ms":284.89041700959206},{"cpu_ms":268.6789999999988,"wall_ms":284.02383904904127},{"cpu_ms":302.4729999999991,"wall_ms":310.8787080273032},{"cpu_ms":234.46400000000267,"wall_ms":244.80093806050718},{"cpu_ms":277.000000000001,"wall_ms":289.386646123603}],"before_median_cpu_ms":249.42800000000176,"after":[{"cpu_ms":248.55499999999964,"wall_ms":271.9615870155394},{"cpu_ms":214.66799999999964,"wall_ms":222.97048196196556},{"cpu_ms":250.31000000000247,"wall_ms":321.82676484808326},{"cpu_ms":259.02599999999865,"wall_ms":267.45164417661726},{"cpu_ms":237.42500000000177,"wall_ms":246.84422393329442},{"cpu_ms":244.3010000000001,"wall_ms":256.6799649503082},{"cpu_ms":234.08999999999835,"wall_ms":244.3717960268259},{"cpu_ms":232.0469999999979,"wall_ms":248.4407660085708},{"cpu_ms":249.99700000000047,"wall_ms":257.36562605015934},{"cpu_ms":223.85699999999886,"wall_ms":235.1624348666519},{"cpu_ms":240.09500000000017,"wall_ms":248.51564690470695},{"cpu_ms":235.5860000000014,"wall_ms":245.96318905241787},{"cpu_ms":254.61899999999815,"wall_ms":265.9838139079511},{"cpu_ms":255.34099999999782,"wall_ms":266.7025360278785},{"cpu_ms":253.31500000000062,"wall_ms":262.63703196309507}],"after_median_cpu_ms":244.3010000000001}, +{"name":"object_deep_clone","node_exit":0,"before":[{"cpu_ms":28.710000000000235,"wall_ms":34.05223321169615},{"cpu_ms":16.76000000000144,"wall_ms":21.030059084296227},{"cpu_ms":17.48100000000008,"wall_ms":22.761313943192363},{"cpu_ms":32.983999999999014,"wall_ms":38.43093104660511},{"cpu_ms":25.832000000001187,"wall_ms":30.15689691528678},{"cpu_ms":29.50399999999931,"wall_ms":35.83392011933029},{"cpu_ms":26.20200000000139,"wall_ms":30.56208393536508},{"cpu_ms":31.027999999999167,"wall_ms":33.44488795846701},{"cpu_ms":17.261000000001303,"wall_ms":23.486437974497676},{"cpu_ms":27.81399999999934,"wall_ms":32.31917601078749},{"cpu_ms":27.842999999997176,"wall_ms":34.211500082165},{"cpu_ms":28.191000000003186,"wall_ms":33.6639869492501},{"cpu_ms":16.40599999999992,"wall_ms":21.710861939936876},{"cpu_ms":30.304000000000997,"wall_ms":34.72461900673807},{"cpu_ms":28.021000000002516,"wall_ms":31.40144213102758}],"before_median_cpu_ms":27.842999999997176,"after":[{"cpu_ms":19.798999999999012,"wall_ms":22.114885970950127},{"cpu_ms":16.59000000000077,"wall_ms":20.078973844647408},{"cpu_ms":17.192999999998904,"wall_ms":20.551435882225633},{"cpu_ms":20.144000000001938,"wall_ms":24.441061075776815},{"cpu_ms":18.702999999998582,"wall_ms":24.01990396901965},{"cpu_ms":25.82999999999913,"wall_ms":31.171042006462812},{"cpu_ms":23.361000000001297,"wall_ms":31.556851929053664},{"cpu_ms":27.04999999999913,"wall_ms":31.38727485202253},{"cpu_ms":21.823999999998733,"wall_ms":26.199355022981763},{"cpu_ms":27.879999999999683,"wall_ms":32.43109514005482},{"cpu_ms":26.97000000000216,"wall_ms":33.35504001006484},{"cpu_ms":29.65099999999765,"wall_ms":33.01387303508818},{"cpu_ms":31.1009999999996,"wall_ms":36.52130509726703},{"cpu_ms":18.07499999999962,"wall_ms":24.449827848002315},{"cpu_ms":31.200999999999368,"wall_ms":37.69332403317094}],"after_median_cpu_ms":23.361000000001297}, +{"name":"promise_all_chains","node_exit":0,"before":[{"cpu_ms":125.83000000000055,"wall_ms":131.23276690021157},{"cpu_ms":160.30800000000056,"wall_ms":166.7407900094986},{"cpu_ms":111.38299999999646,"wall_ms":115.71311997249722},{"cpu_ms":112.39700000000141,"wall_ms":121.82423379272223},{"cpu_ms":147.9579999999956,"wall_ms":156.42425999976695},{"cpu_ms":120.75500000000261,"wall_ms":126.06663815677166},{"cpu_ms":94.5339999999959,"wall_ms":101.8163429107517},{"cpu_ms":98.50899999999996,"wall_ms":103.86285604909062},{"cpu_ms":96.61299999999784,"wall_ms":102.90553793311119},{"cpu_ms":128.57300000000293,"wall_ms":134.96917602606118},{"cpu_ms":103.96000000000072,"wall_ms":111.2873530946672},{"cpu_ms":119.08299999999628,"wall_ms":123.46129212528467},{"cpu_ms":121.01299999999782,"wall_ms":123.34019597619772},{"cpu_ms":155.4959999999994,"wall_ms":159.94064789265394},{"cpu_ms":118.33800000000139,"wall_ms":125.66556897945702}],"before_median_cpu_ms":119.08299999999628,"after":[{"cpu_ms":93.84700000000024,"wall_ms":99.15480203926563},{"cpu_ms":158.59199999999873,"wall_ms":165.00958590768278},{"cpu_ms":107.28200000000143,"wall_ms":113.63742197863758},{"cpu_ms":104.01200000000443,"wall_ms":108.39574201963842},{"cpu_ms":165.46100000000052,"wall_ms":170.90475698933005},{"cpu_ms":97.23499999999774,"wall_ms":106.42969002947211},{"cpu_ms":127.7510000000035,"wall_ms":134.15803108364344},{"cpu_ms":120.78699999999998,"wall_ms":122.13929207064211},{"cpu_ms":95.87700000000154,"wall_ms":101.20199481025338},{"cpu_ms":176.0359999999963,"wall_ms":180.45794102363288},{"cpu_ms":136.2100000000055,"wall_ms":142.57627492770553},{"cpu_ms":108.82000000000147,"wall_ms":114.16819505393505},{"cpu_ms":96.09700000000032,"wall_ms":102.40336018614471},{"cpu_ms":166.51600000000144,"wall_ms":170.92399392277002},{"cpu_ms":139.79799999999898,"wall_ms":151.18001610971987}],"after_median_cpu_ms":120.78699999999998}, +{"name":"regex_replace","node_exit":0,"before":[{"cpu_ms":2677.5710000000004,"wall_ms":2697.970677167177},{"cpu_ms":2559.3100000000036,"wall_ms":2582.6589020434767},{"cpu_ms":2593.6319999999996,"wall_ms":2615.965170087293},{"cpu_ms":2575.847000000003,"wall_ms":2592.210296075791},{"cpu_ms":2645.103999999996,"wall_ms":2673.4496850986034},{"cpu_ms":2825.601000000006,"wall_ms":2847.9435788467526},{"cpu_ms":2783.5079999999975,"wall_ms":2804.8424129374325},{"cpu_ms":2891.9649999999992,"wall_ms":2908.363541821018},{"cpu_ms":3067.3689999999992,"wall_ms":3078.759499825537},{"cpu_ms":3492.304000000004,"wall_ms":3514.7398989647627},{"cpu_ms":2706.4160000000043,"wall_ms":2722.827456193045},{"cpu_ms":2811.4029999999984,"wall_ms":2830.8112821541727},{"cpu_ms":2954.9240000000054,"wall_ms":2975.3242949955165},{"cpu_ms":2993.317999999988,"wall_ms":3038.2688951212913},{"cpu_ms":3147.6200000000035,"wall_ms":3166.040320880711}],"before_median_cpu_ms":2811.4029999999984,"after":[{"cpu_ms":2745.1589999999937,"wall_ms":2767.4913411028683},{"cpu_ms":2160.060999999999,"wall_ms":2178.435869049281},{"cpu_ms":2256.4670000000006,"wall_ms":2268.7975640874356},{"cpu_ms":2259.059999999998,"wall_ms":2283.8966329582036},{"cpu_ms":2190.784999999998,"wall_ms":2204.2509231250733},{"cpu_ms":2338.516999999996,"wall_ms":2357.882808893919},{"cpu_ms":2182.208000000003,"wall_ms":2192.617404041812},{"cpu_ms":2303.179,"wall_ms":2319.553887937218},{"cpu_ms":2615.6450000000004,"wall_ms":2643.0622411426157},{"cpu_ms":2640.3739999999943,"wall_ms":2662.7758850809187},{"cpu_ms":2554.935999999998,"wall_ms":2578.3711338881403},{"cpu_ms":2465.845999999999,"wall_ms":2481.2435910571367},{"cpu_ms":2532.5540000000046,"wall_ms":2549.9344118870795},{"cpu_ms":2465.2240000000065,"wall_ms":2492.59823304601},{"cpu_ms":2999.190999999996,"wall_ms":3021.6669500805438}],"after_median_cpu_ms":2465.2240000000065}, +{"name":"string_concat_csv","node_exit":0,"before":[{"cpu_ms":46.96800000000678,"wall_ms":53.32074803300202},{"cpu_ms":45.559999999994716,"wall_ms":49.97689090669155},{"cpu_ms":46.97900000000743,"wall_ms":53.949902998283505},{"cpu_ms":47.457999999991785,"wall_ms":53.016678895801306},{"cpu_ms":44.3649999999991,"wall_ms":51.67702701874077},{"cpu_ms":37.67000000000564,"wall_ms":48.979579005390406},{"cpu_ms":47.300000000007,"wall_ms":50.850713858380914},{"cpu_ms":41.15199999999675,"wall_ms":45.537388883531094},{"cpu_ms":45.55899999999724,"wall_ms":50.98606692627072},{"cpu_ms":30.41000000000338,"wall_ms":38.61991781741381},{"cpu_ms":50.210999999990236,"wall_ms":54.56780712120235},{"cpu_ms":52.21300000000895,"wall_ms":54.57607191056013},{"cpu_ms":49.41800000000285,"wall_ms":52.80248005874455},{"cpu_ms":47.673000000003185,"wall_ms":54.15411409921944},{"cpu_ms":40.24400000000128,"wall_ms":43.6150380410254}],"before_median_cpu_ms":46.96800000000678,"after":[{"cpu_ms":27.289999999993597,"wall_ms":36.659847013652325},{"cpu_ms":40.22799999999904,"wall_ms":45.61190796084702},{"cpu_ms":49.00100000000407,"wall_ms":56.11340398900211},{"cpu_ms":43.25400000000457,"wall_ms":49.6870600618422},{"cpu_ms":29.984999999996376,"wall_ms":38.27777900733054},{"cpu_ms":30.585000000002083,"wall_ms":37.430036114528775},{"cpu_ms":47.72999999998717,"wall_ms":51.1541401501745},{"cpu_ms":46.14899999999977,"wall_ms":51.651219138875604},{"cpu_ms":45.20800000000236,"wall_ms":51.648152992129326},{"cpu_ms":41.51199999999733,"wall_ms":53.774524945765734},{"cpu_ms":48.290000000008604,"wall_ms":53.80369909107685},{"cpu_ms":49.39099999999996,"wall_ms":54.19923784211278},{"cpu_ms":46.788999999989755,"wall_ms":53.13458992168307},{"cpu_ms":48.44199999999432,"wall_ms":53.916721139103174},{"cpu_ms":48.44700000001012,"wall_ms":54.59142103791237}],"after_median_cpu_ms":46.14899999999977}, +{"name":"string_split_map_join","node_exit":0,"before":[{"cpu_ms":223.18500000000085,"wall_ms":229.5707748271525},{"cpu_ms":231.53299999999888,"wall_ms":236.95261403918266},{"cpu_ms":236.79900000000487,"wall_ms":241.16583401337266},{"cpu_ms":248.03799999999399,"wall_ms":256.3595939427614},{"cpu_ms":238.73399999999378,"wall_ms":243.12359001487494},{"cpu_ms":190.18800000000624,"wall_ms":201.47138787433505},{"cpu_ms":253.87500000002206,"wall_ms":259.2219200450927},{"cpu_ms":189.1639999999768,"wall_ms":196.45991805009544},{"cpu_ms":190.06100000001425,"wall_ms":196.39995484612882},{"cpu_ms":166.893999999985,"wall_ms":174.2706501390785},{"cpu_ms":215.76999999999202,"wall_ms":223.62950793467462},{"cpu_ms":194.7759999999903,"wall_ms":197.10028381086886},{"cpu_ms":166.02799999998297,"wall_ms":173.33244788460433},{"cpu_ms":182.47900000000072,"wall_ms":184.81243215501308},{"cpu_ms":211.54099999998266,"wall_ms":217.86776604130864}],"before_median_cpu_ms":211.54099999998266,"after":[{"cpu_ms":183.15699999999424,"wall_ms":189.45118482224643},{"cpu_ms":185.81600000000265,"wall_ms":191.21075212024152},{"cpu_ms":181.1529999999948,"wall_ms":187.53384402953088},{"cpu_ms":209.01500000000794,"wall_ms":216.35379688814282},{"cpu_ms":225.3910000000019,"wall_ms":227.77227591723204},{"cpu_ms":298.3329999999853,"wall_ms":304.8758569639176},{"cpu_ms":198.98299999999836,"wall_ms":205.33526595681906},{"cpu_ms":189.32300000000168,"wall_ms":197.8422999382019},{"cpu_ms":190.70600000000582,"wall_ms":196.01806183345616},{"cpu_ms":218.24000000000865,"wall_ms":223.57864305377007},{"cpu_ms":180.79099999999926,"wall_ms":188.17538302391768},{"cpu_ms":204.1510000000244,"wall_ms":209.47068999521434},{"cpu_ms":229.50299999999402,"wall_ms":236.8424879387021},{"cpu_ms":179.50100000001612,"wall_ms":187.88750492967665},{"cpu_ms":204.6980000000076,"wall_ms":211.05968882329762}],"after_median_cpu_ms":198.98299999999836}, +{"name":"string_template_interp","node_exit":0,"before":[{"cpu_ms":72.75300000000584,"wall_ms":82.15647004544735},{"cpu_ms":62.739999999990914,"wall_ms":74.1191681008786},{"cpu_ms":72.34099999999444,"wall_ms":81.66525303386152},{"cpu_ms":57.751999999993586,"wall_ms":62.14772700332105},{"cpu_ms":60.45299999999543,"wall_ms":69.74547798745334},{"cpu_ms":52.93100000000095,"wall_ms":57.22254700958729},{"cpu_ms":45.376000000004524,"wall_ms":53.63412294536829},{"cpu_ms":66.3940000000025,"wall_ms":72.74551200680435},{"cpu_ms":57.366000000001804,"wall_ms":60.75973296537995},{"cpu_ms":65.67799999999124,"wall_ms":71.93536893464625},{"cpu_ms":53.336000000001604,"wall_ms":58.671873062849045},{"cpu_ms":63.26699999999619,"wall_ms":67.61796399950981},{"cpu_ms":75.44500000000198,"wall_ms":81.76145306788385},{"cpu_ms":69.74700000000666,"wall_ms":78.5289250779897},{"cpu_ms":63.24699999998984,"wall_ms":71.00911904126406}],"before_median_cpu_ms":63.24699999998984,"after":[{"cpu_ms":53.08299999998667,"wall_ms":65.04497793503106},{"cpu_ms":84.2930000000024,"wall_ms":86.71077713370323},{"cpu_ms":51.91200000001572,"wall_ms":61.29864999093115},{"cpu_ms":51.3650000000041,"wall_ms":59.65281603857875},{"cpu_ms":52.580000000006066,"wall_ms":62.825812958180904},{"cpu_ms":46.803999999980306,"wall_ms":56.090121157467365},{"cpu_ms":52.55999999999972,"wall_ms":54.933039005845785},{"cpu_ms":70.69799999999304,"wall_ms":75.01959893852472},{"cpu_ms":73.49300000001335,"wall_ms":82.8652149066329},{"cpu_ms":47.408000000018546,"wall_ms":52.650787169113755},{"cpu_ms":46.58599999999069,"wall_ms":53.84099995717406},{"cpu_ms":64.1469999999913,"wall_ms":70.47291682101786},{"cpu_ms":53.34600000000478,"wall_ms":56.67302990332246},{"cpu_ms":77.55699999998455,"wall_ms":89.90448294207454},{"cpu_ms":79.716000000019,"wall_ms":88.0797051358968}],"after_median_cpu_ms":53.08299999998667} +] diff --git a/benchmarks/regexp-construction/pre-hint-probe-samples.json b/benchmarks/regexp-construction/pre-hint-probe-samples.json new file mode 100644 index 0000000000..1c3c348e94 --- /dev/null +++ b/benchmarks/regexp-construction/pre-hint-probe-samples.json @@ -0,0 +1,8 @@ +[ +{"mode":"all","scale":1,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"construct":631.214,"test-ascii":2.7,"test-emoji":4.047,"stripAnsi":14.922,"stripAnsi-match":31.871,"ignorable":2.125,"segment":52.228,"eastAsianWidth":0.968,"stringWidth":34235.552},{"construct":604.442,"test-ascii":2.311,"test-emoji":3.476,"stripAnsi":12.349,"stripAnsi-match":24.487,"ignorable":1.633,"segment":46.921,"eastAsianWidth":0.751,"stringWidth":37428.801},{"construct":638.877,"test-ascii":2.445,"test-emoji":3.789,"stripAnsi":14.259,"stripAnsi-match":29.391,"ignorable":1.924,"segment":51.497,"eastAsianWidth":0.909,"stringWidth":38307.057},{"construct":504.58,"test-ascii":1.392,"test-emoji":2.216,"stripAnsi":12.059,"stripAnsi-match":27.042,"ignorable":1.844,"segment":41.578,"eastAsianWidth":0.489,"stringWidth":38002.282},{"construct":636.571,"test-ascii":2.47,"test-emoji":3.951,"stripAnsi":13.679,"stripAnsi-match":28.906,"ignorable":1.976,"segment":51.236,"eastAsianWidth":0.86,"stringWidth":38543.309},{"construct":600.414,"test-ascii":2.334,"test-emoji":3.619,"stripAnsi":14.211,"stripAnsi-match":27.225,"ignorable":1.754,"segment":45.948,"eastAsianWidth":0.794,"stringWidth":43239.188},{"construct":803.543,"test-ascii":3.955,"test-emoji":3.563,"stripAnsi":14.405,"stripAnsi-match":27.729,"ignorable":2.129,"segment":52.5,"eastAsianWidth":0.909,"stringWidth":45661.238},{"construct":557.195,"test-ascii":2.393,"test-emoji":3.591,"stripAnsi":13.575,"stripAnsi-match":26.859,"ignorable":1.792,"segment":48.441,"eastAsianWidth":0.847,"stringWidth":41974.398},{"construct":595.651,"test-ascii":2.37,"test-emoji":3.6,"stripAnsi":13.312,"stripAnsi-match":20.849,"ignorable":1.115,"segment":28.735,"eastAsianWidth":0.466,"stringWidth":44360.217},{"construct":617.387,"test-ascii":2.562,"test-emoji":3.913,"stripAnsi":13.608,"stripAnsi-match":25.817,"ignorable":1.956,"segment":48.173,"eastAsianWidth":0.773,"stringWidth":43097.382},{"construct":604.751,"test-ascii":2.422,"test-emoji":3.598,"stripAnsi":13.745,"stripAnsi-match":28.555,"ignorable":1.883,"segment":47.931,"eastAsianWidth":0.844,"stringWidth":42510.98},{"construct":571.409,"test-ascii":2.185,"test-emoji":3.395,"stripAnsi":12.717,"stripAnsi-match":26.796,"ignorable":1.891,"segment":47.69,"eastAsianWidth":0.739,"stringWidth":42653.535}],"after":[{"construct":4.789,"test-ascii":0.869,"test-emoji":2.003,"stripAnsi":0.582,"stripAnsi-match":4.149,"ignorable":0.339,"segment":47.867,"eastAsianWidth":0.784,"stringWidth":377.344},{"construct":4.852,"test-ascii":0.969,"test-emoji":2.366,"stripAnsi":0.678,"stripAnsi-match":4.901,"ignorable":0.399,"segment":53.062,"eastAsianWidth":0.892,"stringWidth":389.28},{"construct":4.74,"test-ascii":0.877,"test-emoji":2.1,"stripAnsi":0.571,"stripAnsi-match":4.35,"ignorable":0.377,"segment":50.012,"eastAsianWidth":0.816,"stringWidth":380.464},{"construct":4.909,"test-ascii":0.859,"test-emoji":2.104,"stripAnsi":0.516,"stripAnsi-match":4.267,"ignorable":0.354,"segment":48.668,"eastAsianWidth":0.805,"stringWidth":375.752},{"construct":4.644,"test-ascii":1.014,"test-emoji":2.118,"stripAnsi":0.631,"stripAnsi-match":4.578,"ignorable":0.391,"segment":52.786,"eastAsianWidth":0.828,"stringWidth":407.145},{"construct":4.781,"test-ascii":0.906,"test-emoji":2.221,"stripAnsi":0.625,"stripAnsi-match":4.395,"ignorable":0.352,"segment":51.37,"eastAsianWidth":0.995,"stringWidth":381.937},{"construct":4.543,"test-ascii":0.859,"test-emoji":2.0,"stripAnsi":0.578,"stripAnsi-match":4.234,"ignorable":0.375,"segment":45.043,"eastAsianWidth":0.799,"stringWidth":353.406},{"construct":4.415,"test-ascii":0.794,"test-emoji":2.055,"stripAnsi":0.523,"stripAnsi-match":4.213,"ignorable":0.342,"segment":48.165,"eastAsianWidth":0.778,"stringWidth":376.271},{"construct":3.969,"test-ascii":0.839,"test-emoji":2.012,"stripAnsi":0.595,"stripAnsi-match":4.187,"ignorable":0.344,"segment":55.737,"eastAsianWidth":0.715,"stringWidth":370.521},{"construct":4.818,"test-ascii":0.948,"test-emoji":2.194,"stripAnsi":0.619,"stripAnsi-match":4.74,"ignorable":0.382,"segment":51.588,"eastAsianWidth":0.864,"stringWidth":339.054},{"construct":5.252,"test-ascii":0.98,"test-emoji":2.206,"stripAnsi":0.632,"stripAnsi-match":4.521,"ignorable":0.39,"segment":50.797,"eastAsianWidth":0.808,"stringWidth":392.39},{"construct":4.655,"test-ascii":0.878,"test-emoji":2.045,"stripAnsi":0.563,"stripAnsi-match":4.444,"ignorable":0.335,"segment":45.789,"eastAsianWidth":0.724,"stringWidth":351.937}],"bun":[{"construct":3.451,"test-ascii":1.739,"test-emoji":0.644,"stripAnsi":0.828,"stripAnsi-match":0.578,"ignorable":0.381,"segment":18.698,"eastAsianWidth":1.777,"stringWidth":50.145},{"construct":3.541,"test-ascii":0.677,"test-emoji":0.729,"stripAnsi":0.583,"stripAnsi-match":0.617,"ignorable":0.694,"segment":20.776,"eastAsianWidth":1.26,"stringWidth":54.435},{"construct":3.617,"test-ascii":0.723,"test-emoji":0.6,"stripAnsi":0.754,"stripAnsi-match":0.423,"ignorable":0.294,"segment":22.928,"eastAsianWidth":1.042,"stringWidth":59.676},{"construct":3.74,"test-ascii":0.683,"test-emoji":0.71,"stripAnsi":0.561,"stripAnsi-match":0.565,"ignorable":0.292,"segment":17.093,"eastAsianWidth":0.961,"stringWidth":49.901},{"construct":3.687,"test-ascii":0.766,"test-emoji":0.653,"stripAnsi":0.674,"stripAnsi-match":0.784,"ignorable":0.306,"segment":24.897,"eastAsianWidth":1.28,"stringWidth":57.645},{"construct":3.525,"test-ascii":0.824,"test-emoji":0.553,"stripAnsi":0.79,"stripAnsi-match":0.633,"ignorable":0.305,"segment":16.348,"eastAsianWidth":1.31,"stringWidth":46.423},{"construct":18.42,"test-ascii":0.716,"test-emoji":0.545,"stripAnsi":0.857,"stripAnsi-match":0.411,"ignorable":0.274,"segment":21.078,"eastAsianWidth":1.243,"stringWidth":58.85},{"construct":3.239,"test-ascii":0.642,"test-emoji":0.569,"stripAnsi":0.763,"stripAnsi-match":0.431,"ignorable":0.3,"segment":12.052,"eastAsianWidth":0.982,"stringWidth":49.268},{"construct":3.48,"test-ascii":0.665,"test-emoji":0.487,"stripAnsi":0.555,"stripAnsi-match":0.592,"ignorable":0.288,"segment":16.329,"eastAsianWidth":1.017,"stringWidth":53.034},{"construct":3.517,"test-ascii":2.807,"test-emoji":0.669,"stripAnsi":0.781,"stripAnsi-match":0.81,"ignorable":0.517,"segment":22.35,"eastAsianWidth":1.585,"stringWidth":56.14},{"construct":19.072,"test-ascii":2.428,"test-emoji":0.596,"stripAnsi":0.571,"stripAnsi-match":0.632,"ignorable":0.671,"segment":19.827,"eastAsianWidth":1.242,"stringWidth":56.571},{"construct":3.889,"test-ascii":0.712,"test-emoji":0.807,"stripAnsi":0.604,"stripAnsi-match":0.445,"ignorable":0.308,"segment":19.851,"eastAsianWidth":1.632,"stringWidth":54.277}]},"output":["constructed g 14116","checksum 162430"],"medians_us":{"before":{"construct":604.5965,"test-ascii":2.4074999999999998,"test-emoji":3.599,"stripAnsi":13.6435,"stripAnsi-match":27.1335,"ignorable":1.887,"segment":48.052,"eastAsianWidth":0.819,"stringWidth":42242.689},"after":{"construct":4.7605,"test-ascii":0.8775,"test-emoji":2.1020000000000003,"stripAnsi":0.5885,"stripAnsi-match":4.3725,"ignorable":0.3645,"segment":50.4045,"eastAsianWidth":0.8065,"stringWidth":376.8075},"bun":{"construct":3.5789999999999997,"test-ascii":0.7195,"test-emoji":0.622,"stripAnsi":0.714,"stripAnsi-match":0.585,"ignorable":0.3055,"segment":19.839,"eastAsianWidth":1.2515,"stringWidth":54.356}}}, +{"mode":"construct","scale":10,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"construct":628.117},{"construct":602.596},{"construct":594.737},{"construct":606.978},{"construct":590.329},{"construct":580.782},{"construct":594.092},{"construct":606.755},{"construct":613.481},{"construct":642.38},{"construct":631.033},{"construct":597.133}],"after":[{"construct":1.892},{"construct":0.657},{"construct":0.631},{"construct":0.584},{"construct":0.578},{"construct":0.664},{"construct":0.663},{"construct":0.814},{"construct":0.643},{"construct":0.661},{"construct":0.678},{"construct":0.644}],"bun":[{"construct":0.722},{"construct":0.635},{"construct":0.751},{"construct":0.683},{"construct":0.666},{"construct":0.67},{"construct":0.691},{"construct":0.742},{"construct":0.715},{"construct":0.582},{"construct":0.793},{"construct":0.62}]},"output":["constructed g 14116","checksum 0"],"medians_us":{"before":{"construct":604.6755},"after":{"construct":0.659},"bun":{"construct":0.687}}}, +{"mode":"test-ascii","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"test-ascii":2.451},{"test-ascii":2.527},{"test-ascii":2.388},{"test-ascii":2.371},{"test-ascii":2.457},{"test-ascii":2.516},{"test-ascii":2.657},{"test-ascii":2.565},{"test-ascii":2.328},{"test-ascii":2.288},{"test-ascii":2.51},{"test-ascii":2.384}],"after":[{"test-ascii":0.933},{"test-ascii":1.079},{"test-ascii":0.899},{"test-ascii":0.87},{"test-ascii":0.863},{"test-ascii":0.823},{"test-ascii":0.888},{"test-ascii":0.829},{"test-ascii":0.956},{"test-ascii":0.851},{"test-ascii":0.851},{"test-ascii":0.955}],"bun":[{"test-ascii":0.066},{"test-ascii":0.058},{"test-ascii":0.057},{"test-ascii":0.056},{"test-ascii":0.05},{"test-ascii":0.074},{"test-ascii":0.052},{"test-ascii":0.05},{"test-ascii":0.072},{"test-ascii":0.057},{"test-ascii":0.057},{"test-ascii":0.061}]},"output":["checksum 0"],"medians_us":{"before":{"test-ascii":2.4539999999999997},"after":{"test-ascii":0.879},"bun":{"test-ascii":0.057}}}, +{"mode":"test-emoji","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"test-emoji":3.538},{"test-emoji":3.794},{"test-emoji":3.605},{"test-emoji":3.792},{"test-emoji":3.803},{"test-emoji":3.777},{"test-emoji":3.813},{"test-emoji":3.663},{"test-emoji":3.457},{"test-emoji":3.755},{"test-emoji":3.606},{"test-emoji":3.641}],"after":[{"test-emoji":2.038},{"test-emoji":2.125},{"test-emoji":2.098},{"test-emoji":2.161},{"test-emoji":2.064},{"test-emoji":2.218},{"test-emoji":2.238},{"test-emoji":2.064},{"test-emoji":2.207},{"test-emoji":2.242},{"test-emoji":2.173},{"test-emoji":2.024}],"bun":[{"test-emoji":0.062},{"test-emoji":0.067},{"test-emoji":0.077},{"test-emoji":0.065},{"test-emoji":0.051},{"test-emoji":0.069},{"test-emoji":0.085},{"test-emoji":0.085},{"test-emoji":0.071},{"test-emoji":0.072},{"test-emoji":0.063},{"test-emoji":0.078}]},"output":["checksum 100000"],"medians_us":{"before":{"test-emoji":3.7089999999999996},"after":{"test-emoji":2.143},"bun":{"test-emoji":0.07}}}, +{"mode":"stripAnsi","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"stripAnsi":13.929},{"stripAnsi":14.091},{"stripAnsi":15.314},{"stripAnsi":14.99},{"stripAnsi":14.787},{"stripAnsi":13.402},{"stripAnsi":14.609},{"stripAnsi":15.021},{"stripAnsi":15.453},{"stripAnsi":14.675},{"stripAnsi":15.458},{"stripAnsi":17.777}],"after":[{"stripAnsi":0.633},{"stripAnsi":0.599},{"stripAnsi":0.86},{"stripAnsi":0.566},{"stripAnsi":0.601},{"stripAnsi":0.861},{"stripAnsi":0.632},{"stripAnsi":0.615},{"stripAnsi":0.626},{"stripAnsi":0.627},{"stripAnsi":0.684},{"stripAnsi":0.605}],"bun":[{"stripAnsi":0.05},{"stripAnsi":0.089},{"stripAnsi":0.057},{"stripAnsi":0.068},{"stripAnsi":0.091},{"stripAnsi":0.058},{"stripAnsi":0.073},{"stripAnsi":0.085},{"stripAnsi":0.052},{"stripAnsi":0.093},{"stripAnsi":0.055},{"stripAnsi":0.133}]},"output":["checksum 13400000"],"medians_us":{"before":{"stripAnsi":14.8885},"after":{"stripAnsi":0.6265000000000001},"bun":{"stripAnsi":0.07050000000000001}}}, +{"mode":"stripAnsi-match","scale":100,"orders":[["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"],["before","after","bun"],["before","bun","after"],["bun","before","after"],["bun","after","before"],["after","bun","before"],["after","before","bun"]],"samples":{"before":[{"stripAnsi-match":31.12},{"stripAnsi-match":35.927},{"stripAnsi-match":32.578},{"stripAnsi-match":28.974},{"stripAnsi-match":23.754},{"stripAnsi-match":20.219},{"stripAnsi-match":20.292},{"stripAnsi-match":20.528},{"stripAnsi-match":25.818},{"stripAnsi-match":21.584},{"stripAnsi-match":19.553},{"stripAnsi-match":19.741}],"after":[{"stripAnsi-match":5.235},{"stripAnsi-match":5.41},{"stripAnsi-match":4.543},{"stripAnsi-match":5.361},{"stripAnsi-match":3.713},{"stripAnsi-match":3.413},{"stripAnsi-match":3.585},{"stripAnsi-match":3.034},{"stripAnsi-match":2.769},{"stripAnsi-match":3.662},{"stripAnsi-match":3.269},{"stripAnsi-match":3.141}],"bun":[{"stripAnsi-match":0.167},{"stripAnsi-match":0.156},{"stripAnsi-match":0.137},{"stripAnsi-match":0.139},{"stripAnsi-match":0.134},{"stripAnsi-match":0.097},{"stripAnsi-match":0.093},{"stripAnsi-match":0.099},{"stripAnsi-match":0.101},{"stripAnsi-match":0.098},{"stripAnsi-match":0.111},{"stripAnsi-match":0.139}]},"output":["checksum 600000"],"medians_us":{"before":{"stripAnsi-match":22.669},"after":{"stripAnsi-match":3.6235},"bun":{"stripAnsi-match":0.1225}}} +] diff --git a/benchmarks/regexp-construction/prepare.py b/benchmarks/regexp-construction/prepare.py new file mode 100644 index 0000000000..0e70d78414 --- /dev/null +++ b/benchmarks/regexp-construction/prepare.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +"""Resolve the issue #10179 probe against an existing OpenCode Bun install.""" +import os +from pathlib import Path +import sys + +npm = Path(os.environ["OPENCODE_SRC"]) / "node_modules/.bun" +source = Path(__file__).with_name("probe.ts.in").read_text() +target = Path(sys.argv[1]) +target.write_text(source.replace("@NPM@", str(npm))) +print(target) diff --git a/benchmarks/regexp-construction/probe-samples.json b/benchmarks/regexp-construction/probe-samples.json new file mode 100644 index 0000000000..1b11373059 --- /dev/null +++ b/benchmarks/regexp-construction/probe-samples.json @@ -0,0 +1,8 @@ +[ + {"mode":"all","scale":1,"samples":{"before":[{"construct":919.482,"test-ascii":16.864,"test-emoji":17.46,"stripAnsi":19.014,"stripAnsi-match":39.775,"ignorable":1.85,"segment":66.121,"eastAsianWidth":0.91,"stringWidth":51835.012},{"construct":801.939,"test-ascii":14.531,"test-emoji":15.352,"stripAnsi":15.784,"stripAnsi-match":29.512,"ignorable":1.791,"segment":43.914,"eastAsianWidth":0.834,"stringWidth":47926.129},{"construct":1464.296,"test-ascii":11.893,"test-emoji":14.081,"stripAnsi":21.543,"stripAnsi-match":29.997,"ignorable":1.962,"segment":1099.759,"eastAsianWidth":0.805,"stringWidth":51872.395},{"construct":832.316,"test-ascii":10.937,"test-emoji":12.047,"stripAnsi":13.804,"stripAnsi-match":27.152,"ignorable":2.009,"segment":49.243,"eastAsianWidth":0.938,"stringWidth":46752.377},{"construct":2744.332,"test-ascii":11.928,"test-emoji":13.198,"stripAnsi":13.023,"stripAnsi-match":29.906,"ignorable":2.059,"segment":50.742,"eastAsianWidth":0.985,"stringWidth":47947.708},{"construct":861.723,"test-ascii":16.754,"test-emoji":13.487,"stripAnsi":16.448,"stripAnsi-match":30.594,"ignorable":2.113,"segment":49.53,"eastAsianWidth":0.867,"stringWidth":47593.967},{"construct":884.63,"test-ascii":11.637,"test-emoji":13.403,"stripAnsi":14.848,"stripAnsi-match":29.191,"ignorable":1.875,"segment":63.407,"eastAsianWidth":0.781,"stringWidth":47315.556}],"after":[{"construct":4.609,"test-ascii":0.907,"test-emoji":2.088,"stripAnsi":0.625,"stripAnsi-match":4.786,"ignorable":0.336,"segment":63.347,"eastAsianWidth":0.738,"stringWidth":459.889},{"construct":5.312,"test-ascii":0.929,"test-emoji":3.233,"stripAnsi":0.614,"stripAnsi-match":5.169,"ignorable":0.416,"segment":64.026,"eastAsianWidth":0.786,"stringWidth":495.2},{"construct":5.025,"test-ascii":0.988,"test-emoji":1.725,"stripAnsi":0.68,"stripAnsi-match":4.43,"ignorable":0.393,"segment":92.28,"eastAsianWidth":0.9,"stringWidth":484.945},{"construct":21.831,"test-ascii":1.01,"test-emoji":1.739,"stripAnsi":0.655,"stripAnsi-match":5.247,"ignorable":0.399,"segment":115.754,"eastAsianWidth":0.838,"stringWidth":437.959},{"construct":4.93,"test-ascii":0.925,"test-emoji":1.671,"stripAnsi":0.547,"stripAnsi-match":3.075,"ignorable":0.376,"segment":59.411,"eastAsianWidth":0.853,"stringWidth":397.354},{"construct":4.779,"test-ascii":2.365,"test-emoji":1.596,"stripAnsi":0.596,"stripAnsi-match":4.847,"ignorable":0.369,"segment":65.465,"eastAsianWidth":0.791,"stringWidth":467.079},{"construct":4.636,"test-ascii":0.905,"test-emoji":1.692,"stripAnsi":0.579,"stripAnsi-match":4.017,"ignorable":0.345,"segment":56.433,"eastAsianWidth":0.786,"stringWidth":367.474}],"bun":[{"construct":3.432,"test-ascii":1.776,"test-emoji":1.58,"stripAnsi":1.092,"stripAnsi-match":0.963,"ignorable":0.559,"segment":34.638,"eastAsianWidth":1.622,"stringWidth":55.52},{"construct":3.61,"test-ascii":0.711,"test-emoji":0.588,"stripAnsi":0.796,"stripAnsi-match":0.426,"ignorable":0.282,"segment":15.738,"eastAsianWidth":1.406,"stringWidth":44.343},{"construct":4.019,"test-ascii":0.765,"test-emoji":0.548,"stripAnsi":0.625,"stripAnsi-match":0.649,"ignorable":0.343,"segment":19.859,"eastAsianWidth":1.468,"stringWidth":47.418},{"construct":3.938,"test-ascii":0.725,"test-emoji":0.618,"stripAnsi":0.748,"stripAnsi-match":0.751,"ignorable":2.385,"segment":21.913,"eastAsianWidth":1.293,"stringWidth":57.548},{"construct":3.531,"test-ascii":0.737,"test-emoji":0.573,"stripAnsi":0.456,"stripAnsi-match":0.394,"ignorable":0.276,"segment":21.888,"eastAsianWidth":1.233,"stringWidth":53.291},{"construct":3.982,"test-ascii":0.746,"test-emoji":0.632,"stripAnsi":0.69,"stripAnsi-match":0.424,"ignorable":0.318,"segment":30.117,"eastAsianWidth":1.372,"stringWidth":45.931},{"construct":3.879,"test-ascii":0.674,"test-emoji":0.57,"stripAnsi":0.529,"stripAnsi-match":0.404,"ignorable":0.289,"segment":20.488,"eastAsianWidth":0.738,"stringWidth":51.902}]},"output":["constructed g 14116","checksum 162430"],"medians_us":{"before":{"construct":884.63,"test-ascii":11.928,"test-emoji":13.487,"stripAnsi":15.784,"stripAnsi-match":29.906,"ignorable":1.962,"segment":50.742,"eastAsianWidth":0.867,"stringWidth":47926.129},"after":{"construct":4.93,"test-ascii":0.929,"test-emoji":1.725,"stripAnsi":0.614,"stripAnsi-match":4.786,"ignorable":0.376,"segment":64.026,"eastAsianWidth":0.791,"stringWidth":459.889},"bun":{"construct":3.879,"test-ascii":0.737,"test-emoji":0.588,"stripAnsi":0.69,"stripAnsi-match":0.426,"ignorable":0.318,"segment":21.888,"eastAsianWidth":1.372,"stringWidth":51.902}}}, + {"mode":"construct","scale":10,"samples":{"before":[{"construct":729.387},{"construct":810.684},{"construct":734.733},{"construct":739.864},{"construct":778.646},{"construct":744.492},{"construct":769.738}],"after":[{"construct":1.848},{"construct":12.632},{"construct":9.926},{"construct":1.244},{"construct":2.558},{"construct":1.212},{"construct":5.149}],"bun":[{"construct":2.276},{"construct":0.683},{"construct":0.743},{"construct":0.906},{"construct":0.719},{"construct":0.759},{"construct":0.717}]},"output":["constructed g 14116","checksum 0"],"medians_us":{"before":{"construct":744.492},"after":{"construct":2.558},"bun":{"construct":0.743}}}, + {"mode":"test-ascii","scale":100,"samples":{"before":[{"test-ascii":12.187},{"test-ascii":12.194},{"test-ascii":12.434},{"test-ascii":12.19},{"test-ascii":11.062},{"test-ascii":12.314},{"test-ascii":12.042}],"after":[{"test-ascii":0.983},{"test-ascii":0.966},{"test-ascii":0.881},{"test-ascii":1.042},{"test-ascii":0.905},{"test-ascii":1.114},{"test-ascii":0.822}],"bun":[{"test-ascii":0.057},{"test-ascii":0.079},{"test-ascii":0.089},{"test-ascii":0.082},{"test-ascii":0.071},{"test-ascii":0.103},{"test-ascii":0.057}]},"output":["checksum 0"],"medians_us":{"before":{"test-ascii":12.19},"after":{"test-ascii":0.966},"bun":{"test-ascii":0.079}}}, + {"mode":"test-emoji","scale":100,"samples":{"before":[{"test-emoji":12.847},{"test-emoji":13.167},{"test-emoji":13.183},{"test-emoji":12.999},{"test-emoji":13.473},{"test-emoji":12.764},{"test-emoji":13.453}],"after":[{"test-emoji":1.707},{"test-emoji":1.808},{"test-emoji":1.516},{"test-emoji":1.674},{"test-emoji":1.68},{"test-emoji":1.704},{"test-emoji":1.871}],"bun":[{"test-emoji":0.069},{"test-emoji":0.065},{"test-emoji":0.087},{"test-emoji":0.079},{"test-emoji":0.061},{"test-emoji":0.081},{"test-emoji":0.083}]},"output":["checksum 100000"],"medians_us":{"before":{"test-emoji":13.167},"after":{"test-emoji":1.704},"bun":{"test-emoji":0.079}}}, + {"mode":"stripAnsi","scale":100,"samples":{"before":[{"stripAnsi":15.206},{"stripAnsi":14.954},{"stripAnsi":13.483},{"stripAnsi":13.853},{"stripAnsi":15.425},{"stripAnsi":15.214},{"stripAnsi":15.573}],"after":[{"stripAnsi":0.51},{"stripAnsi":0.515},{"stripAnsi":0.527},{"stripAnsi":0.528},{"stripAnsi":0.562},{"stripAnsi":0.641},{"stripAnsi":0.602}],"bun":[{"stripAnsi":0.078},{"stripAnsi":0.1},{"stripAnsi":0.076},{"stripAnsi":0.069},{"stripAnsi":0.054},{"stripAnsi":0.071},{"stripAnsi":0.079}]},"output":["checksum 13400000"],"medians_us":{"before":{"stripAnsi":15.206},"after":{"stripAnsi":0.528},"bun":{"stripAnsi":0.076}}}, + {"mode":"stripAnsi-match","scale":100,"samples":{"before":[{"stripAnsi-match":30.969},{"stripAnsi-match":31.724},{"stripAnsi-match":31.702},{"stripAnsi-match":31.875},{"stripAnsi-match":34.952},{"stripAnsi-match":32.028},{"stripAnsi-match":30.948}],"after":[{"stripAnsi-match":4.084},{"stripAnsi-match":4.504},{"stripAnsi-match":3.789},{"stripAnsi-match":4.02},{"stripAnsi-match":4.636},{"stripAnsi-match":4.622},{"stripAnsi-match":4.886}],"bun":[{"stripAnsi-match":0.196},{"stripAnsi-match":0.136},{"stripAnsi-match":0.131},{"stripAnsi-match":0.153},{"stripAnsi-match":0.214},{"stripAnsi-match":0.151},{"stripAnsi-match":0.193}]},"output":["checksum 600000"],"medians_us":{"before":{"stripAnsi-match":31.724},"after":{"stripAnsi-match":4.504},"bun":{"stripAnsi-match":0.153}}} +] diff --git a/benchmarks/regexp-construction/probe.ts.in b/benchmarks/regexp-construction/probe.ts.in new file mode 100644 index 0000000000..30d4e7f627 --- /dev/null +++ b/benchmarks/regexp-construction/probe.ts.in @@ -0,0 +1,33 @@ +// Issue #10179: original npm inputs. prepare.py resolves the isolated Bun install. +import emojiRegex from "@NPM@/emoji-regex@10.6.0/node_modules/emoji-regex/index.js"; +import stripAnsi from "@NPM@/strip-ansi@7.1.2/node_modules/strip-ansi/index.js"; +import stringWidth from "@NPM@/string-width@7.2.0/node_modules/string-width/index.js"; +import { eastAsianWidth } from "@NPM@/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/index.js"; + +const mode = process.argv[2] || "all"; +const scale = Number(process.argv[3] || "1"); +if (!Number.isSafeInteger(scale) || scale < 1) throw new Error("scale must be a positive integer"); +let sink: any; +let checksum = 0; +function t(name: string, n: number, f: () => void) { + if (mode !== "all" && mode !== name) return; + n *= scale; + const t0 = performance.now(); + for (let i = 0; i < n; i++) f(); + console.log(name, ((performance.now() - t0) * 1000 / n).toFixed(3), "us/iter"); +} +const line = " --log-level log level [string] [choices: \"DEBUG\", \"INFO\"]"; +t("construct", 200, () => { sink = emojiRegex(); }); +const re = emojiRegex(); +t("test-ascii", 2000, () => { checksum += Number(re.test("a")); }); +t("test-emoji", 2000, () => { checksum += Number(re.test("🚀")); }); +t("stripAnsi", 2000, () => { checksum += stripAnsi(line).length; }); +t("stripAnsi-match", 2000, () => { checksum += stripAnsi("\u001b[31mred\u001b[39m").length; }); +const ignorable = /^\p{Default_Ignorable_Code_Point}$/u; +t("ignorable", 2000, () => { checksum += Number(ignorable.test("a")); }); +const segmenter = new Intl.Segmenter(); +t("segment", 200, () => { for (const item of segmenter.segment(line)) checksum += item.segment.length; }); +t("eastAsianWidth", 2000, () => { checksum += eastAsianWidth(97); }); +t("stringWidth", 90, () => { checksum += stringWidth(line); }); +if (sink) console.log("constructed", sink.flags, sink.source.length); +console.log("checksum", checksum); diff --git a/benchmarks/regexp-construction/results.md b/benchmarks/regexp-construction/results.md new file mode 100644 index 0000000000..068b815584 --- /dev/null +++ b/benchmarks/regexp-construction/results.md @@ -0,0 +1,249 @@ +# Issue #10179: measurement and verification record + +This is the historical Perex 0.1.0 lane record, including a binding cache later +removed in favor of upstream program witnesses. See [final-results.md](final-results.md) +for the final Perex 0.1.4 implementation and comparison. + +Linux x86-64 build host, 2026-09-13. Baseline: Perry +`9b911855f8ca877ee439d5ddc416b27d4b7b018f`, Perex 0.1.0. Both compilers +use release builds and matching runtime/stdlib archives. All native compilation, +tests and probes ran on the host through `./remote.sh`; none ran on the Mac. +The oracle is Node 26.5.1 (`.node-version`), and the timing comparison uses Bun +1.3.14 and the actual installed OpenCode v1.18.30 dependencies. + +## Attribution before changing the code + +This checkout has already replaced the old standard/fallback split with Perex. +There was **no construction cache** on the active path. `js_regexp_new`, literal +sites, constructor calls, and `RegExp.prototype.compile` all eagerly compiled +through `perex_construct`. The issue was not a hash miss, eviction, or an +uncached fallback engine. Lookbehind and backreferences use the same engine. + +`perf record -F 499 -g --call-graph dwarf` on the unmodified probe found: + +| Operation | Where sampled CPU went | +|---|---| +| 14,116-character emoji construction | Parser cursor 32.13%, sequence 18.28%, escape parsing 17.83%, class parsing 6.73%, program emission 6.27%; remaining parser routines dominate the rest. No cache lookup or fallback engine. | +| Reused emoji `.test("a")` | `Program::from_words` 66.69%, range validation 8.13%, repeat validation 2.67%: about 77% repeatedly validating the same immutable program. | +| Plain-text `stripAnsi` | Object field reads 6.73%, reflective Get 5.09%, runtime handles 4.95%, field lookup tail 4.60%, exception traps 4.39%, accessor lookup 4.38%, UTF-8 validation 3.82%, RegExp property reads 2.86%, closure dispatch 2.76%, program validation 2.12%. Generic `@@replace`, `exec`, and eight flag reads dominate. This no-match case did not allocate match results. | + +The changes were measured incrementally: construction sharing alone brought +construction to 0.588 µs, but reused ASCII tests still took 9.964 µs; binding +reuse brought tests to 2.262 µs; guarded builtin dispatch and empty replacement +brought tests to 1.115 µs and plain `stripAnsi` to 0.568 µs. These exploratory +samples used `all 10` and were collected at different host loads, so they are +attribution evidence, not the controlled final comparison. + +That revision kept eager syntax checking, two bounded LRU caches (512 +entries/32 MiB each), per-object state, and operation-owned scratch. Source +identity hits avoid hashing the pattern; independently allocated equal sources +use a hash and exact comparison. Perex bindings retain their original immutable +owner and are rewritten on GC movement. Cache scanners register on first use. + +After optimization, construction samples move to runtime handles (17.49%), +constructor setup (12.84%), the JS factory closure (11.26%), publication and GC +barriers. The remaining SipHash work (3.45%) hashes short identity/flag keys; +pattern bytes are only hashed on source-identity misses. Reused tests move to +host search setup (8.48%), native method dispatch (7.12%), VM matching (4.75%), +and primitive dispatch (4.47%). Plain replacement moves to host search setup +(10.64%), VM matching (8.13%), the String replacement boundary (6.68%), subject +binding (6.39%), and exception traps (5.94%). It returns the original string on +no match and uses spans rather than exec arrays when deleting matches. + +## Final probe comparison + +Seven alternating rounds, pinned to CPU 15, with identical inputs, iteration +counts, and checked checksums. Values are median wall microseconds per operation. +The host is shared, so raw samples are included in [probe-samples.json](probe-samples.json). +The original-count `all 1` run uses 200 constructions, 2,000 tests/replacements, +200 segmentations, and 90 string-width calls; checksum 162430. + +| Operation | Perry before | Perry after | Bun 1.3.14 | +|---|---:|---:|---:| +| emoji construction | 884.630 | 4.930 | 3.879 | +| reused emoji test, ASCII | 11.928 | 0.929 | 0.737 | +| reused emoji test, rocket | 13.487 | 1.725 | 0.588 | +| stripAnsi, plain help line | 15.784 | 0.614 | 0.690 | +| stripAnsi, ANSI-colored text | 29.906 | 4.786 | 0.426 | +| Default_Ignorable test | 1.962 | 0.376 | 0.318 | +| Intl.Segmenter | 50.742 | 64.026 | 21.888 | +| eastAsianWidth | 0.867 | 0.791 | 1.372 | +| stringWidth | 47926.129 | 459.889 | 51.902 | + +Construction is 179× faster, plain stripAnsi 26× faster, and stringWidth 104× +faster in this run. Construction, both reused tests, and plain stripAnsi meet +the requested 20/2/1 µs targets. ANSI text with matches still takes 4.786 µs: +decoding, repeated searches, and output construction remain. The mixed-run +Segmenter result is slower; an isolated follow-up is reported below. + +Separate longer cases amortize initial work and Bun warmup: + +| Operation | Iterations | Perry before | Perry after | Bun 1.3.14 | +|---|---:|---:|---:|---:| +| emoji construction | 2000 | 744.492 | 2.558 | 0.743 | +| reused emoji test, ASCII | 200000 | 12.190 | 0.966 | 0.079 | +| reused emoji test, rocket | 200000 | 13.167 | 1.704 | 0.079 | +| stripAnsi, plain help line | 200000 | 15.206 | 0.528 | 0.076 | +| stripAnsi, ANSI-colored text | 200000 | 31.724 | 4.504 | 0.153 | + +The warm results retain a substantial Bun gap despite meeting the targets. +These are timed-loop results, not a complete CLI startup measurement. + +## Existing benchmark comparison + +All 12 existing app-pattern kernels match pinned Node output under both builds. +The following 15-round medians use child user CPU milliseconds, one warmup, +alternating execution order, CPU 15, and one fixed executable path for both +builds. Raw samples: [app-samples.json](app-samples.json). + +| Existing kernel | Before (ms) | After (ms) | Change | +|---|---:|---:|---:| +| batch | 49.759 | 51.183 | 2.86% | +| buffer_transcode | 76.919 | 75.305 | -2.10% | +| date_format_parse | 46.199 | 46.363 | 0.35% | +| json_parse_1mb | 100.600 | 98.845 | -1.74% | +| json_stringify_1mb | 136.806 | 138.325 | 1.11% | +| map_1m | 327.807 | 321.354 | -1.97% | +| object_deep_clone | 27.981 | 28.352 | 1.33% | +| promise_all_chains | 164.130 | 171.369 | 4.41% | +| regex_replace | 6153.973 | 5354.053 | -13.00% | +| string_concat_csv | 42.662 | 43.877 | 2.85% | +| string_split_map_join | 308.978 | 304.255 | -1.53% | +| string_template_interp | 78.797 | 77.954 | -1.07% | + +The regex replacement kernel improves by 13.0%. Small positive deltas require +controls on this busy host; they are not silently rounded to zero. An initial +101-round JSON comparison appeared about 1.7% slower, but an A/A comparison +using byte-identical binaries reproduced 1.7% (SHA-256 +`59adac3c48986eff98f1c2a4fa507285ab793b487da0d34375f547e607ed770b`). +The harness now copies each build to one fixed pathname/inode outside the +timed window, removing argv[0]/execPath as a variable. Under this harness the +101-round JSON A/A medians are 132.288/133.561 ms (+0.96%), while A/B is +128.572/129.423 ms (+0.66%). This does not establish a JSON regression. +Additional controls are recorded below and in [controls-samples.json](controls-samples.json). + +The 101-round controls for the other initially slower kernels give: + +| Kernel | A/A before / after (ms) | A/A change | A/B before / after (ms) | A/B change | +|---|---:|---:|---:|---:| +| batch | 27.035 / 27.964 | 3.44% | 33.977 / 31.231 | -8.08% | +| promise_all_chains | 122.030 / 124.991 | 2.43% | 126.570 / 128.014 | 1.14% | +| string_concat_csv | 46.047 / 44.907 | -2.48% | 43.790 / 43.530 | -0.59% | + +The isolated `segment 100` follow-up (20,000 iterations, seven rounds, +matching checksum 1340000) measures 44.991 µs before, 43.909 µs after, and +4.773 µs in Bun. The slower mixed-run Segmenter observation does not reproduce +in isolation. These controls establish no repeatable slowdown in the kernels +checked; they cannot certify zero slowdown for every workload on a shared host. + +## Reproduction commands + +Each command below ran on the Linux host, invoked from the Mac as +`GIT_DIR=/tmp/perry-regexp-lane.git ./remote.sh ''`. The isolated Git +directory is a lane workaround for read-only parent worktree metadata. + +```sh +cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static +export PERRY_RUNTIME_DIR="$CARGO_TARGET_DIR/release" +python3 benchmarks/regexp-construction/prepare.py /tmp/regexp-probe.ts +"$CARGO_TARGET_DIR/release/perry" compile /tmp/regexp-probe.ts \ + -o /tmp/regexp-probe-after --no-auto-optimize --debug-symbols +/root/claude-opencode/bun/bin/bun /tmp/regexp-probe.ts all 1 +PERRY_REGEX_DIAG=1 /tmp/regexp-probe-after all 1 +perf record -q -F 499 -g --call-graph dwarf -o /tmp/regexp-construct-before.perf \ + /tmp/regexp-probe-before-symbols construct 100 +perf report --stdio --no-children -g none -i /tmp/regexp-construct-before.perf \ + --percent-limit 1 -F overhead,symbol +taskset -c 15 python3 benchmarks/regexp-construction/measure.py \ + --before /tmp/regexp-probe-before --after /tmp/regexp-probe-lazy \ + --source /tmp/regexp-probe.ts --bun /root/claude-opencode/bun/bin/bun \ + --output /tmp/regexp-probe-final.json --runs 7 +taskset -c 15 python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-baseline-libs --after /tmp/regexp-lazy-libs \ + --output /tmp/regexp-app-fixed --runs 15 +for kernel in json_stringify_1mb batch promise_all_chains string_concat_csv; do + taskset -c 15 python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-baseline-libs --after /tmp/regexp-baseline-libs \ + --output /tmp/regexp-aa-fixed-$kernel --runs 101 --filter $kernel + taskset -c 15 python3 benchmarks/regexp-construction/compare.py \ + --before /tmp/regexp-baseline-libs --after /tmp/regexp-lazy-libs \ + --output /tmp/regexp-app-fixed-$kernel --runs 101 --filter $kernel +done +taskset -c 15 python3 benchmarks/regexp-construction/measure.py \ + --before /tmp/regexp-probe-before --after /tmp/regexp-probe-lazy \ + --source /tmp/regexp-probe.ts --bun /root/claude-opencode/bun/bin/bun \ + --output /tmp/regexp-segment-final.json --runs 7 --case segment:100 +``` + +The same perf commands were run for `test-ascii` and `stripAnsi`. Final samples +used `-F 999`, `--percent-limit 2`, and scale 10000. Timings exclude diagnostics +and perf. The diagnostics snapshot showed three compiled programs, 200 identity +hits, zero content hits, and only 14,277 bytes hashed after 203 constructions; +the emoji program was validated once, with repeated canonical test dispatches. + +## Verification + +```sh +RUST_TEST_THREADS=1 cargo test -p perry-runtime --lib regex +RUST_TEST_THREADS=1 cargo test -p perry-runtime --lib +cargo fmt --all -- --check +./scripts/check_file_size.sh +python3 scripts/check_test_registration.py +python3 scripts/gc_runtime_root_holders.py +./scripts/pre-tag-check.sh --quick +export PATH=/tmp/regexp-oracle/node-v26.5.1-linux-x64/bin:$PATH +PERRY_RUNTIME_DIR=/tmp/regexp-lazy-libs /tmp/regexp-lazy-libs/perry compile \ + test-files/test_gap_10179_regexp_cache.ts --no-auto-optimize \ + -o /tmp/regexp-parity-final +node --experimental-strip-types test-files/test_gap_10179_regexp_cache.ts \ + > /tmp/regexp-parity-final-node.txt +/tmp/regexp-parity-final > /tmp/regexp-parity-final-perry.txt +diff -u /tmp/regexp-parity-final-node.txt /tmp/regexp-parity-final-perry.txt +PERRY_SKIP_BUILD=1 PERRY_BIN=/tmp/regexp-final-libs/perry \ + PERRY_RUNTIME_DIR=/tmp/regexp-final-libs ./run_parity_tests.sh --filter regex +grep -rn regex .github/workflows/test.yml +PERRY_RUNTIME_DIR=/tmp/regexp-final-libs python3 scripts/test262_subset.py \ + --root /tmp/regexp-test262 --dir built-ins/RegExp annexB/built-ins/RegExp \ + --all-features --jobs 4 --perry-bin /tmp/regexp-final-libs/perry \ + --report /tmp/regexp-test262-after.json --sample-cap 100000 +``` + +The final regex unit slice passes 102 tests. The full runtime lib run reports +3,686 passed, one failed, and four ignored. Formatting, file size, test +registration, and GC root inventory pass. The full quick gate additionally +checks GC stores and address classifications; its baseline failure is below. + +The workflow contains no dedicated regex/Test262 job; the committed Test262 +radar was run over both RegExp subtrees, pinned at +`4249661388e5d3f92a85186213da140a6481490f`. Baseline and candidate both report +1,786 passes, 129 runtime failures, zero output differences, zero compile +failures, and seven skips (1,915 judged, 93.3%). The complete sets of failing +`(test, bucket)` pairs are identical. + +The full Test262 run preceded the last registration-only refinement (register +cache scanners on first use). Matching code is identical; the final full +runtime/GC tests and standalone Node fixture exercise the final registration. +`/tmp/regexp-lazy-libs` and `/tmp/regexp-probe-lazy` are the final snapshots; +`/tmp/regexp-final-libs` is the earlier snapshot used by the full parity radar. + +The regex parity runner reports 24 matches, no runtime differences or crashes, +and one HTTP-extension compile failure. Its automatic extension rebuild used a +different sync-commit build ID from the preserved compiler; the linker correctly +rejected it. The standalone #10179 fixture also matches Node byte for byte, +with 48 matching output lines, covering source, flags, independent lastIndex, +sticky/global behavior, compile, +invalid patterns, read-only lastIndex, prototype/own overrides, and Unicode. + +The full runtime suite's sole failure is +`native_stack::tests::stack_top_respects_custom_thread_stack_sizes`. It also +fails in an untouched baseline worktree with +`RUST_TEST_THREADS=1 cargo test --manifest-path /tmp/regexp-baseline-tree/Cargo.toml -p perry-runtime --lib native_stack` +(7 pass, 1 fail). Disabling glibc's stack cache did not resolve it. +`pre-tag-check.sh --quick` passes every check except published benchmark +freshness; that exact failure also reproduces on the untouched baseline. + +This is a standalone change against the lane's pinned base. Open PR #10183 +also changes cross-call binding using Perex 0.1.3 and overlaps this area; merge +order needs review. These measurements cover the real dependency probe, not a +newly rebuilt full OpenCode executable, so they do not establish its final +`--help` startup time. diff --git a/changelog.d/10066-startup-empty-checkpoint.md b/changelog.d/10066-startup-empty-checkpoint.md new file mode 100644 index 0000000000..26f07f720f --- /dev/null +++ b/changelog.d/10066-startup-empty-checkpoint.md @@ -0,0 +1,8 @@ +### Runtime + +- Avoid callback-phase and exception-trap setup at empty entry event-loop + checkpoints. Preserve GC progress and event-loop timing, and retain the full + pump for queued work, stdin, timers, rejections, and native completions. +- Resume the event loop when a beforeExit listener schedules more asynchronous + work, emitting beforeExit again after that work drains. Promise/nextTick work + alone does not create an extra beforeExit event. diff --git a/changelog.d/10066-startup-lazy-class-parents.md b/changelog.d/10066-startup-lazy-class-parents.md new file mode 100644 index 0000000000..0099b11fe8 --- /dev/null +++ b/changelog.d/10066-startup-lazy-class-parents.md @@ -0,0 +1,4 @@ +Defer each application image's 256 KiB dense class-parent table until its first +representable inheritance edge is registered. Empty programs and images without +inheritance avoid the allocation. Parent lookups, publication ordering, shared +worker images, and isolated application images retain their existing behavior. diff --git a/changelog.d/10066-startup-memory-profile.md b/changelog.d/10066-startup-memory-profile.md new file mode 100644 index 0000000000..8ccc16eb55 --- /dev/null +++ b/changelog.d/10066-startup-memory-profile.md @@ -0,0 +1,5 @@ +### Runtime + +- Add an opt-in `PERRY_MEMORY_PROFILE=small` policy for Linux executables using + mimalloc. Configure THP before startup allocations while preserving explicit + allocator environment settings; document throughput and embedding tradeoffs. diff --git a/changelog.d/10066-startup-prebuilt-core.md b/changelog.d/10066-startup-prebuilt-core.md new file mode 100644 index 0000000000..29dfff954d --- /dev/null +++ b/changelog.d/10066-startup-prebuilt-core.md @@ -0,0 +1,8 @@ +Add a prebuilt core runtime for source-free Unix installs. The compiler selects +it only when the existing feature analysis requires its supported runtime-only +subset; optional engines, native modules, dynamic evaluation, and unavailable +core archives retain their existing fallback. The profile preserves unwinding +and the allocator policy. It is intended to reduce linked binary size; startup +and RSS effects are measured separately. +Global-object access retains the full runtime because a runtime property key can +reach optional constructors without their names appearing in source. diff --git a/changelog.d/10066-startup-tiny-program.md b/changelog.d/10066-startup-tiny-program.md new file mode 100644 index 0000000000..0df598f6b8 --- /dev/null +++ b/changelog.d/10066-startup-tiny-program.md @@ -0,0 +1,3 @@ +Automatically specialize standalone empty programs and direct constant-string console output after a strict proof over the original AST. Unknown syntax, effects, receiver provenance, and unsupported build modes retain normal compilation. No enabling flag is required. The minimal executable omits Perry's managed heap, GC, event loop, allocator, and runtime archives. + +Local AMD Linux and macOS verification, accepted-language limits, raw startup/RSS samples, allocator tradeoffs and build-provenance findings are recorded in the [startup campaign report](https://github.com/proggeramlug/js-compiled/blob/perf/startup-verification/diagnostics/startup/evidence/2026-09-11/REPORT.md). diff --git a/changelog.d/10144-wasm-review-hardening.md b/changelog.d/10144-wasm-review-hardening.md new file mode 100644 index 0000000000..42f27989f2 --- /dev/null +++ b/changelog.d/10144-wasm-review-hardening.md @@ -0,0 +1,13 @@ +--- +category: Runtime +title: Harden WebAssembly host bindings +--- + +`WebAssembly.instantiate(Module)` now returns the specified Promise while +preserving its optional-imports dispatch, Wasm table function wrappers release +their host handles when collected, and a JavaScript import that re-enters +WebAssembly no longer aliases the shared host store: nested export calls, +funcref-table calls and standalone Memory/Table/Global operations borrow +through the active import's wasmi `Caller` (Emscripten `invoke_*`/`dynCall` +and imports calling exports such as `_malloc` keep working). Overlapping store +access outside an import boundary is refused. diff --git a/changelog.d/10156-cjs-getter-reexport-binding.md b/changelog.d/10156-cjs-getter-reexport-binding.md new file mode 100644 index 0000000000..31c304656b --- /dev/null +++ b/changelog.d/10156-cjs-getter-reexport-binding.md @@ -0,0 +1,12 @@ +### Fixed + +- Recognize CommonJS exports installed with `Object.defineProperty`, including + Babel's getter re-exports, as value exports. ESM named and namespace imports + now read the current property through the existing getter-aware runtime + lookup and call the returned value, instead of referencing a function symbol + that the CommonJS barrel never defined. +- Avoid evaluating synthetic CommonJS property exports during initialization, + preserving accessor side effects and exports assigned or replaced later. + Regression coverage includes named imports, namespace calls, ESM barrels, + materialized and dynamic namespaces, writable descriptors, and late + assignments (#10153). diff --git a/changelog.d/10193-regexp-construction-cache.md b/changelog.d/10193-regexp-construction-cache.md new file mode 100644 index 0000000000..06509c0d21 --- /dev/null +++ b/changelog.d/10193-regexp-construction-cache.md @@ -0,0 +1 @@ +Cache immutable Perex programs by source identity and canonical flags, with bounded LRU eviction and GC relocation support. Share upstream program-validation witnesses across cached constructions and reduce builtin RegExp test and empty-string replacement overhead while preserving independent `lastIndex`, overrides, and `RegExp.prototype.compile` behavior. This removes repeated compilation of emoji-regex in string-width (#10179). diff --git a/changelog.d/10244-android-tls-pool.md b/changelog.d/10244-android-tls-pool.md new file mode 100644 index 0000000000..e1530f08bf --- /dev/null +++ b/changelog.d/10244-android-tls-pool.md @@ -0,0 +1 @@ +- Android runtimes share one pthread key across Perry's cached thread-local declarations, preventing the key exhaustion that aborted minimal UI apps when the timer pump started (#10219). Per-thread values keep independent initialization and cleanup, and accesses after teardown remain fallible. diff --git a/changelog.d/10245-prune-unused-reexports.md b/changelog.d/10245-prune-unused-reexports.md new file mode 100644 index 0000000000..78b14254e6 --- /dev/null +++ b/changelog.d/10245-prune-unused-reexports.md @@ -0,0 +1 @@ +Perry now postpones unused named and wildcard re-exports during module collection when package sideEffects declarations prove their static dependency trees safe to omit. Barrels consisting of forwarded named imports are normalized to equivalent re-exports with dependency order preserved. Cyclic dependency trees stay intact so pruning cannot change their initialization entry point. Export demand grows to a fixed point across later importers and barrel chains; imports used by module code, dynamic-import namespaces, live bindings, and effectful or uncertain dependencies remain available. The compile summary reports omitted modules. Set PERRY_NO_REEXPORT_PRUNE=1 for an unpruned comparison, or PERRY_COLLECT_ONLY=1 to write the collection audit without generating code. diff --git a/changelog.d/10246-window-frame-persistence.md b/changelog.d/10246-window-frame-persistence.md new file mode 100644 index 0000000000..bd746a845c --- /dev/null +++ b/changelog.d/10246-window-frame-persistence.md @@ -0,0 +1,11 @@ +Add opt-in `App({ frameAutosaveName: "main", ... })` desktop window persistence +(#10170). macOS uses AppKit frame autosave; Windows and WinUI restore native +placement and maximized/fullscreen state; GTK4 restores normal size and window +state while leaving positioning to the compositor. Saved frames take precedence +over launch defaults, and empty or omitted names disable persistence. + +Persistence keys are scoped to the executable and window name. Windows/Linux +settings are validated and replaced atomically, minimized Windows sessions reopen +in their last non-minimized state, and fullscreen placement preserves the normal +frame. Add compiler regression coverage, storage tests, a native Windows +close/reopen test, TypeScript declarations, and a desktop example. diff --git a/crates/perry-codegen/src/codegen/artifacts.rs b/crates/perry-codegen/src/codegen/artifacts.rs index 6d39180555..b94530f303 100644 --- a/crates/perry-codegen/src/codegen/artifacts.rs +++ b/crates/perry-codegen/src/codegen/artifacts.rs @@ -1972,5 +1972,7 @@ pub(super) fn emit_module_artifacts(c: ModuleArtifactsCtx<'_>) -> Result<()> { ); progress.checkpoint("string pool and registration initializer"); + super::namespace_value_getters::emit(llmod, module_prefix, cross_module); + Ok(()) } diff --git a/crates/perry-codegen/src/codegen/cjs_exports.rs b/crates/perry-codegen/src/codegen/cjs_exports.rs new file mode 100644 index 0000000000..1451edf820 --- /dev/null +++ b/crates/perry-codegen/src/codegen/cjs_exports.rs @@ -0,0 +1,123 @@ +//! Turn the CJS wrapper's synthetic property exports into live value getters. + +use std::borrow::Cow; +use std::collections::{HashMap, HashSet}; + +use perry_hir::{Expr, Module, Stmt}; + +use crate::module::LlModule; +use crate::types::{DOUBLE, I32, I64, PTR}; + +pub(super) type PropertyExports = HashMap; + +/// The wrapper emits `export const x = _cjs.x` to register value-export +/// metadata. Do not execute that read during module initialization: it can +/// invoke a getter too early and freezes exports assigned after initialization. +/// Keep the caller's HIR immutable (it is also the object-cache input). +pub(super) fn prepare(hir: &Module) -> (Cow<'_, Module>, PropertyExports) { + // Pair the wrapper's private binding with its compiler-owned preamble; + // an ordinary ESM variable named `_cjs` still has snapshot semantics. + let has_preamble = hir.imports.iter().any(|import| { + import + .source + .strip_prefix("node:") + .unwrap_or(&import.source) + == "module" + && import.specifiers.iter().any(|specifier| { + matches!(specifier, perry_hir::ImportSpecifier::Named { imported, local } + if imported == "createRequire" && local == "__perry_cjs_create_require") + }) + }); + if !has_preamble { + return (Cow::Borrowed(hir), HashMap::new()); + } + let entry = super::entry_outline::logical_entry_stmts(hir); + let Some(object_id) = entry.iter().find_map(|stmt| match stmt { + Stmt::Let { id, name, .. } if name == "_cjs" => Some(*id), + _ => None, + }) else { + return (Cow::Borrowed(hir), HashMap::new()); + }; + let exported: HashSet<&str> = hir + .exports + .iter() + .filter_map(|export| match export { + perry_hir::Export::Named { local, .. } => Some(local.as_str()), + _ => None, + }) + .collect(); + let mut properties = HashMap::new(); + let mut bindings = HashSet::new(); + for stmt in entry { + if let Stmt::Let { + id, + name, + init: Some(Expr::PropertyGet { + object, property, .. + }), + .. + } = stmt + { + if exported.contains(name.as_str()) + && matches!(object.as_ref(), Expr::LocalGet(id) if *id == object_id) + { + properties.insert(name.clone(), (object_id, property.clone())); + bindings.insert(*id); + } + } + } + if properties.is_empty() { + return (Cow::Borrowed(hir), properties); + } + let mut owned = hir.clone(); + let clear_snapshots = |stmts: &mut [Stmt]| { + for stmt in stmts { + if let Stmt::Let { id, init, .. } = stmt { + if bindings.contains(id) { + *init = None; + } + } + } + }; + clear_snapshots(&mut owned.init); + for function in &mut owned.functions { + if super::entry_outline::is_entry_chunk(function) { + clear_snapshots(&mut function.body); + } + } + (Cow::Owned(owned), properties) +} + +pub(super) fn emit_getter( + llmod: &mut LlModule, + getter_name: &str, + module_prefix: &str, + object_id: u32, + property: &str, +) { + let (key_global, key_len) = llmod.add_string_constant(property); + let getter = llmod.define_function(getter_name, DOUBLE, vec![]); + getter.create_block("entry"); + let blk = getter.block_mut(0).unwrap(); + // Allocate the key before loading the GC-rooted namespace. No raw object + // pointer survives that allocation; the boxed property helper handles + // accessors, prototypes and function-valued module.exports. + let key = blk.call( + I64, + "js_string_from_bytes", + &[ + (PTR, &format!("@{key_global}")), + (I32, &key_len.to_string()), + ], + ); + let object = blk.load( + DOUBLE, + &format!("@perry_global_{module_prefix}__{object_id}"), + ); + let value = blk.call( + DOUBLE, + "js_object_get_field_by_name_boxed", + &[(DOUBLE, &object), (I64, &key)], + ); + blk.ret(DOUBLE, &value); +} diff --git a/crates/perry-codegen/src/codegen/entry.rs b/crates/perry-codegen/src/codegen/entry.rs index ed0ef5eccf..b185fe85b8 100644 --- a/crates/perry-codegen/src/codegen/entry.rs +++ b/crates/perry-codegen/src/codegen/entry.rs @@ -1231,6 +1231,7 @@ pub(super) fn compile_module_entry( let body_check_idx = ctx.new_block("event_loop.body_check"); let body_wait_idx = ctx.new_block("event_loop.body_wait"); let exit_idx = ctx.new_block("event_loop.exit"); + let finalize_idx = ctx.new_block("event_loop.finalize"); let header_label = ctx.block_label(header_idx); let pending_label = ctx.block_label(pending_idx); let host_ret_label = ctx.block_label(host_ret_idx); @@ -1238,6 +1239,7 @@ pub(super) fn compile_module_entry( let body_check_label = ctx.block_label(body_check_idx); let body_wait_label = ctx.block_label(body_wait_idx); let exit_label = ctx.block_label(exit_idx); + let finalize_label = ctx.block_label(finalize_idx); // Initial event-loop flush (4 rounds) before entering the // main loop — handles fire-and-forget .then() chains that @@ -1354,11 +1356,11 @@ pub(super) fn compile_module_entry( ctx.block().call_void("js_wait_for_event", &[]); ctx.block().br(&header_label); - // loop_exit: fire `beforeExit` (#2135) with the would-be - // exit code, then drain microtasks/timers once more so any - // last-minute work the listener queued still runs before - // we ret. Mirrors Node's "event loop drained → one - // beforeExit pass" semantics. + // loop_exit: fire beforeExit with the would-be exit code, + // then finish its ticks/promises without consuming timers or + // I/O completions. If it scheduled another event-loop turn, + // return to the loop; its next drain emits beforeExit again. + // Microtasks alone finish here and do not resurrect the loop. // // `beforeExit` is emitted with the PENDING `process.exitCode` // rather than a literal `0`: Node passes the code the process @@ -1379,7 +1381,13 @@ pub(super) fn compile_module_entry( .call_void("js_process_emit_before_exit_pending", &[]); let _ = ctx .block() - .call(I32, "js_promise_run_microtasks_event_loop", &[]); + .call(I32, "js_promise_run_before_exit_checkpoint", &[]); + let resumed = emit_event_loop_liveness(&mut ctx, cross_module.needs_stdlib); + let resumed_cmp = ctx.block().icmp_ne(I32, &resumed, &zero); + ctx.block() + .cond_br(&resumed_cmp, &header_label, &finalize_label); + + ctx.current_block = finalize_idx; ctx.block().call_void("js_process_run_exit_sequence", &[]); ctx.block() .call_void("js_process_run_finalization_exit", &[]); diff --git a/crates/perry-codegen/src/codegen/entry/tests.rs b/crates/perry-codegen/src/codegen/entry/tests.rs index 663f22dedb..10c0bdde44 100644 --- a/crates/perry-codegen/src/codegen/entry/tests.rs +++ b/crates/perry-codegen/src/codegen/entry/tests.rs @@ -202,7 +202,7 @@ fn executable_exit_block_emits_the_process_exit_event() { .find("call void @js_process_emit_before_exit_pending()") .expect("beforeExit should be emitted with the pending exit code"); let pump = exit_block - .find("call i32 @js_promise_run_microtasks_event_loop()") + .find("call i32 @js_promise_run_before_exit_checkpoint()") .expect("the post-beforeExit drain should be emitted"); let exit_event = exit_block .find("call void @js_process_run_exit_sequence()") @@ -224,6 +224,11 @@ fn executable_exit_block_emits_the_process_exit_event() { pump < exit_event, "the exit event must come after beforeExit and its drain\n{exit_block}" ); + let after_checkpoint = &exit_block[pump..exit_event]; + assert!( + after_checkpoint.contains("br i1") && after_checkpoint.contains("%event_loop.header."), + "work scheduled by beforeExit must be able to return to the event loop\n{after_checkpoint}" + ); assert!( exit_event < finalization, "user exit listeners run before the finalization-registry callbacks\n{exit_block}" diff --git a/crates/perry-codegen/src/codegen/mod.rs b/crates/perry-codegen/src/codegen/mod.rs index 2c93833b55..df28e079e1 100644 --- a/crates/perry-codegen/src/codegen/mod.rs +++ b/crates/perry-codegen/src/codegen/mod.rs @@ -182,6 +182,7 @@ mod artifact_display_names; mod artifact_source_text; mod artifacts; mod boxed_locals; +mod cjs_exports; #[cfg(test)] mod clone_suffix_tests; mod closure; @@ -213,6 +214,7 @@ mod method; mod method_registry; mod method_trampolines; mod module_globals_emit; +pub(crate) mod namespace_value_getters; mod native_namespace_exports; #[cfg(test)] mod number_exactness_tests; @@ -410,6 +412,8 @@ pub fn user_function_symbol(module_name: &str, function_name: &str) -> String { /// guarantee — do not change to `&mut` without also moving the cache /// hash to AFTER codegen. pub fn compile_module(hir: &HirModule, opts: CompileOptions) -> Result> { + let (live_cjs_hir, cjs_property_exports) = cjs_exports::prepare(hir); + let hir = live_cjs_hir.as_ref(); let progress = CompileProgress::new(&hir.name, module_callable_count(hir)); let triple = opts.target.clone().unwrap_or_else(default_target_triple); let fp_flags = crate::block::FpFlags::new(opts.fast_math, opts.fp_contract_mode); @@ -2731,6 +2735,7 @@ pub fn compile_module(hir: &HirModule, opts: CompileOptions) -> Result> &opts.imported_classes, &cross_module.compile_time_constants, &module_prefix, + &cjs_property_exports, ); cross_module.module_global_proven_types = module_global_proven_types; diff --git a/crates/perry-codegen/src/codegen/module_globals_emit.rs b/crates/perry-codegen/src/codegen/module_globals_emit.rs index 3e4c057b18..ee0669c9a4 100644 --- a/crates/perry-codegen/src/codegen/module_globals_emit.rs +++ b/crates/perry-codegen/src/codegen/module_globals_emit.rs @@ -160,6 +160,7 @@ pub(crate) fn emit_module_globals( imported_classes: &[ImportedClass], compile_time_constants: &HashMap, module_prefix: &str, + cjs_property_exports: &super::cjs_exports::PropertyExports, ) -> ModuleGlobals { // Module-level globals registry. Pre-walk: // 1. Collect every LocalId referenced from any function or method @@ -170,6 +171,9 @@ pub(crate) fn emit_module_globals( // as cheap stack alloca (preserves perf for the bench // benchmarks that don't share state with helper functions). let mut referenced_from_fn: std::collections::HashSet = std::collections::HashSet::new(); + // Live CJS value getters read the namespace even after its synthetic + // snapshot initializers have been removed. + referenced_from_fn.extend(cjs_property_exports.values().map(|(id, _)| *id)); // Helper that handles "params + lets define a scope, refs minus // defines flow out". Used for every function/method/closure body. let scan_body = |params: &[perry_hir::Param], @@ -505,11 +509,21 @@ pub(crate) fn emit_module_globals( let getter_name = format!("perry_fn_{}__{}", module_prefix, sanitize(public_name)); if !llmod.has_function(&getter_name) { - let getter = llmod.define_function(&getter_name, DOUBLE, vec![]); - let _ = getter.create_block("entry"); - let blk = getter.block_mut(0).unwrap(); - let val = blk.load(DOUBLE, &format!("@{}", global_name)); - blk.ret(DOUBLE, &val); + if let Some((object_id, property)) = cjs_property_exports.get(name) { + super::cjs_exports::emit_getter( + llmod, + &getter_name, + module_prefix, + *object_id, + property, + ); + } else { + let getter = llmod.define_function(&getter_name, DOUBLE, vec![]); + let _ = getter.create_block("entry"); + let blk = getter.block_mut(0).unwrap(); + let val = blk.load(DOUBLE, &format!("@{}", global_name)); + blk.ret(DOUBLE, &val); + } } // Import-origin metadata preserves the raw exported diff --git a/crates/perry-codegen/src/codegen/namespace_value_getters.rs b/crates/perry-codegen/src/codegen/namespace_value_getters.rs new file mode 100644 index 0000000000..4b5d14dd0d --- /dev/null +++ b/crates/perry-codegen/src/codegen/namespace_value_getters.rs @@ -0,0 +1,41 @@ +//! Closure-ABI adapters for live exports in materialized static namespaces. + +use super::helpers::sanitize_member; +use super::opts::CrossModuleCtx; +use crate::module::LlModule; +use crate::types::{DOUBLE, I64}; + +pub(crate) fn symbol(module_prefix: &str, namespace: &str, member: &str) -> String { + format!( + "__perry_namespace_value_{}__{}", + module_prefix, + sanitize_member(&format!("{namespace}:{member}")) + ) +} + +pub(super) fn emit(llmod: &mut LlModule, module_prefix: &str, imports: &CrossModuleCtx) { + let mut members: Vec<_> = imports.namespace_member_prefixes.iter().collect(); + members.sort_by_key(|(key, _)| *key); + for ((namespace, member), source_prefix) in members { + let wrapper_name = symbol(module_prefix, namespace, member); + // Lowering declares an adapter only when a namespace value actually + // escapes. Direct `ns.x` reads need no additional wrapper. + if !llmod.is_declared(&wrapper_name) || llmod.has_function(&wrapper_name) { + continue; + } + let origin = crate::expr::import_origin_suffix_ns( + &imports.import_function_origin_names, + &imports.namespace_member_origin_names, + namespace, + member, + ); + let getter_name = format!("perry_fn_{source_prefix}__{origin}"); + llmod.declare_function(&getter_name, DOUBLE, &[]); + let wrapper = + llmod.define_function(wrapper_name, DOUBLE, vec![(I64, "%closure".to_string())]); + wrapper.create_block("entry"); + let block = wrapper.block_mut(0).unwrap(); + let value = block.call(DOUBLE, &getter_name, &[]); + block.ret(DOUBLE, &value); + } +} diff --git a/crates/perry-codegen/src/expr/dyn_extern_i18n.rs b/crates/perry-codegen/src/expr/dyn_extern_i18n.rs index e6c77523f6..109d708c81 100644 --- a/crates/perry-codegen/src/expr/dyn_extern_i18n.rs +++ b/crates/perry-codegen/src/expr/dyn_extern_i18n.rs @@ -8,7 +8,7 @@ use anyhow::{anyhow, bail, Result}; use perry_hir::types::Type as HirType; use perry_hir::Expr; -use crate::nanbox::{double_literal, POINTER_MASK_I64}; +use crate::nanbox::double_literal; use crate::rooting::{with_rooted_accumulator, with_rooted_group, Arg, Repr}; use crate::types::{DOUBLE, I32, I64, PTR}; @@ -439,9 +439,10 @@ fn materialize_compiled_namespace(ctx: &mut FnCtx<'_>, name: &str) -> Result, name: &str) -> Result = None; - let mut window_state_ptr: Option = None; - let mut frameless_val: Option = None; - let mut level_ptr: Option = None; - let mut transparent_val: Option = None; - let mut vibrancy_ptr: Option = None; - let mut activation_policy_ptr: Option = None; - for (key, val) in &props { - match key.as_str() { - "title" => { - let v = lower_expr(ctx, val)?; - let blk = ctx.block(); - title_ptr = unbox_to_i64(blk, &v); - } - "width" => { - width_d = lower_expr(ctx, val)?; - } - "height" => { - height_d = lower_expr(ctx, val)?; - } - "body" => { - let v = lower_expr(ctx, val)?; - let blk = ctx.block(); - body_handle = unbox_to_i64(blk, &v); - } - "icon" => { - let v = lower_expr(ctx, val)?; - let blk = ctx.block(); - icon_ptr = Some(unbox_to_i64(blk, &v)); - } - // Issue #1280 — `windowState: "normal" | "maximized" | "fullscreen"`. - // Forwarded to perry_ui_app_set_window_state; each platform - // backend applies the state at app_run time. - "windowState" => { - let v = lower_expr(ctx, val)?; - let blk = ctx.block(); - window_state_ptr = Some(unbox_to_i64(blk, &v)); - } - // v0.4.11 launcher-style window options, lost in the Phase K - // Cranelift→LLVM cutover (2026-07-16 docs audit). `frameless` - // and `transparent` are forwarded as the raw NaN-boxed value — - // every platform backend only acts when the bits equal - // TAG_TRUE, exactly like the original Cranelift wiring. - "frameless" => { - frameless_val = Some(lower_expr(ctx, val)?); - } - "transparent" => { - transparent_val = Some(lower_expr(ctx, val)?); - } - // `level` / `vibrancy` / `activationPolicy` are strings. Route - // through the SSO-safe unbox (js_get_string_pointer_unified) - // rather than the raw pointer mask: short literals like - // "modal" or "menu" can arrive as inline SSO values whose low - // 48 bits are not a StringHeader pointer. - "level" => { - let v = lower_expr(ctx, val)?; - let blk = ctx.block(); - level_ptr = Some(crate::expr::unbox_str_handle(blk, &v)); - } - "vibrancy" => { - let v = lower_expr(ctx, val)?; - let blk = ctx.block(); - vibrancy_ptr = Some(crate::expr::unbox_str_handle(blk, &v)); - } - "activationPolicy" => { - let v = lower_expr(ctx, val)?; - let blk = ctx.block(); - activation_policy_ptr = Some(crate::expr::unbox_str_handle(blk, &v)); - } - _ => { - let _ = lower_expr(ctx, val)?; + return crate::rooting::with_rooted_group(ctx, 1, |ctx, frame_roots| { + let mut title_ptr: String = "0".to_string(); + let mut width_d: String = "1024.0".to_string(); + let mut height_d: String = "768.0".to_string(); + let mut body_handle: String = "0".to_string(); + let mut icon_ptr: Option = None; + let mut window_state_ptr: Option = None; + let mut frame_autosave_name_root: Option = None; + let mut frameless_val: Option = None; + let mut level_ptr: Option = None; + let mut transparent_val: Option = None; + let mut vibrancy_ptr: Option = None; + let mut activation_policy_ptr: Option = None; + for (key, val) in &props { + match key.as_str() { + "title" => { + let v = lower_expr(ctx, val)?; + let blk = ctx.block(); + title_ptr = unbox_to_i64(blk, &v); + } + "width" => { + width_d = lower_expr(ctx, val)?; + } + "height" => { + height_d = lower_expr(ctx, val)?; + } + "body" => { + let v = lower_expr(ctx, val)?; + let blk = ctx.block(); + body_handle = unbox_to_i64(blk, &v); + } + "icon" => { + let v = lower_expr(ctx, val)?; + let blk = ctx.block(); + icon_ptr = Some(unbox_to_i64(blk, &v)); + } + // Issue #1280 — `windowState: "normal" | "maximized" | "fullscreen"`. + // Forwarded to perry_ui_app_set_window_state; each platform + // backend applies the state at app_run time. + "windowState" => { + let v = lower_expr(ctx, val)?; + let blk = ctx.block(); + window_state_ptr = Some(unbox_to_i64(blk, &v)); + } + "frameAutosaveName" => { + // Later options (especially body builders) can collect. + // Keep the JS string rooted until the backend copies it. + frame_autosave_name_root = Some(frame_roots.lower(ctx, val, true)?); + } + // v0.4.11 launcher-style window options, lost in the Phase K + // Cranelift→LLVM cutover (2026-07-16 docs audit). `frameless` + // and `transparent` are forwarded as the raw NaN-boxed value — + // every platform backend only acts when the bits equal + // TAG_TRUE, exactly like the original Cranelift wiring. + "frameless" => { + frameless_val = Some(lower_expr(ctx, val)?); + } + "transparent" => { + transparent_val = Some(lower_expr(ctx, val)?); + } + // `level` / `vibrancy` / `activationPolicy` are strings. Route + // through the SSO-safe unbox (js_get_string_pointer_unified) + // rather than the raw pointer mask: short literals like + // "modal" or "menu" can arrive as inline SSO values whose low + // 48 bits are not a StringHeader pointer. + "level" => { + let v = lower_expr(ctx, val)?; + let blk = ctx.block(); + level_ptr = Some(crate::expr::unbox_str_handle(blk, &v)); + } + "vibrancy" => { + let v = lower_expr(ctx, val)?; + let blk = ctx.block(); + vibrancy_ptr = Some(crate::expr::unbox_str_handle(blk, &v)); + } + "activationPolicy" => { + let v = lower_expr(ctx, val)?; + let blk = ctx.block(); + activation_policy_ptr = Some(crate::expr::unbox_str_handle(blk, &v)); + } + _ => { + let _ = lower_expr(ctx, val)?; + } } } - } - ctx.pending_declares.push(( - "perry_ui_app_create".to_string(), - I64, - vec![I64, DOUBLE, DOUBLE], - )); - ctx.pending_declares.push(( - "perry_ui_app_set_icon".to_string(), - crate::types::VOID, - vec![I64], - )); - ctx.pending_declares.push(( - "perry_ui_app_set_window_state".to_string(), - crate::types::VOID, - vec![I64, I64], - )); - ctx.pending_declares.push(( - "perry_ui_app_set_frameless".to_string(), - crate::types::VOID, - vec![I64, DOUBLE], - )); - ctx.pending_declares.push(( - "perry_ui_app_set_level".to_string(), - crate::types::VOID, - vec![I64, I64], - )); - ctx.pending_declares.push(( - "perry_ui_app_set_transparent".to_string(), - crate::types::VOID, - vec![I64, DOUBLE], - )); - ctx.pending_declares.push(( - "perry_ui_app_set_vibrancy".to_string(), - crate::types::VOID, - vec![I64, I64], - )); - ctx.pending_declares.push(( - "perry_ui_app_set_activation_policy".to_string(), - crate::types::VOID, - vec![I64, I64], - )); - ctx.pending_declares.push(( - "perry_ui_app_set_body".to_string(), - crate::types::VOID, - vec![I64, I64], - )); - ctx.pending_declares.push(( - "perry_ui_app_run".to_string(), - crate::types::VOID, - vec![I64], - )); - let blk = ctx.block(); - let app_handle = blk.call( - I64, - "perry_ui_app_create", - &[(I64, &title_ptr), (DOUBLE, &width_d), (DOUBLE, &height_d)], - ); - if let Some(icon) = icon_ptr { - blk.call_void("perry_ui_app_set_icon", &[(I64, &icon)]); - } - if let Some(state_ptr) = window_state_ptr { - blk.call_void( - "perry_ui_app_set_window_state", - &[(I64, &app_handle), (I64, &state_ptr)], - ); - } - // Window properties are applied BEFORE the body so vibrancy / - // frameless can reconfigure the window (content view swap, style - // mask) before Auto Layout constraints are installed — same call - // order the Cranelift backend used (v0.4.11). - if let Some(v) = &frameless_val { - blk.call_void( - "perry_ui_app_set_frameless", - &[(I64, &app_handle), (DOUBLE, v)], - ); - } - if let Some(p) = &level_ptr { - blk.call_void("perry_ui_app_set_level", &[(I64, &app_handle), (I64, p)]); - } - if let Some(v) = &transparent_val { - blk.call_void( - "perry_ui_app_set_transparent", - &[(I64, &app_handle), (DOUBLE, v)], + ctx.pending_declares.push(( + "perry_ui_app_create".to_string(), + I64, + vec![I64, DOUBLE, DOUBLE], + )); + ctx.pending_declares.push(( + "perry_ui_app_set_icon".to_string(), + crate::types::VOID, + vec![I64], + )); + ctx.pending_declares.push(( + "perry_ui_app_set_window_state".to_string(), + crate::types::VOID, + vec![I64, I64], + )); + ctx.pending_declares.push(( + "perry_ui_app_set_frame_autosave_name".to_string(), + crate::types::VOID, + vec![I64, I64], + )); + ctx.pending_declares.push(( + "perry_ui_app_set_frameless".to_string(), + crate::types::VOID, + vec![I64, DOUBLE], + )); + ctx.pending_declares.push(( + "perry_ui_app_set_level".to_string(), + crate::types::VOID, + vec![I64, I64], + )); + ctx.pending_declares.push(( + "perry_ui_app_set_transparent".to_string(), + crate::types::VOID, + vec![I64, DOUBLE], + )); + ctx.pending_declares.push(( + "perry_ui_app_set_vibrancy".to_string(), + crate::types::VOID, + vec![I64, I64], + )); + ctx.pending_declares.push(( + "perry_ui_app_set_activation_policy".to_string(), + crate::types::VOID, + vec![I64, I64], + )); + ctx.pending_declares.push(( + "perry_ui_app_set_body".to_string(), + crate::types::VOID, + vec![I64, I64], + )); + ctx.pending_declares.push(( + "perry_ui_app_run".to_string(), + crate::types::VOID, + vec![I64], + )); + let blk = ctx.block(); + let app_handle = blk.call( + I64, + "perry_ui_app_create", + &[(I64, &title_ptr), (DOUBLE, &width_d), (DOUBLE, &height_d)], ); - } - if let Some(p) = &vibrancy_ptr { - blk.call_void("perry_ui_app_set_vibrancy", &[(I64, &app_handle), (I64, p)]); - } - if let Some(p) = &activation_policy_ptr { + if let Some(icon) = icon_ptr { + blk.call_void("perry_ui_app_set_icon", &[(I64, &icon)]); + } + if let Some(state_ptr) = window_state_ptr { + blk.call_void( + "perry_ui_app_set_window_state", + &[(I64, &app_handle), (I64, &state_ptr)], + ); + } + // Window properties are applied BEFORE the body so vibrancy / + // frameless can reconfigure the window (content view swap, style + // mask) before Auto Layout constraints are installed — same call + // order the Cranelift backend used (v0.4.11). + if let Some(v) = &frameless_val { + blk.call_void( + "perry_ui_app_set_frameless", + &[(I64, &app_handle), (DOUBLE, v)], + ); + } + if let Some(p) = &level_ptr { + blk.call_void("perry_ui_app_set_level", &[(I64, &app_handle), (I64, p)]); + } + if let Some(v) = &transparent_val { + blk.call_void( + "perry_ui_app_set_transparent", + &[(I64, &app_handle), (DOUBLE, v)], + ); + } + if let Some(p) = &vibrancy_ptr { + blk.call_void("perry_ui_app_set_vibrancy", &[(I64, &app_handle), (I64, p)]); + } + if let Some(p) = &activation_policy_ptr { + blk.call_void( + "perry_ui_app_set_activation_policy", + &[(I64, &app_handle), (I64, p)], + ); + } + if let Some(root) = frame_autosave_name_root { + let value = frame_roots.reread(ctx, root)?; + let blk = ctx.block(); + let name_ptr = crate::expr::unbox_str_handle(blk, &value); + blk.call_void( + "perry_ui_app_set_frame_autosave_name", + &[(I64, &app_handle), (I64, &name_ptr)], + ); + } + let blk = ctx.block(); blk.call_void( - "perry_ui_app_set_activation_policy", - &[(I64, &app_handle), (I64, p)], + "perry_ui_app_set_body", + &[(I64, &app_handle), (I64, &body_handle)], ); - } - blk.call_void( - "perry_ui_app_set_body", - &[(I64, &app_handle), (I64, &body_handle)], - ); - blk.call_void("perry_ui_app_run", &[(I64, &app_handle)]); - return Ok(double_literal(0.0)); + blk.call_void("perry_ui_app_run", &[(I64, &app_handle)]); + Ok(double_literal(0.0)) + }); } } diff --git a/crates/perry-codegen/src/runtime_decls/strings_part2.rs b/crates/perry-codegen/src/runtime_decls/strings_part2.rs index 558702293d..14d9ac9516 100644 --- a/crates/perry-codegen/src/runtime_decls/strings_part2.rs +++ b/crates/perry-codegen/src/runtime_decls/strings_part2.rs @@ -719,6 +719,7 @@ pub(crate) fn declare_phase_b_strings_part2(module: &mut LlModule) { // the microtask drain and the timer queues. Emitted only by // `codegen::entry`'s event loop, whose JS stack is fully unwound. module.declare_function("js_promise_run_microtasks_event_loop", I32, &[]); + module.declare_function("js_promise_run_before_exit_checkpoint", I32, &[]); module.declare_function("js_promise_run_microtasks_await_loop", I32, &[]); module.declare_function("js_await_loop_tick_timers", I32, &[]); // ESM entry marker: first microtask drain finishes promise jobs before diff --git a/crates/perry-codegen/tests/app_window_config_options.rs b/crates/perry-codegen/tests/app_window_config_options.rs index 934700c25d..1c141060da 100644 --- a/crates/perry-codegen/tests/app_window_config_options.rs +++ b/crates/perry-codegen/tests/app_window_config_options.rs @@ -151,12 +151,13 @@ fn compile_ir(name: &str, body: Vec) -> String { String::from_utf8(compile_module(&module(name, body), empty_opts()).unwrap()).unwrap() } -const WINDOW_OPTION_SETTERS: [&str; 5] = [ +const WINDOW_OPTION_SETTERS: [&str; 6] = [ "call void @perry_ui_app_set_frameless", "call void @perry_ui_app_set_level", "call void @perry_ui_app_set_transparent", "call void @perry_ui_app_set_vibrancy", "call void @perry_ui_app_set_activation_policy", + "call void @perry_ui_app_set_frame_autosave_name", ]; #[test] @@ -173,6 +174,7 @@ fn app_config_window_options_emit_ffi_calls() { ("transparent", Expr::Bool(true)), ("vibrancy", Expr::String("sidebar".to_string())), ("activationPolicy", Expr::String("accessory".to_string())), + ("frameAutosaveName", Expr::String("launcher".to_string())), ])], ); for setter in WINDOW_OPTION_SETTERS { @@ -192,6 +194,36 @@ fn app_config_window_options_emit_ffi_calls() { ); } +#[test] +fn named_frame_persistence_is_sso_safe_and_configured_before_run() { + for name in ["main", "settings-window-with-a-long-name", ""] { + for name_first in [true, false] { + let name_field = ("frameAutosaveName", Expr::String(name.to_string())); + let state_field = ("windowState", Expr::String("fullscreen".to_string())); + let fields = if name_first { + vec![name_field, state_field] + } else { + vec![state_field, name_field] + }; + let ir = compile_ir("app_frame_persistence", vec![app_call(fields)]); + let setter = "call void @perry_ui_app_set_frame_autosave_name"; + assert_eq!(ir.matches(setter).count(), 1, "IR:\n{ir}"); + assert!( + ir.contains("call i64 @js_get_string_pointer_unified"), + "IR:\n{ir}" + ); + let create = ir.find("call i64 @perry_ui_app_create").unwrap(); + let state = ir.find("call void @perry_ui_app_set_window_state").unwrap(); + let autosave = ir.find(setter).unwrap(); + let run = ir.find("call void @perry_ui_app_run").unwrap(); + assert!( + create < state && state < autosave && autosave < run, + "IR:\n{ir}" + ); + } + } +} + #[test] fn app_config_without_window_options_emits_no_setter_calls() { let ir = compile_ir( diff --git a/crates/perry-runtime/Cargo.toml b/crates/perry-runtime/Cargo.toml index a7ac2c2950..0d0967dd99 100644 --- a/crates/perry-runtime/Cargo.toml +++ b/crates/perry-runtime/Cargo.toml @@ -50,6 +50,11 @@ keepalive-anchors = [] # retag rides the same gate (it only exists to label mimalloc's mappings). alloc-mimalloc = ["dep:mimalloc", "dep:libmimalloc-sys"] +# Prebuilt native runtime for programs whose existing feature analysis requires +# only this subset. Keep in sync with optimized_libs/prebuilt_core.rs (tested). +# Unwind behavior stays unchanged; this is a feature/size profile only. +prebuilt-core = ["full", "alloc-mimalloc", "keepalive-anchors", "global-math"] + # `PERRY_ALLOC_CENSUS` — a sampling profiler for the Rust heap # (`alloc_census`). OFF by default and compiled out entirely: the census wraps # the `#[global_allocator]`, and `gc_malloc` runs ~1M times/sec, so even the diff --git a/crates/perry-runtime/build.rs b/crates/perry-runtime/build.rs index acc9d81ad7..1453c8956a 100644 --- a/crates/perry-runtime/build.rs +++ b/crates/perry-runtime/build.rs @@ -534,6 +534,19 @@ fn generate_single_byte_encodings(out_dir: &str) { } fn main() { + println!("cargo:rerun-if-changed=src/ffi/perry_memory_profile.c"); + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") + && std::env::var("CARGO_CFG_TARGET_POINTER_WIDTH").as_deref() == Ok("64") + && std::env::var_os("CARGO_FEATURE_ALLOC_MIMALLOC").is_some() + { + let include = std::env::var_os("DEP_MIMALLOC_INCLUDE_DIR") + .expect("alloc-mimalloc must expose its matching C headers"); + cc::Build::new() + .file("src/ffi/perry_memory_profile.c") + .include(include) + .flag_if_supported("-std=c11") + .compile("perry_memory_profile"); + } println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/node_api_host/symbols.txt"); println!("cargo:rerun-if-changed=../perry-dispatch/src/lib.rs"); diff --git a/crates/perry-runtime/src/async_hooks.rs b/crates/perry-runtime/src/async_hooks.rs index f49f8828fe..11abb6c2f2 100644 --- a/crates/perry-runtime/src/async_hooks.rs +++ b/crates/perry-runtime/src/async_hooks.rs @@ -157,8 +157,7 @@ per_test_global! { static HOOKS: LazyLock>> = LazyLock::new(|| Mutex::new(Vec::new())); static RESOURCES: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); - static GC_DESTROY_QUEUE: LazyLock>> = - LazyLock::new(|| Mutex::new(VecDeque::new())); + static GC_DESTROY_QUEUE: Mutex> = Mutex::new(VecDeque::new()); static NEXT_CONTEXT_SNAPSHOT_ID: AtomicUsize = AtomicUsize::new(1); static CONTEXT_SNAPSHOTS: LazyLock< Mutex>, @@ -938,6 +937,10 @@ pub fn enqueue_gc_destroy(async_id: u64) { } } +pub(crate) fn gc_destroy_work_pending() -> bool { + !GC_DESTROY_QUEUE.lock().unwrap().is_empty() +} + pub fn drain_gc_destroy_queue() -> i32 { let ids: Vec = { let mut q = GC_DESTROY_QUEUE.lock().unwrap(); diff --git a/crates/perry-runtime/src/bun_ffi/callback.rs b/crates/perry-runtime/src/bun_ffi/callback.rs index df7b1c7c2d..f57f0e21d6 100644 --- a/crates/perry-runtime/src/bun_ffi/callback.rs +++ b/crates/perry-runtime/src/bun_ffi/callback.rs @@ -638,6 +638,10 @@ pub unsafe extern "C" fn perry_ffi_callback_dispatch( /// Execute foreign-thread callbacks on their owning JS thread. Called at the /// beginning of every microtask/event-loop pump. +pub(crate) fn threadsafe_callback_work_pending() -> bool { + !PENDING_CALLBACKS.lock().unwrap().is_empty() +} + pub(crate) fn drain_threadsafe_callbacks() -> i32 { let owner = std::thread::current().id(); let pending = { diff --git a/crates/perry-runtime/src/closure/dynamic_props.rs b/crates/perry-runtime/src/closure/dynamic_props.rs index d2fbcaf4dd..d9946ee5ab 100644 --- a/crates/perry-runtime/src/closure/dynamic_props.rs +++ b/crates/perry-runtime/src/closure/dynamic_props.rs @@ -95,9 +95,42 @@ fn get_closure_props() -> &'static Mutex> { CLOSURE_PROPS.get_or_init(|| Mutex::new(new_ptr_hash_map())) } +#[cfg(feature = "wasm-host")] +per_test_global! { + /// Host-owned funcref handles whose JavaScript wrappers are closures. + /// The closure address is a weak owner key; move/death hooks rekey or + /// release the handle without keeping the wrapper alive. + static WASM_FUNCREF_EXTERNALS: OnceLock>> = OnceLock::new(); +} + +#[cfg(feature = "wasm-host")] +fn get_wasm_funcref_externals() -> &'static Mutex> { + WASM_FUNCREF_EXTERNALS.get_or_init(|| Mutex::new(new_ptr_hash_map())) +} + +#[cfg(feature = "wasm-host")] +fn drop_wasm_funcref_external(handle: usize) { + crate::webassembly::drop_host_extern_handle(handle); +} + +#[cfg(feature = "wasm-host")] +pub(crate) fn register_wasm_funcref_external(owner: usize, handle: usize) { + if owner == 0 || handle == 0 { + drop_wasm_funcref_external(handle); + return; + } + note_young_closure_owner(owner, 0); + let replaced = match get_wasm_funcref_externals().lock() { + Ok(mut externals) => externals.insert(owner, handle), + Err(_) => Some(handle), + }; + if let Some(replaced) = replaced { + drop_wasm_funcref_external(replaced); + } +} + crate::perry_thread_local! { - /// #9754: this thread's young-entry log for the three closure side tables - /// (`CLOSURE_PROPS`, `CLOSURE_STATIC_PROTOTYPES`, `CLOSURE_DELETED_KEYS`) — + /// #9754: this thread's young-entry log for the closure side tables — /// the owners whose entry may hold a pointer a minor can act on, as key /// or as value. Thread-local although the tables are process-global: an /// entry's addresses belong to the inserting thread's heap, and only that @@ -322,25 +355,41 @@ pub(crate) fn clear_closure_side_tables_for_dead_ptr(ptr: usize) { if let Ok(mut deleted) = get_closure_deleted_keys().lock() { deleted.remove(&ptr); } + #[cfg(feature = "wasm-host")] + let external = get_wasm_funcref_externals() + .lock() + .ok() + .and_then(|mut externals| externals.remove(&ptr)); + #[cfg(feature = "wasm-host")] + if let Some(external) = external { + drop_wasm_funcref_external(external); + } } -/// Cheap sweep gate: true when any of the three closure side tables has +/// Cheap sweep gate: true when any closure side table has /// entries, so the per-dead-object `clear_dead_payload` dispatch can be /// skipped entirely on the (overwhelmingly common) runs that never attach /// props to closures. Mirrors `object::overflow_fields_is_empty`. pub(crate) fn closure_dynamic_side_tables_nonempty() -> bool { - get_closure_props().lock().is_ok_and(|m| !m.is_empty()) + let dynamic = get_closure_props().lock().is_ok_and(|m| !m.is_empty()) || get_closure_prototypes().lock().is_ok_and(|m| !m.is_empty()) || get_closure_deleted_keys() .lock() - .is_ok_and(|m| !m.is_empty()) + .is_ok_and(|m| !m.is_empty()); + #[cfg(feature = "wasm-host")] + return dynamic + || get_wasm_funcref_externals() + .lock() + .is_ok_and(|m| !m.is_empty()); + #[cfg(not(feature = "wasm-host"))] + dynamic } /// Death pruning for tenured/uncollected-by-sweep closures (2026-07-09 GC /// audit wave 2): the sweep's dead-payload arm above only fires for headers /// the ordinary sweep reclaims; closures dying in the ACTIVE nursery block, /// in bulk block resets, or in copied-minor from-space never reach it. This -/// registry-style pass walks the three tables with one of the GC's deadness +/// registry-style pass walks the tables with one of the GC's deadness /// predicates (`gc::dead_owner`, narrowed to `GC_TYPE_CLOSURE`). The tables /// are process-global: foreign threads' closure addresses don't attribute /// and are skipped (documented residual). @@ -361,6 +410,24 @@ pub(crate) fn prune_dead_closure_side_table_owners(is_dead_closure: &dyn Fn(usiz if let Ok(mut deleted) = get_closure_deleted_keys().lock() { deleted.retain(|owner, _| !is_dead(*owner)); } + #[cfg(feature = "wasm-host")] + let removed = if let Ok(mut externals) = get_wasm_funcref_externals().lock() { + let mut removed = Vec::new(); + externals.retain(|owner, handle| { + let keep = !is_dead(*owner); + if !keep { + removed.push(*handle); + } + keep + }); + removed + } else { + Vec::new() + }; + #[cfg(feature = "wasm-host")] + for external in removed { + drop_wasm_funcref_external(external); + } } /// [`prune_dead_closure_side_table_owners`] for a MINOR: only a young owner @@ -401,6 +468,18 @@ pub(crate) fn closure_dynamic_props_owner_moved(old_owner: usize, new_owner: usi deleted.entry(new_owner).or_default().extend(keys); } } + #[cfg(feature = "wasm-host")] + let replaced = get_wasm_funcref_externals() + .lock() + .ok() + .and_then(|mut externals| { + let handle = externals.remove(&old_owner)?; + externals.insert(new_owner, handle) + }); + #[cfg(feature = "wasm-host")] + if let Some(replaced) = replaced { + drop_wasm_funcref_external(replaced); + } } pub(crate) fn visit_closure_dynamic_prop_values_mut(owner: usize, mut visit: impl FnMut(&mut f64)) { @@ -497,6 +576,10 @@ pub fn scan_closure_dynamic_props_roots_mut(visitor: &mut crate::gc::RuntimeRoot if let Ok(deleted) = get_closure_deleted_keys().lock() { owners.extend(deleted.keys().copied()); } + #[cfg(feature = "wasm-host")] + if let Ok(externals) = get_wasm_funcref_externals().lock() { + owners.extend(externals.keys().copied()); + } owners.sort_unstable(); owners.dedup(); let table_len = owners.len() as u64; @@ -541,7 +624,14 @@ fn scan_closure_side_tables_young(visitor: &mut crate::gc::RuntimeRootVisitor<'_ .lock() .map(|m| m.len()) .unwrap_or(0); - (props + prototypes + deleted) as u64 + #[cfg(feature = "wasm-host")] + let externals = get_wasm_funcref_externals() + .lock() + .map(|m| m.len()) + .unwrap_or(0); + #[cfg(not(feature = "wasm-host"))] + let externals = 0; + (props + prototypes + deleted + externals) as u64 }; #[cfg(any(debug_assertions, test))] debug_assert_closure_young_log_complete(); @@ -608,6 +698,14 @@ fn debug_assert_closure_young_log_complete() { } } } + #[cfg(feature = "wasm-host")] + if let Ok(externals) = get_wasm_funcref_externals().lock() { + for &owner in externals.keys() { + if addr_is_minor_collectible(owner) { + relevant.push(owner); + } + } + } CLOSURE_YOUNG_OWNERS.with(|log| { log.borrow() .debug_assert_logged(CLOSURE_YOUNG_LOG_NAME, &relevant) @@ -685,6 +783,21 @@ fn scan_closure_owner( } } + #[cfg(feature = "wasm-host")] + if let Ok(mut externals) = get_wasm_funcref_externals().lock() { + if externals.contains_key(&owner) { + let mut new_owner = owner; + if visitor.visit_metadata_usize_slot(&mut new_owner) && new_owner != owner { + if let Some(handle) = externals.remove(&owner) { + if let Some(replaced) = externals.insert(new_owner, handle) { + drop_wasm_funcref_external(replaced); + } + } + current_owner = new_owner; + } + } + } + relevant |= addr_is_minor_collectible(current_owner); (current_owner, relevant) } @@ -1129,6 +1242,20 @@ pub(crate) fn test_clear_closure_side_tables() { if let Ok(mut deleted) = get_closure_deleted_keys().lock() { deleted.clear(); } + #[cfg(feature = "wasm-host")] + let externals = get_wasm_funcref_externals() + .lock() + .map(|mut externals| { + externals + .drain() + .map(|(_, handle)| handle) + .collect::>() + }) + .unwrap_or_default(); + #[cfg(feature = "wasm-host")] + for external in externals { + drop_wasm_funcref_external(external); + } } /// Snapshot every dynamic property on a closure as `(name, value)` pairs in diff --git a/crates/perry-runtime/src/closure/mod.rs b/crates/perry-runtime/src/closure/mod.rs index 93177371db..4effa0b213 100644 --- a/crates/perry-runtime/src/closure/mod.rs +++ b/crates/perry-runtime/src/closure/mod.rs @@ -73,6 +73,8 @@ pub(crate) use box_captures::{ box_capture_count, clone_closure_box_captures, closure_box_captures_owner_moved, prune_dead_closure_box_capture_owners, visit_closure_box_payload_slots_mut, }; +#[cfg(feature = "wasm-host")] +pub(crate) use dynamic_props::register_wasm_funcref_external; #[cfg(test)] pub(crate) use dynamic_props::test_clear_closure_side_tables; pub(crate) use dynamic_props::{ diff --git a/crates/perry-runtime/src/ffi/perry_memory_profile.c b/crates/perry-runtime/src/ffi/perry_memory_profile.c new file mode 100644 index 0000000000..41c419a2b2 --- /dev/null +++ b/crates/perry-runtime/src/ffi/perry_memory_profile.c @@ -0,0 +1,24 @@ +/* Use mimalloc's actual header: libmimalloc-sys 0.1.49 does not expose the + * allow_thp enum member in Rust, and its numeric id is not a stable API. */ +#include +#include +#include + +/* Run before mimalloc's default-priority constructor and Rust startup. + * getenv/strcmp/mi_option_set_default do not allocate. The default setter + * preserves mimalloc's own environment parser and explicit operator settings. */ +__attribute__((constructor(101))) +static void perry_apply_small_process_default(void) { + const char *profile = getenv("PERRY_MEMORY_PROFILE"); + if (profile != NULL && strcmp(profile, "small") == 0) { + mi_option_set_default(mi_option_allow_thp, 0); + } +} + +/* A referenced symbol pulls this member and its constructor from the archive. */ +void perry_retain_memory_profile_init(void) {} + +/* Used by the fresh-process policy probe; dead-stripped from ordinary apps. */ +long perry_memory_profile_allow_thp(void) { + return mi_option_get(mi_option_allow_thp); +} diff --git a/crates/perry-runtime/src/gc/census.rs b/crates/perry-runtime/src/gc/census.rs index ff1a0b95ed..ed70080067 100644 --- a/crates/perry-runtime/src/gc/census.rs +++ b/crates/perry-runtime/src/gc/census.rs @@ -579,6 +579,8 @@ pub(super) fn side_tables() -> Vec { rows.push(crate::symbol::symbol_registry_census()); #[cfg(feature = "regex-engine")] rows.extend(crate::regex::site_test::side_table_census()); + #[cfg(feature = "regex-engine")] + rows.push(crate::regex::perex_cache::census()); let (masks, typed) = super::layout_tables::per_object_layout_table_sizes(); rows.push(("gc.layout_slot_masks", masks, masks * 24)); rows.push(("gc.typed_layouts", typed, typed * 24)); @@ -598,10 +600,10 @@ mod regex_census_tests { .into_iter() .filter_map(|(name, _, _)| name.starts_with("regex.").then_some(name)) .collect(); - // `regex.content_cache` and `regex.literal_sites` were the previous - // engine's compiled-program caches. Perex keeps no such table: a - // program is a GC allocation owned through its header, so the ordinary - // heap census counts it. The call-site header table is what remains. + // Programs themselves remain GC leaves, counted by the ordinary heap + // census. These rows account for the bounded native cache metadata. + #[cfg(feature = "regex-engine")] + assert!(names.contains(&"regex.program_cache"), "rows: {names:?}"); assert!( names.contains(&"regex.site_test_headers"), "rows: {names:?}" diff --git a/crates/perry-runtime/src/gc/mod.rs b/crates/perry-runtime/src/gc/mod.rs index cd2027c9ff..757909bcc0 100644 --- a/crates/perry-runtime/src/gc/mod.rs +++ b/crates/perry-runtime/src/gc/mod.rs @@ -1317,6 +1317,7 @@ pub extern "C" fn js_gc_init() { // constructor; this call keeps that constructor in the link and re-applies // the option idempotently. See `crate::mimalloc_os_tag`. crate::mimalloc_os_tag::ensure_mimalloc_os_tag_applied(); + crate::startup_memory_profile::retain_constructor(); crate::node_submodules::diagnostics_channel_init_main_thread(); crate::node_submodules::init_trace_events_runtime(); // #5093: force every class-field access back through the full guard call — diff --git a/crates/perry-runtime/src/gc/tests/copying/survival_and_malloc.rs b/crates/perry-runtime/src/gc/tests/copying/survival_and_malloc.rs index f68424a6b6..7157340e65 100644 --- a/crates/perry-runtime/src/gc/tests/copying/survival_and_malloc.rs +++ b/crates/perry-runtime/src/gc/tests/copying/survival_and_malloc.rs @@ -1033,7 +1033,7 @@ fn test_copied_minor_promotable_census_filtered_walk_matches_unfiltered() { fn nursery_regexp_that_dies_young_is_finalized_by_the_copied_minor() { let _guard = CopyingNurseryTestGuard::new(1); let dead = crate::regex::test_construct_regexp_and_exec_once("b(?:c)+d-die-young", "g"); - let live = crate::regex::test_construct_regexp_and_exec_once("b(?:c)+d-die-young", "g"); + let live = crate::regex::test_construct_regexp_and_exec_once("b(?:c)+d-stay-live", "g"); let dead_addr = dead as usize; let live_addr = live as usize; // Premise: production construction is nursery-allocated now. @@ -1059,11 +1059,14 @@ fn nursery_regexp_that_dies_young_is_finalized_by_the_copied_minor() { let live_program = crate::regex::test_regexp_program_address(live); assert_ne!( dead_program, live_program, - "constructors currently emit distinct GC programs" + "different patterns must have distinct GC programs" ); let count_before = programs(); assert!(count_before >= 2); + // Evict the shared-program roots so this witness isolates header lifetime. + crate::regex::perex_cache::clear_for_tests(); + // Only `live` is rooted; `dead` is garbage. js_shadow_slot_set(0, ptr_bits(live_addr)); let _ = gc_collect_minor(); diff --git a/crates/perry-runtime/src/gc/tests/host_safepoints.rs b/crates/perry-runtime/src/gc/tests/host_safepoints.rs index 743c49c015..0969fc0d23 100644 --- a/crates/perry-runtime/src/gc/tests/host_safepoints.rs +++ b/crates/perry-runtime/src/gc/tests/host_safepoints.rs @@ -160,6 +160,37 @@ fn microtask_runner_tail_pays_bounded_safepoint_under_pressure() { assert_eq!(completed.status, JS_GC_STEP_STATUS_COMPLETED); } +#[test] +fn empty_event_loop_checkpoint_advances_gc_and_preserves_roots() { + let _guard = CopyingNurseryTestGuard::new(1); + let trigger_guard = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); + reset_old_reclaim_pressure(); + make_arena_pressure(&trigger_guard, b"empty_checkpoint_live"); + assert!(crate::promise::microtasks::empty_checkpoint_eligible_for_test()); + + let before = gc_collection_count(); + assert_eq!(crate::promise::js_promise_run_microtasks_event_loop(), 0); + let mut status = JsGcStepResult::default(); + assert_eq!(js_gc_step_status(&mut status), JS_GC_STEP_STATUS_ACTIVE); + assert_eq!(status.trigger_kind, GcTriggerKind::ArenaBytes.ffi_code()); + assert_eq!(gc_collection_count(), before); + + // Drive completion through the optimized entry itself, rather than + // bypassing it with the scheduler helper used by the other tests. + for _ in 0..500_000 { + crate::promise::js_promise_run_microtasks_event_loop(); + if gc_collection_count() > before { + break; + } + } + assert!( + gc_collection_count() > before, + "empty pumps must complete pending GC" + ); + let live_after = (js_shadow_slot_get(0) & POINTER_MASK) as *const crate::StringHeader; + unsafe { assert_string_bytes(live_after, b"empty_checkpoint_live") }; +} + #[test] fn stdlib_pump_and_perry_poll_pay_debt_through_shared_scheduler_surfaces() { let _guard = CopyingNurseryTestGuard::new(1); diff --git a/crates/perry-runtime/src/gc/tests/runtime_roots/perex_lifecycle.rs b/crates/perry-runtime/src/gc/tests/runtime_roots/perex_lifecycle.rs index 506695a809..a196ad9674 100644 --- a/crates/perry-runtime/src/gc/tests/runtime_roots/perex_lifecycle.rs +++ b/crates/perry-runtime/src/gc/tests/runtime_roots/perex_lifecycle.rs @@ -1,4 +1,4 @@ -//! Program lifetime after removing the native engine caches and Arc owners. +//! GC program lifetime after bounded cache eviction releases its strong roots. use super::*; use crate::regex::RegExpHeader; @@ -37,7 +37,27 @@ fn programs() -> usize { } #[test] -fn perex_lifecycle_reclaims_programs_when_their_only_receivers_die() { +fn regexp_cache_registers_roots_only_on_use_and_not_again_after_eviction() { + let _guard = CopyingNurseryTestGuard::new(0); + let _triggers = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); + super::perex_public::register_host_roots(); + let _ = crate::object::js_get_global_this(); + let before = root_scanner_registry_counts().1; + crate::regex::perex_cache::census(); + assert_eq!(root_scanner_registry_counts().1, before); + let scope = RuntimeHandleScope::new(); + let re = regex(&scope, "registered-on-use", ""); + assert_eq!(root_scanner_registry_counts().1, before + 1); + assert!(matches(&re, "registered-on-use")); + assert_eq!(root_scanner_registry_counts().1, before + 1); + crate::regex::perex_cache::clear_for_tests(); + let re = regex(&scope, "registered-on-use", ""); + assert!(matches(&re, "registered-on-use")); + assert_eq!(root_scanner_registry_counts().1, before + 1); +} + +#[test] +fn perex_lifecycle_reclaims_evicted_programs_when_their_only_receivers_die() { let _guard = CopyingNurseryTestGuard::new(0); let _scan = ConservativeScanDisabledGuard::new(); let _triggers = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); @@ -64,11 +84,12 @@ fn perex_lifecycle_reclaims_programs_when_their_only_receivers_die() { }; let survivor = survivor_scope.root_raw_mut_ptr(survivor); let old = survivor.with_const_ptr(|p| crate::regex::test_regexp_program_address(p)); + crate::regex::perex_cache::clear_for_tests(); gc_collect_minor(); assert_eq!( programs(), before + 1, - "dead programs must not remain in a cache" + "evicted dead programs must be reclaimed" ); assert_ne!( survivor.with_const_ptr(|p| crate::regex::test_regexp_program_address(p)), @@ -90,7 +111,7 @@ fn perex_lifecycle_reclaims_programs_when_their_only_receivers_die() { } #[test] -fn perex_lifecycle_unrelated_compilation_cannot_retain_programs_or_disarm_receivers() { +fn perex_lifecycle_eviction_releases_programs_without_disarming_receivers() { let _guard = CopyingNurseryTestGuard::new(0); let _scan = ConservativeScanDisabledGuard::new(); let _triggers = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); @@ -103,8 +124,9 @@ fn perex_lifecycle_unrelated_compilation_cannot_retain_programs_or_disarm_receiv let retained = programs(); assert!(retained >= 3); let native = external_side_live_bytes(); - // Exceed each former 512-entry cache capacity repeatedly. Collection - // must reclaim the temporary programs while the original owners survive. + // Releasing cached roots must let collection reclaim temporary programs + // while original receiver owners survive. The cache unit tests exercise + // actual LRU pressure separately. for i in 0..2078 { { let temporary = RuntimeHandleScope::new(); @@ -124,6 +146,7 @@ fn perex_lifecycle_unrelated_compilation_cannot_retain_programs_or_disarm_receiv programs() > retained, "the allocation pressure must be real" ); + crate::regex::perex_cache::clear_for_tests(); gc_collect_minor(); assert_eq!(programs(), retained, "temporary programs must be reclaimed"); assert_eq!(external_side_live_bytes(), native); @@ -187,3 +210,93 @@ fn perex_lifecycle_literal_and_dynamic_construction_have_independent_state() { let insensitive = regex(&scope, "born[0-9]+built", "i"); assert!(matches(&insensitive, "BORN9BUILT")); } + +#[test] +fn regexp_cache_is_the_only_root_and_rekeys_after_actual_movement() { + let _guard = CopyingNurseryTestGuard::new(0); + let _scan = ConservativeScanDisabledGuard::new(); + let _triggers = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); + let _force = ForcedEvacuationTestGuard::on(); + super::perex_public::register_host_roots(); + let before = programs(); + let old = { + let scope = RuntimeHandleScope::new(); + let re = regex(&scope, "cache-only-root", "g"); + assert!(matches(&re, "cache-only-root")); + re.with_const_ptr(|p| crate::regex::test_regexp_program_address(p)) + }; + let cycles = copying_minor_cycles(); + gc_collect_minor(); + assert!(copying_minor_cycles() > cycles); + assert_eq!(programs(), before + 1, "cache must retain the live program"); + { + let scope = RuntimeHandleScope::new(); + let re = regex(&scope, "cache-only-root", "g"); + assert_ne!( + old, + re.with_const_ptr(|p| crate::regex::test_regexp_program_address(p)) + ); + assert_eq!( + programs(), + before + 1, + "post-move lookup must reuse the program" + ); + assert!(matches(&re, "cache-only-root")); + } + crate::regex::perex_cache::clear_for_tests(); + gc_collect_minor(); + assert_eq!( + programs(), + before, + "eviction must release the construction cache's roots" + ); +} + +#[test] +fn regexp_active_binding_survives_eviction_and_relocation() { + use crate::regex::perex_memory::MemoryBudget; + use crate::regex::{perex_api as api, perex_runtime as host}; + use perex::{binding::BoundSubject, Budget}; + let _guard = CopyingNurseryTestGuard::new(0); + let _scan = ConservativeScanDisabledGuard::new(); + let _triggers = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); + let _force = ForcedEvacuationTestGuard::on(); + super::perex_public::register_host_roots(); + let before = programs(); + let scope = RuntimeHandleScope::new(); + let raw = { + let temporary = RuntimeHandleScope::new(); + let re = regex(&temporary, "active-binding", ""); + re.with_const_ptr::(|re| unsafe { (*re).perex_program }) + }; + // No collecting operation between extracting the live cache entry and + // rooting it in the outer scope, after the temporary scope has ended. + let owner = unsafe { crate::regex::perex_owner::GcProgram::from_cached(&scope, raw) }; + let program = api::bind_program(owner, &mut Budget::new(api::WORK)).unwrap(); + let old = program.with_view(|p| p.words().as_ptr() as usize).unwrap(); + crate::regex::perex_cache::clear_for_tests(); + gc_collect_minor(); + assert_eq!(programs(), before + 1); + assert_ne!( + program.with_view(|p| p.words().as_ptr() as usize).unwrap(), + old + ); + let subject = + BoundSubject::new(crate::regex::perex_glob::NativeText("active-binding")).unwrap(); + assert!(host::find( + &program, + &subject, + 0, + host::CaptureMode::Full, + &mut Budget::new(api::WORK), + &MemoryBudget::new(api::SCRATCH_BYTES), + api::QUANTUM, + &mut host::poll + ) + .unwrap() + .is_some()); + drop(program); + drop(scope); + gc_collect_minor(); + assert_eq!(programs(), before); +} diff --git a/crates/perry-runtime/src/gc/tests/runtime_roots/perex_match_all.rs b/crates/perry-runtime/src/gc/tests/runtime_roots/perex_match_all.rs index cdf717e6b1..3fb3eb69c2 100644 --- a/crates/perry-runtime/src/gc/tests/runtime_roots/perex_match_all.rs +++ b/crates/perry-runtime/src/gc/tests/runtime_roots/perex_match_all.rs @@ -304,6 +304,8 @@ fn perex_match_all_iterator_is_the_only_input_owner_and_releases_on_completion() assert_eq!(get(&step, b"done").to_bits(), crate::value::TAG_TRUE); assert_eq!(slot(&iter, 0).to_bits(), TAG_UNDEFINED); assert_eq!(slot(&iter, 1).to_bits(), TAG_UNDEFINED); + // Isolate the iterator's ownership from the bounded program caches. + crate::regex::perex_cache::clear_for_tests(); gc_collect_minor(); let live = build_valid_pointer_set(); assert!( diff --git a/crates/perry-runtime/src/gc/tests/runtime_roots/perex_public.rs b/crates/perry-runtime/src/gc/tests/runtime_roots/perex_public.rs index 1b9dd2922e..26232afff6 100644 --- a/crates/perry-runtime/src/gc/tests/runtime_roots/perex_public.rs +++ b/crates/perry-runtime/src/gc/tests/runtime_roots/perex_public.rs @@ -30,6 +30,9 @@ pub(super) fn register_host_roots() { gc_register_mutable_root_scanner(crate::object::scan_object_cache_roots_mut); gc_register_mutable_root_scanner(crate::object::scan_exotic_expando_roots_mut); gc_register_mutable_root_scanner(crate::regex::scan_last_exec_groups_root_mut); + gc_register_mutable_root_scanner( + crate::object::regex_proto_thunks::scan_canonical_test_site_roots_mut, + ); gc_register_mutable_root_scanner(crate::object::scan_implicit_this_roots_mut); gc_register_mutable_root_scanner(crate::closure::scan_singleton_closure_roots_mut); gc_register_mutable_root_scanner(crate::closure::scan_closure_dynamic_props_roots_mut); diff --git a/crates/perry-runtime/src/gc/tests/runtime_roots/perex_reuse.rs b/crates/perry-runtime/src/gc/tests/runtime_roots/perex_reuse.rs index 618bf73ca1..8fee6f2179 100644 --- a/crates/perry-runtime/src/gc/tests/runtime_roots/perex_reuse.rs +++ b/crates/perry-runtime/src/gc/tests/runtime_roots/perex_reuse.rs @@ -144,17 +144,17 @@ fn perex_reuse_serves_a_whole_global_loop_across_moving_collections() { let fresh = regex(&scope, PATTERN, "gu"); let (fresh_matches, fresh_work) = global_loop(&fresh, &fresh_input, None); assert_eq!(fresh_matches, expected); - // Both loops validate their program once: the witness in each program cell - // lets later searches bind it without validating (#10166). The reused loop - // paid its validation in `setup`, and additionally resumes each search from - // the previous one instead of seeking from an end of this non-ASCII subject, - // so it must charge strictly less than the fresh loop minus one validation. - // Without reuse the two differ by exactly that one validation. + // Construction now shares the same program cell and its validation witness. + // The second receiver pays no validation; the remaining work difference + // must come from resuming searches near the preceding match. + assert_eq!(unsafe { (*receiver_ptr(&fresh)).perex_program }, unsafe { + (*receiver_ptr(&reused)).perex_program + }); let validation = api::WORK - setup.remaining(); - assert!(validation > 0); + assert!(validation > 0, "the first binding must really validate"); assert!( - fresh_work > reused_work + validation, - "reuse must save its seeks as well as the validation: fresh {fresh_work}, reused {reused_work}, one validation {validation}" + fresh_work > reused_work, + "reuse must save seeks even when validation is shared: fresh {fresh_work}, reused {reused_work}" ); } diff --git a/crates/perry-runtime/src/gc/tests/runtime_roots/regexp_last_index.rs b/crates/perry-runtime/src/gc/tests/runtime_roots/regexp_last_index.rs index de1db4e05d..6b1b323f33 100644 --- a/crates/perry-runtime/src/gc/tests/runtime_roots/regexp_last_index.rs +++ b/crates/perry-runtime/src/gc/tests/runtime_roots/regexp_last_index.rs @@ -38,6 +38,13 @@ extern "C" fn collect_then_zero(_closure: *const crate::closure::ClosureHeader) 0.0 } +fn register_regex_roots() { + register_runtime_handle_root_scanner_for_tests(); + gc_register_mutable_root_scanner( + crate::object::regex_proto_thunks::scan_canonical_test_site_roots_mut, + ); +} + fn heap_string(bytes: &[u8]) -> *mut StringHeader { crate::string::js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) } @@ -72,7 +79,7 @@ fn regexp_exec_survives_a_moving_minor_inside_the_lastindex_coercion() { let _scan = ConservativeScanDisabledGuard::new(); let _triggers = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); let _force = ForcedEvacuationTestGuard::on(); - register_runtime_handle_root_scanner_for_tests(); + register_regex_roots(); let scope = crate::gc::RuntimeHandleScope::new(); @@ -158,7 +165,7 @@ fn regexp_exec_materializes_an_owned_snapshot_after_an_alloc_point_minor() { let _pacing = crate::gc::policy::force_alloc_point_minor_pacing(); let _scan = ConservativeScanDisabledGuard::new(); let trigger_guard = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); - register_runtime_handle_root_scanner_for_tests(); + register_regex_roots(); let scope = crate::gc::RuntimeHandleScope::new(); let subject = heap_string(b"prefix-young-42-suffix"); @@ -217,7 +224,7 @@ fn string_match_fancy_materializes_an_owned_snapshot_after_an_alloc_point_minor( let _pacing = crate::gc::policy::force_alloc_point_minor_pacing(); let _scan = ConservativeScanDisabledGuard::new(); let trigger_guard = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); - register_runtime_handle_root_scanner_for_tests(); + register_regex_roots(); let scope = crate::gc::RuntimeHandleScope::new(); let subject = heap_string(b"prefix-young-42-suffix"); diff --git a/crates/perry-runtime/src/gc/tests/support.rs b/crates/perry-runtime/src/gc/tests/support.rs index 34c4fcc20e..ec6e4409ce 100644 --- a/crates/perry-runtime/src/gc/tests/support.rs +++ b/crates/perry-runtime/src/gc/tests/support.rs @@ -434,6 +434,12 @@ pub(super) fn reset_copying_nursery_runtime_test_state() { crate::json::test_clear_parse_roots(); crate::string::prune_dead_utf16_indexes(&|_| true); crate::string::trim_cache::test_clear_trim_cache(); + #[cfg(feature = "regex-engine")] + crate::regex::perex_cache::clear_for_tests(); + #[cfg(feature = "regex-engine")] + crate::regex::perex_cache::reset_registration_for_tests(); + #[cfg(feature = "regex-engine")] + crate::object::regex_proto_thunks::test_clear_canonical_site(); crate::set::test_clear_set_roots(); crate::os::test_clear_process_event_listeners(); crate::promise::test_clear_promise_scanner_roots(); diff --git a/crates/perry-runtime/src/hot_diag.rs b/crates/perry-runtime/src/hot_diag.rs index b75e3ddd25..a2b4ab8be0 100644 --- a/crates/perry-runtime/src/hot_diag.rs +++ b/crates/perry-runtime/src/hot_diag.rs @@ -119,6 +119,16 @@ pub struct RegexDiag { last_dump: Option, events: u32, pub new_calls: u64, + pub perex_compiles: u64, + pub perex_validations: u64, + pub perex_canonical_execs: u64, + pub perex_removes: u64, + pub perex_searches: u64, + pub perex_scratch_allocs: u64, + pub perex_scratch_grows: u64, + pub program_identity_hits: u64, + pub program_content_hits: u64, + pub program_hash_bytes: u64, /// `js_regexp_new` found `(pattern, flags)` in `VALIDATED_PATTERNS`. pub new_validated_hit: u64, /// `js_regexp_new` answered from the literal-site cache (no validation, @@ -349,6 +359,10 @@ impl RegexDiag { use std::fmt::Write as _; let mut out = String::with_capacity(4096); let secs = self.started.map_or(0.0, |t| t.elapsed().as_secs_f64()); + let _ = writeln!(out, "[regex-cache] perex_compiles={} validations={} identity_hits={} content_hits={} hash_bytes={}", + self.perex_compiles, self.perex_validations, self.program_identity_hits, self.program_content_hits, self.program_hash_bytes); + let _ = writeln!(out, "[regex-perex] canonical_execs={} removes={} searches={} scratch_allocs={} scratch_grows={}", + self.perex_canonical_execs, self.perex_removes, self.perex_searches, self.perex_scratch_allocs, self.perex_scratch_grows); let _ = writeln!( out, "[regex-diag] t={secs:.1}s new={} validated_hit={} site_hit={} pattern_bytes={} \ diff --git a/crates/perry-runtime/src/lib.rs b/crates/perry-runtime/src/lib.rs index 264f395c30..ce248fe2b2 100644 --- a/crates/perry-runtime/src/lib.rs +++ b/crates/perry-runtime/src/lib.rs @@ -136,6 +136,7 @@ pub mod net_validate; #[cfg(feature = "node-api-host")] pub mod node_api_host; mod param_type_guard; +mod startup_memory_profile; // #6468: the `node:http2` constant tables are only reachable through the // `http2` native-module namespace, so a program that never imports `node:http2` // links none of them. The auto-optimizer enables `mod-http2-constants` on an @@ -198,6 +199,9 @@ pub mod timer; /// #7469: one `_tlv_get_addr` for the whole allocation hot path. #[doc(hidden)] pub mod tls_hot; +#[cfg(any(target_os = "android", all(test, unix)))] +#[doc(hidden)] +pub mod tls_os_pool; pub mod typed_feedback; pub mod typedarray; pub mod typedarray_half; diff --git a/crates/perry-runtime/src/node_submodules/diagnostics.rs b/crates/perry-runtime/src/node_submodules/diagnostics.rs index f3732ae788..6bd2756a25 100644 --- a/crates/perry-runtime/src/node_submodules/diagnostics.rs +++ b/crates/perry-runtime/src/node_submodules/diagnostics.rs @@ -854,6 +854,10 @@ pub(crate) fn schedule_uncaught(err: f64) { DIAG_PENDING_UNCAUGHT.with(|q| q.borrow_mut().push(err)); } +pub(crate) fn uncaught_work_pending() -> bool { + DIAG_PENDING_UNCAUGHT.with(|queue| !queue.borrow().is_empty()) +} + pub fn diagnostics_channel_drain_uncaught() { if DIAG_SUPPRESS_UNCAUGHT_DRAIN.with(|n| *n.borrow() > 0) { return; diff --git a/crates/perry-runtime/src/object/class_image.rs b/crates/perry-runtime/src/object/class_image.rs index 2d14997426..33e13bce26 100644 --- a/crates/perry-runtime/src/object/class_image.rs +++ b/crates/perry-runtime/src/object/class_image.rs @@ -134,8 +134,10 @@ pub struct ClassImageTables { pub(crate) parents: RwLock>>, /// `parent + 1` for every registered edge whose child id is /// `< PARENT_DENSE_CAP`; `0` means "no edge". Heap-allocated per image - /// (256 KiB) rather than `.bss`, because there is one per image now. - pub(crate) parent_dense: Box<[AtomicU32]>, + /// (256 KiB) on the first edge registration. Images that never register + /// an in-window parent edge answer absent reads without allocating it. + /// Once published, the backing allocation never moves or changes size. + pub(crate) parent_dense: OnceLock>, pub(crate) fetch_parent_kind: RwLock>>, pub(crate) generic_origin: RwLock>>, pub(crate) extends_error: RwLock>>, @@ -161,7 +163,7 @@ impl ClassImageTables { static_method_bind_lengths: RwLock::new(None), registered_class_ids: RwLock::new(None), parents: RwLock::new(None), - parent_dense: (0..PARENT_DENSE_CAP).map(|_| AtomicU32::new(0)).collect(), + parent_dense: OnceLock::new(), fetch_parent_kind: RwLock::new(None), generic_origin: RwLock::new(None), extends_error: RwLock::new(None), @@ -313,18 +315,24 @@ impl ImageTable> { } } -/// One relaxed-ordering load from the calling image's dense parent table. +/// An acquire load from the calling image's dense parent table, if allocated. /// `idx` must be `< PARENT_DENSE_CAP`. #[inline] pub(crate) fn parent_dense_load(idx: usize) -> u32 { - current().parent_dense[idx].load(Ordering::Acquire) + current() + .parent_dense + .get() + .map_or(0, |table| table[idx].load(Ordering::Acquire)) } /// Publish one biased parent edge into the calling image's dense table. /// `idx` must be `< PARENT_DENSE_CAP`. #[inline] pub(crate) fn parent_dense_store(idx: usize, biased_parent: u32) { - current().parent_dense[idx].store(biased_parent, Ordering::Release); + current() + .parent_dense + .get_or_init(|| (0..PARENT_DENSE_CAP).map(|_| AtomicU32::new(0)).collect())[idx] + .store(biased_parent, Ordering::Release); } #[cfg(test)] @@ -332,6 +340,53 @@ mod tests { use super::*; use std::sync::Barrier; + #[test] + fn absent_parent_reads_do_not_allocate_the_dense_table() { + let image = ClassImageHandle(Arc::new(ClassImageTables::new())); + std::thread::spawn(move || { + adopt_image(image); + for index in [0, 1, 1000, PARENT_DENSE_CAP - 1] { + assert_eq!(parent_dense_load(index), 0); + } + assert!(current().parent_dense.get().is_none()); + // A first registration after reads publishes a real edge and + // leaves every other slot absent, including the boundary slots. + parent_dense_store(1000, 1); + assert_eq!(parent_dense_load(1000), 1); + assert_eq!(parent_dense_load(0), 0); + assert_eq!(parent_dense_load(PARENT_DENSE_CAP - 1), 0); + }) + .join() + .unwrap(); + } + + #[test] + fn concurrent_first_parent_registrations_share_one_image_table() { + let image = ClassImageHandle(Arc::new(ClassImageTables::new())); + let barrier = Arc::new(Barrier::new(4)); + let workers: Vec<_> = (0..4) + .map(|index| { + let image = image.clone(); + let barrier = Arc::clone(&barrier); + std::thread::spawn(move || { + adopt_image(image); + barrier.wait(); + parent_dense_store(index, (index + 1) as u32); + barrier.wait(); + for other in 0..4 { + assert_eq!(parent_dense_load(other), (other + 1) as u32); + } + current().parent_dense.get().unwrap().as_ptr() as usize + }) + }) + .collect(); + let tables: Vec<_> = workers + .into_iter() + .map(|worker| worker.join().unwrap()) + .collect(); + assert!(tables.iter().all(|table| *table == tables[0])); + } + /// Register `method` on `class_id` the way codegen's module-init prelude /// does, with `func_ptr` standing in for the image's code address. unsafe fn register_method(class_id: u32, method: &str, func_ptr: usize) { diff --git a/crates/perry-runtime/src/object/class_meta_registry.rs b/crates/perry-runtime/src/object/class_meta_registry.rs index 83c2693d91..267055f050 100644 --- a/crates/perry-runtime/src/object/class_meta_registry.rs +++ b/crates/perry-runtime/src/object/class_meta_registry.rs @@ -32,8 +32,10 @@ pub(crate) static CLASS_REGISTRY: ImageTable> // the window (the reserved builtin bands `0xFFFF_00xx` / `0x7FFF_FFxx` and // the high-bit synthetic ids) keep using the map. // -// The table is one 256 KiB zero-filled allocation per image (#8546: it lives -// in `ClassImageTables::parent_dense`, one per hosted application), reached +// The table is one 256 KiB zero-filled allocation per image, created on its +// first representable edge (#8546: `ClassImageTables::parent_dense`, one per +// hosted application). Reads before registration answer absent without an +// allocation. Initialized reads retain the atomic indexed load, reached // through the same thread-local image resolution as every other class table. // // Encoding: `parent + 1` for every registered edge whose child id is @@ -71,7 +73,7 @@ pub(crate) fn parent_dense_store(class_id: u32, parent_class_id: u32) { /// Look up parent class ID from the registry. /// -/// In-window ids answer from one relaxed-ordering atomic load. Everything else +/// In-window ids answer from an acquire atomic load once initialized. Everything else /// (builtin reserved bands, synthetic high-bit ids) falls back to the locked /// map, exactly as before. #[inline] diff --git a/crates/perry-runtime/src/object/exotic_expando.rs b/crates/perry-runtime/src/object/exotic_expando.rs index ff1f71f90e..1e54921a13 100644 --- a/crates/perry-runtime/src/object/exotic_expando.rs +++ b/crates/perry-runtime/src/object/exotic_expando.rs @@ -124,6 +124,19 @@ pub(crate) fn expando_in_use() -> bool { crate::state::state().exotic_expando.in_use.get() } +/// Plain assignments on exotic receivers can live here without an ObjectMeta. +/// Builtin-operation guards must check this table as well as the header. +#[cfg(feature = "regex-engine")] +pub(crate) fn has_expando_values(addr: usize) -> bool { + let tables = &crate::state::state().exotic_expando; + tables.in_use.get() + && tables + .entries + .borrow() + .get(&addr) + .is_some_and(|values| !values.is_empty()) +} + fn expando_store(addr: usize, key: &str, bits: u64) { let tables = &crate::state::state().exotic_expando; tables.in_use.set(true); diff --git a/crates/perry-runtime/src/object/global_this_webassembly.rs b/crates/perry-runtime/src/object/global_this_webassembly.rs index d8c0a8be01..cc4696695d 100644 --- a/crates/perry-runtime/src/object/global_this_webassembly.rs +++ b/crates/perry-runtime/src/object/global_this_webassembly.rs @@ -1018,7 +1018,6 @@ pub(super) fn create_webassembly_namespace() -> f64 { 1, true, ); - crate::closure::js_register_closure_arity(webassembly_instantiate_thunk as *const u8, 2); install_webassembly_static_fn( ns_obj, "validate", @@ -1033,6 +1032,9 @@ pub(super) fn create_webassembly_namespace() -> f64 { 1, true, ); + // The optional imports object is a real second call argument, while + // `WebAssembly.instantiate.length` stays 1. + crate::closure::js_register_closure_arity(webassembly_instantiate_thunk as *const u8, 2); install_webassembly_static_fn( ns_obj, "compileStreaming", @@ -1409,6 +1411,11 @@ mod tests { let grow = js_object_get_field_by_name_f64(memory_proto, named_key(b"grow")); let grow_jv = crate::value::JSValue::from_bits(grow.to_bits()); assert!(grow_jv.is_pointer(), "Memory.prototype.grow must exist"); + assert_eq!( + crate::closure::lookup_closure_arity(webassembly_instantiate_thunk as *const u8), + Some(2), + "the install helper must not overwrite instantiate's two-argument dispatch" + ); } #[cfg(not(feature = "wasm-host"))] diff --git a/crates/perry-runtime/src/object/mod.rs b/crates/perry-runtime/src/object/mod.rs index e15899d4e5..2460f50b2f 100644 --- a/crates/perry-runtime/src/object/mod.rs +++ b/crates/perry-runtime/src/object/mod.rs @@ -171,6 +171,8 @@ mod prototype_helpers; mod reflect_support; mod reserved_floor; pub(crate) use reserved_floor::{ensure_reserved_floor_keys, reserved_slot_floor_for_class_id}; +#[cfg(feature = "regex-engine")] +pub(crate) mod regex_canonical; pub(crate) mod regex_proto_thunks; // #6812 object-owned overflow storage + the legacy thread-local side table. // Split out of this file to stay under the 2000-line CI cap; the sibling diff --git a/crates/perry-runtime/src/object/regex_canonical.rs b/crates/perry-runtime/src/object/regex_canonical.rs new file mode 100644 index 0000000000..44860c49e2 --- /dev/null +++ b/crates/perry-runtime/src/object/regex_canonical.rs @@ -0,0 +1,162 @@ +//! Non-observable admission for builtin RegExp operations. ShapeId guards the +//! own key/descriptor layout, an indexed load guards exec's current value, and +//! the existing symbol epoch guards @@replace. No getter is invoked here. +use super::{regex_proto_thunks as thunks, ObjectHeader}; +use crate::regex::RegExpHeader; +use crate::value::{js_nanbox_pointer, JSValue}; +use std::cell::Cell; +use std::sync::atomic::Ordering; + +#[derive(Clone, Copy, Default)] +struct Proof { + shape: u32, + exec_index: Option, + flags: bool, + symbol_epoch: u64, + replace: bool, +} +crate::perry_thread_local! { + // ShapeIds are immutable scalar identities. This record holds no GC edge. + static PROOF: Cell = Cell::new(Proof::default()); +} + +fn native(value: f64, function: *const u8) -> bool { + if !super::is_callable_function_value(value) { + return false; + } + let closure = + crate::value::js_nanbox_get_pointer(value) as *const crate::closure::ClosureHeader; + crate::closure::js_closure_get_func(closure) == function +} + +fn field_index(proto: *mut ObjectHeader, name: &[u8]) -> Option { + let keys = unsafe { super::object_keys_array(proto) }; + if keys.is_null() { + return None; + } + (0..crate::array::js_array_length(keys)).find(|&i| unsafe { + crate::string::js_string_key_matches_bytes( + JSValue::from_bits(crate::array::js_array_get_f64(keys, i).to_bits()), + name, + ) + }) +} + +fn refresh(proto: *mut ObjectHeader, shape: u32) -> Proof { + let exec_index = if super::get_accessor_descriptor(proto as usize, "exec").is_none() { + field_index(proto, b"exec") + } else { + None + }; + let flags = [ + ("flags", thunks::regex_proto_flags_getter as *const u8), + ("global", thunks::regex_proto_global_getter as *const u8), + ( + "ignoreCase", + thunks::regex_proto_ignore_case_getter as *const u8, + ), + ( + "multiline", + thunks::regex_proto_multiline_getter as *const u8, + ), + ("dotAll", thunks::regex_proto_dot_all_getter as *const u8), + ("sticky", thunks::regex_proto_sticky_getter as *const u8), + ("unicode", thunks::regex_proto_unicode_getter as *const u8), + ( + "unicodeSets", + thunks::regex_proto_unicode_sets_getter as *const u8, + ), + ( + "hasIndices", + thunks::regex_proto_has_indices_getter as *const u8, + ), + ] + .iter() + .all(|&(name, fp)| { + super::get_accessor_descriptor(proto as usize, name) + .is_some_and(|accessor| native(f64::from_bits(accessor.get), fp)) + }); + Proof { + shape, + exec_index, + flags, + ..Proof::default() + } +} + +/// Only an untouched RegExp receiver is admitted. Metadata would permit own +/// overrides, descriptors or a custom prototype and takes the generic path. +pub(crate) fn exec(value: f64) -> bool { + let receiver = JSValue::from_bits(value.to_bits()); + if !receiver.is_pointer() { + return false; + } + let re = receiver.as_pointer::(); + if !crate::regex::is_valid_regex_ptr(re) + || unsafe { !(*re).meta.is_null() } + || super::exotic_expando::has_expando_values(re as usize) + { + return false; + } + if super::prototype_chain::object_static_prototype_known_non_meta(re as usize).is_some() { + return false; + } + let proto = thunks::recorded_regexp_prototype(); + if proto.is_null() { + return false; + } + let shape = unsafe { super::shapes::object_shape_stamp(proto) }; + if shape == 0 { + return false; + } + PROOF.with(|cell| { + let mut proof = cell.get(); + if proof.shape != shape { + proof = refresh(proto, shape); + cell.set(proof); + } + proof.exec_index.is_some_and(|index| { + native( + f64::from_bits(super::js_object_get_field(proto, index).bits()), + thunks::regex_proto_exec_thunk as *const u8, + ) + }) + }) +} + +pub(crate) fn replace(value: f64) -> bool { + if !exec(value) { + return false; + } + let symbol = crate::symbol::well_known_symbol_if_cached("replace"); + if symbol.is_null() { + return false; + } + let key = js_nanbox_pointer(symbol as i64); + if unsafe { crate::symbol::js_object_has_own_symbol_property(value, key) } { + return false; + } + PROOF.with(|cell| { + let mut proof = cell.get(); + if !proof.flags { + return false; + } + let epoch = crate::symbol::PERRY_SYMBOL_PROPERTY_IC_EPOCH.load(Ordering::Acquire); + if proof.symbol_epoch != epoch { + let proto = thunks::recorded_regexp_prototype(); + proof.replace = + crate::symbol::symbol_accessor_descriptor_bits(proto as usize, symbol as usize) + .is_none() + && crate::symbol::symbol_property_root_bits(proto as usize, symbol as usize) + .is_some_and(|v| { + native( + f64::from_bits(v), + crate::regex::perex_replace::regexp_thunk as *const u8, + ) + }); + proof.symbol_epoch = epoch; + cell.set(proof); + } + proof.replace + }) +} diff --git a/crates/perry-runtime/src/object/regex_proto_thunks.rs b/crates/perry-runtime/src/object/regex_proto_thunks.rs index 1d1203ba95..fbe0b20f49 100644 --- a/crates/perry-runtime/src/object/regex_proto_thunks.rs +++ b/crates/perry-runtime/src/object/regex_proto_thunks.rs @@ -405,6 +405,23 @@ pub(crate) fn test_recorded_regexp_prototype() -> i64 { .with(|site| site.prototype.load(std::sync::atomic::Ordering::Acquire)) } +#[cfg(feature = "regex-engine")] +pub(super) fn recorded_regexp_prototype() -> *mut ObjectHeader { + REGEXP_PROTOTYPE_TEST_SITE + .with(|site| site.prototype.load(std::sync::atomic::Ordering::Acquire) as *mut ObjectHeader) +} + +#[cfg(all(test, feature = "regex-engine"))] +pub(crate) fn test_clear_canonical_site() { + REGEXP_PROTOTYPE_TEST_SITE.with(|site| { + site.prototype + .store(0, std::sync::atomic::Ordering::Release); + site.closure.store(0, std::sync::atomic::Ordering::Release); + site.index + .store(u32::MAX, std::sync::atomic::Ordering::Release); + }); +} + /// How many by-name walks the canonicality proof has done in this process. /// The fast path does none: the only walk is the one-time recording below, so /// this must read **1 per realm**, not one per call. It is the counter that diff --git a/crates/perry-runtime/src/os.rs b/crates/perry-runtime/src/os.rs index 596a6dfaf8..7d314e4786 100644 --- a/crates/perry-runtime/src/os.rs +++ b/crates/perry-runtime/src/os.rs @@ -839,6 +839,7 @@ pub use signal::{js_process_kill, js_util_convert_process_signal_to_exit_code}; #[path = "os_process_streams.rs"] mod process_streams; +pub(crate) use process_streams::process_stdin_needs_pump; #[cfg(test)] pub(crate) use process_streams::test_set_stdin_data_listener; pub use process_streams::{ diff --git a/crates/perry-runtime/src/os_process_streams.rs b/crates/perry-runtime/src/os_process_streams.rs index de4597775d..e6e585f8e5 100644 --- a/crates/perry-runtime/src/os_process_streams.rs +++ b/crates/perry-runtime/src/os_process_streams.rs @@ -1236,6 +1236,36 @@ pub fn pump_process_stdin() { maybe_fire_stdin_end(); } +/// An active reader may publish bytes between queue checks. Conservatively +/// keep its ordinary timer phase, including EOF delivery after the reader +/// has exited. `stdin_push_bytes` can also populate the buffer without one. +pub(crate) fn process_stdin_needs_pump() -> bool { + use std::sync::atomic::Ordering; + STDIN_READER_STARTED.load(Ordering::Acquire) + || (STDIN_EOF_SEEN.load(Ordering::Acquire) && !STDIN_END_FIRED.load(Ordering::Acquire)) + || STDIN_BUFFER.lock().map(|b| !b.is_empty()).unwrap_or(true) +} + +#[cfg(test)] +mod empty_checkpoint_tests { + use super::*; + + #[test] + fn reader_and_undelivered_eof_require_the_stdin_phase() { + use std::sync::atomic::Ordering; + assert!(!process_stdin_needs_pump()); + STDIN_READER_STARTED.store(true, Ordering::Release); + assert!(process_stdin_needs_pump()); + STDIN_READER_STARTED.store(false, Ordering::Release); + STDIN_EOF_SEEN.store(true, Ordering::Release); + assert!(process_stdin_needs_pump()); + STDIN_END_FIRED.store(true, Ordering::Release); + assert!(!process_stdin_needs_pump()); + STDIN_EOF_SEEN.store(false, Ordering::Release); + STDIN_END_FIRED.store(false, Ordering::Release); + } +} + fn pump_stdin_data_chunks() { let has_bytes = STDIN_BUFFER.lock().map(|b| !b.is_empty()).unwrap_or(false); if !has_bytes { diff --git a/crates/perry-runtime/src/perf_hooks.rs b/crates/perry-runtime/src/perf_hooks.rs index 68690d9d80..862b9bb1fb 100644 --- a/crates/perry-runtime/src/perf_hooks.rs +++ b/crates/perry-runtime/src/perf_hooks.rs @@ -1876,6 +1876,27 @@ pub(crate) fn test_perf_entry_keys_array() -> usize { PERF_ENTRY_KEYS_ARRAY.with(|slot| slot.get()) } +#[cfg(test)] +mod empty_checkpoint_tests { + use super::*; + + #[test] + fn empty_checkpoint_sets_loop_start_once() { + std::thread::spawn(|| { + crate::gc::js_gc_init(); + assert_eq!(LOOP_START_MS.with(|slot| slot.get()), -1.0); + assert!(crate::promise::microtasks::empty_checkpoint_eligible_for_test()); + crate::promise::js_promise_run_microtasks_event_loop(); + let started = LOOP_START_MS.with(|slot| slot.get()); + assert!(started >= 0.0); + crate::promise::js_promise_run_microtasks_event_loop(); + assert_eq!(LOOP_START_MS.with(|slot| slot.get()), started); + }) + .join() + .unwrap(); + } +} + #[cfg(test)] mod sso_tests_1781 { use super::*; diff --git a/crates/perry-runtime/src/promise/microtasks.rs b/crates/perry-runtime/src/promise/microtasks.rs index 78ecd46c4e..fc5e035631 100644 --- a/crates/perry-runtime/src/promise/microtasks.rs +++ b/crates/perry-runtime/src/promise/microtasks.rs @@ -112,6 +112,30 @@ pub extern "C" fn js_promise_run_microtasks_event_loop() -> i32 { run_microtasks(MicrotaskDrainMode::EventLoop) } +/// The entry's checkpoint after beforeExit. Finish ticks, promise jobs, and +/// rejection reporting before deciding whether the listener created another +/// event-loop turn. Leave timers and foreign completions for that turn. +#[no_mangle] +pub extern "C" fn js_promise_run_before_exit_checkpoint() -> i32 { + let mut ran = 0; + loop { + ran += run_microtasks(MicrotaskDrainMode::BeforeExit); + // A rejection listener can queue more jobs after the ordinary job + // drain. Finish those here too; microtasks alone must not re-emit + // beforeExit as if a new timer/I/O turn had occurred. + if TASK_QUEUE.with(|queue| queue.borrow().is_empty()) + && !crate::builtins::queued_microtasks_pending() + { + return ran; + } + } +} + +#[cfg(feature = "keepalive-anchors")] +#[used] +static KEEP_PROMISE_RUN_BEFORE_EXIT_CHECKPOINT: extern "C" fn() -> i32 = + js_promise_run_before_exit_checkpoint; + // The entry event loop is generated code, so nothing in the Rust runtime // references this symbol — anchor it like the other codegen-only hooks so the // auto-optimize internalize+dead-strip pass can't drop it (#4876). @@ -152,6 +176,9 @@ enum MicrotaskDrainMode { /// drain and the timer queues (#6077). Reserved for the compiled entry's /// event loop — see `js_promise_run_microtasks_event_loop`. EventLoop, + /// Entry-only tick/promise/rejection checkpoint after beforeExit; foreign + /// callbacks and timers stay pending for the next event-loop iteration. + BeforeExit, MicrotasksOnly, /// Promise/queueMicrotask jobs only — no nextTick drain, no timers. PromiseJobsOnly, @@ -213,6 +240,28 @@ fn rooted_closure(h: &crate::gc::RuntimeHandle<'_>) -> ClosurePtr { fn run_microtasks(mode: MicrotaskDrainMode) -> i32 { mt_profile_register(); bump(&MT_DRAIN_COUNT); + if matches!( + mode, + MicrotaskDrainMode::EventLoop + | MicrotaskDrainMode::BeforeExit + | MicrotaskDrainMode::PromiseJobsOnly + ) && empty::can_skip_callback_phases() + { + // No callback can run in this checkpoint, so no exception trap or + // async execution-reference stack is needed. The GC/box boundary is + // still required: synchronous allocation can leave collection work + // pending even though every callback queue is empty. + MICROTASK_RUN_DEPTH.with(|depth| depth.set(MicrotaskRunDepths { pump: 1, jobs: 0 })); + // The ordinary callback-timer phase stamps this even with no timers. + // Keep nodeTiming.loopStart/eventLoopUtilization observable on beforeExit. + if matches!(mode, MicrotaskDrainMode::EventLoop) { + crate::perf_hooks::note_event_loop_start(); + } + finish_gc_and_box_boundary(); + MICROTASK_RUN_DEPTH.with(|depth| depth.set(MicrotaskRunDepths { pump: 0, jobs: 0 })); + bump(&MT_EMPTY_DRAIN_COUNT); + return 0; + } let async_box_ref_depth = u32::try_from(async_box_execution_ref_depth()) .expect("microtask execution-ref depth overflow"); ASYNC_BOX_EXECUTION_REF_BASES.with(|bases| bases.borrow_mut().push(async_box_ref_depth)); @@ -226,7 +275,9 @@ fn run_microtasks(mode: MicrotaskDrainMode) -> i32 { }); let mut ran = 0; - ran += crate::bun_ffi::callback::drain_threadsafe_callbacks(); + if !matches!(mode, MicrotaskDrainMode::BeforeExit) { + ran += crate::bun_ffi::callback::drain_threadsafe_callbacks(); + } ran += crate::async_hooks::drain_gc_destroy_queue(); @@ -238,16 +289,20 @@ fn run_microtasks(mode: MicrotaskDrainMode) -> i32 { ran += crate::weakref::drain_pending_finalization_jobs(); // Native async tokens settle only through the main-thread handoff path. - ran += super::native_async::js_native_async_process_pending(); + if !matches!(mode, MicrotaskDrainMode::BeforeExit) { + ran += super::native_async::js_native_async_process_pending(); + } // Process any scheduled resolutions (simulates async completions) ran += super::combinators::process_scheduled_resolves(); // Process diagnostics_channel publishes queued by perry/thread workers. - ran += crate::node_submodules::diagnostics_channel_process_pending(); + if !matches!(mode, MicrotaskDrainMode::BeforeExit) { + ran += crate::node_submodules::diagnostics_channel_process_pending(); - // Process pending thread results (from perry/thread spawn) - ran += crate::thread::js_thread_process_pending(); + // Process pending thread results (from perry/thread spawn) + ran += crate::thread::js_thread_process_pending(); + } // Then process the task queue. // @@ -302,6 +357,33 @@ fn run_microtasks(mode: MicrotaskDrainMode) -> i32 { crate::exception::js_try_end(); crate::node_submodules::diagnostics_channel_drain_uncaught(); + finish_gc_and_box_boundary(); + + ASYNC_BOX_EXECUTION_REF_BASES.with(|bases| { + let base = bases + .borrow_mut() + .pop() + .expect("microtask execution-ref boundary"); + debug_assert_eq!(async_box_execution_ref_depth(), base as usize); + }); + + MICROTASK_RUN_DEPTH.with(|depth| { + let mut current = depth.get(); + current.pump = current.pump.saturating_sub(1); + depth.set(current); + }); + + ran +} + +mod empty; + +#[cfg(test)] +pub(crate) fn empty_checkpoint_eligible_for_test() -> bool { + empty::can_skip_callback_phases() +} + +fn finish_gc_and_box_boundary() { let _ = crate::gc::gc_runtime_safepoint(); // Phase 1 of the moving-GC project (see project_gc_one_great_moving_gc): at @@ -326,22 +408,6 @@ fn run_microtasks(mode: MicrotaskDrainMode) -> i32 { { crate::r#box::flush_released_boxes(); } - - ASYNC_BOX_EXECUTION_REF_BASES.with(|bases| { - let base = bases - .borrow_mut() - .pop() - .expect("microtask execution-ref boundary"); - debug_assert_eq!(async_box_execution_ref_depth(), base as usize); - }); - - MICROTASK_RUN_DEPTH.with(|depth| { - let mut current = depth.get(); - current.pump = current.pump.saturating_sub(1); - depth.set(current); - }); - - ran } /// The microtask trap's protected region (#9305): the recovery for a @@ -1123,7 +1189,11 @@ fn pump_protected(mode: MicrotaskDrainMode, reentrant: bool, landed: bool, ran: // Only the codegen event-loop pump qualifies (see the doc comment on // `js_promise_run_microtasks_event_loop`); a nested drain is not a // checkpoint boundary. - if matches!(mode, MicrotaskDrainMode::EventLoop) && !reentrant { + if matches!( + mode, + MicrotaskDrainMode::EventLoop | MicrotaskDrainMode::BeforeExit + ) && !reentrant + { super::rejection::process_rejections(); } diff --git a/crates/perry-runtime/src/promise/microtasks/empty.rs b/crates/perry-runtime/src/promise/microtasks/empty.rs new file mode 100644 index 0000000000..2694714452 --- /dev/null +++ b/crates/perry-runtime/src/promise/microtasks/empty.rs @@ -0,0 +1,241 @@ +//! Conservative eligibility for an outer entry checkpoint with no callbacks. +//! This is separate from event-loop liveness: unref/cleared timer entries still +//! need their normal phase, as do rejection reports without a queued Promise. + +use super::*; + +pub(super) fn can_skip_callback_phases() -> bool { + if MICROTASK_RUN_DEPTH.with(|depth| depth.get().pump != 0 || depth.get().jobs != 0) + || ESM_EVAL_CHECKPOINT_PENDING.with(|pending| pending.get()) + || async_box_execution_ref_depth() != 0 + { + return false; + } + if TASK_QUEUE.with(|queue| !queue.borrow().is_empty()) + || crate::builtins::queued_microtasks_pending() + || super::super::combinators::SCHEDULED_RESOLVES.with(|queue| !queue.borrow().is_empty()) + || super::super::rejection::checkpoint_work_pending() + || crate::weakref::pending_finalization_jobs_count() != 0 + { + return false; + } + // The foreign queues are checked under their own locks. Future arrivals + // retain their existing active-handle/wakeup contracts; a callback arriving + // after this check is handled by the next pump, just as one arriving after + // the ordinary drain. Conservatively use the full path for active workers. + !(super::super::native_async::completion_work_pending() + || crate::thread::js_thread_has_pending() != 0 + || crate::bun_ffi::callback::threadsafe_callback_work_pending() + || crate::async_hooks::gc_destroy_work_pending() + || crate::node_submodules::diagnostics_channel_has_pending_publishes() + || crate::node_submodules::diagnostics::uncaught_work_pending() + || crate::os::process_stdin_needs_pump() + || crate::timer::timer_phase_work_pending()) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn isolated(test: impl FnOnce() + Send + 'static) { + let result = std::thread::spawn(|| { + crate::gc::js_gc_init(); + test(); + }) + .join(); + // The thread isolates thread-local state only. `js_gc_init` also + // disables the process-wide class-field inline gate in test builds; + // restore it so later tests do not inherit this test's init. + crate::object::descriptor_state::test_reset_class_field_inline_guard(); + result.unwrap(); + } + + #[test] + fn empty_checkpoint_avoids_execution_stack_allocation() { + isolated(|| { + assert!(can_skip_callback_phases()); + assert_eq!(js_promise_run_microtasks_event_loop(), 0); + assert_eq!(js_promise_run_before_exit_checkpoint(), 0); + assert_eq!(js_promise_run_promise_jobs(), 0); + // The full runner's push allocates this Vec. Verify the branch + // actually runs, independently of testing the guard itself. + ASYNC_BOX_EXECUTION_REF_BASES.with(|bases| assert_eq!(bases.borrow().capacity(), 0)); + assert!(!microtask_drain_active()); + assert!(!microtask_job_drain_active()); + }); + } + + #[test] + fn esm_evaluation_checkpoint_uses_full_path_once() { + isolated(|| { + js_mark_entry_module_esm(); + assert!(!can_skip_callback_phases()); + assert_eq!(js_promise_run_microtasks_event_loop(), 0); + assert!(can_skip_callback_phases()); + }); + } + + #[test] + fn queued_microtask_runs_and_restores_quiescence() { + isolated(|| { + static CALLED: AtomicU64 = AtomicU64::new(0); + extern "C" fn callback(_: *const crate::closure::ClosureHeader) -> f64 { + CALLED.fetch_add(1, Ordering::Relaxed); + 0.0 + } + crate::closure::js_register_closure_arity(callback as *const u8, 0); + let callback = crate::closure::js_closure_alloc(callback as *const u8, 0); + crate::builtins::js_queue_microtask(callback as i64); + assert!(!can_skip_callback_phases()); + assert!(js_promise_run_microtasks_event_loop() > 0); + assert_eq!(CALLED.load(Ordering::Relaxed), 1); + assert!(!microtask_drain_active()); + assert!(!microtask_job_drain_active()); + }); + } + + #[test] + fn scheduled_resolution_without_a_task_is_not_empty() { + isolated(|| { + let promise = crate::promise::js_promise_new(); + let roots = crate::gc::RuntimeHandleScope::new(); + let promise = roots.root_nanbox_f64(crate::value::js_nanbox_pointer(promise as i64)); + crate::promise::js_promise_schedule_resolve( + crate::value::js_nanbox_get_pointer(promise.get_nanbox_f64()) as *mut Promise, + 13.0, + ); + assert!(!can_skip_callback_phases()); + assert!(js_promise_run_microtasks_event_loop() > 0); + let promise = + crate::value::js_nanbox_get_pointer(promise.get_nanbox_f64()) as *mut Promise; + assert_eq!(crate::promise::js_promise_state(promise), 1); + assert_eq!(crate::promise::js_promise_result(promise), 13.0); + }); + } + + #[test] + fn rejection_without_a_task_requires_checkpoint() { + isolated(|| { + let promise = crate::promise::js_promise_new(); + crate::promise::js_promise_reject(promise, 23.0); + assert!(TASK_QUEUE.with(|queue| queue.borrow().is_empty())); + assert!(!can_skip_callback_phases()); + crate::promise::js_promise_mark_internally_handled(promise); + assert!(!super::super::super::rejection::checkpoint_work_pending()); + }); + } + + #[test] + fn unref_timer_still_requires_its_phase() { + isolated(|| { + crate::timer::js_set_timeout_value_ref(1_000_000.0, 0.0, 0); + assert_eq!(crate::timer::js_timer_has_pending(), 0); + assert!(!can_skip_callback_phases()); + crate::timer::purge_agent_timers(crate::agent::current_agent()); + assert!(!crate::timer::timer_phase_work_pending()); + }); + } + + #[test] + fn gc_destroy_jobs_are_not_skipped() { + isolated(|| { + crate::async_hooks::enqueue_gc_destroy(u64::MAX - 1); + assert!(!can_skip_callback_phases()); + assert!(js_promise_run_microtasks_event_loop() > 0); + assert!(!crate::async_hooks::gc_destroy_work_pending()); + }); + } + + #[test] + fn native_completion_arriving_after_empty_checkpoint_is_delivered() { + isolated(|| { + use crate::promise::native_async::*; + let _guard = test_native_async_lock(); + test_reset_native_async_registry(); + assert_eq!(js_promise_run_microtasks_event_loop(), 0); + let token = js_native_async_completion_new(0); + let promise = js_native_async_completion_promise(token); + let token_addr = token as usize; + std::thread::spawn(move || { + assert_eq!( + js_native_async_completion_resolve_bits( + token_addr as *mut NativeAsyncCompletion, + 37.0f64.to_bits(), + ), + PERRY_NATIVE_ASYNC_OK, + ); + }) + .join() + .unwrap(); + assert!(!can_skip_callback_phases()); + assert_eq!(js_promise_run_before_exit_checkpoint(), 0); + assert_eq!(crate::promise::js_promise_state(promise), 0); + assert!(js_promise_run_microtasks_event_loop() > 0); + assert_eq!(crate::promise::js_promise_state(promise), 1); + assert_eq!(crate::promise::js_promise_value(promise), 37.0); + test_reset_native_async_registry(); + }); + } + + #[test] + fn buffered_stdin_is_delivered_without_any_timer() { + isolated(|| { + static CALLED: AtomicU64 = AtomicU64::new(0); + extern "C" fn callback(_: *const crate::closure::ClosureHeader, _: f64) -> f64 { + CALLED.fetch_add(1, Ordering::Relaxed); + 0.0 + } + crate::closure::js_register_closure_arity(callback as *const u8, 1); + let callback = crate::closure::js_closure_alloc(callback as *const u8, 0); + crate::os::test_set_stdin_data_listener(Some(callback as i64)); + crate::os::stdin_push_bytes(b"input"); + assert!(!crate::timer::timer_phase_work_pending()); + assert!(!can_skip_callback_phases()); + js_promise_run_microtasks_event_loop(); + assert_eq!(CALLED.load(Ordering::Relaxed), 1); + assert!(!crate::os::process_stdin_needs_pump()); + crate::os::test_set_stdin_data_listener(None); + }); + } + + #[test] + fn before_exit_checkpoint_preserves_timer_for_the_next_turn() { + isolated(|| { + let scope = crate::gc::RuntimeHandleScope::new(); + let promise = scope.root_nanbox_f64(crate::value::js_nanbox_pointer( + crate::timer::js_set_timeout_value_ref(0.0, 47.0, 1) as i64, + )); + assert_eq!(js_promise_run_before_exit_checkpoint(), 0); + assert_eq!( + crate::promise::js_promise_state(rooted_promise(&promise)), + 0 + ); + assert_eq!(crate::timer::js_timer_has_pending(), 1); + assert!(js_promise_run_microtasks_event_loop() > 0); + assert_eq!( + crate::promise::js_promise_value(rooted_promise(&promise)), + 47.0 + ); + }); + } + + #[test] + fn exit_promise_checkpoint_leaves_ticks_forbidden() { + isolated(|| { + static CALLED: AtomicU64 = AtomicU64::new(0); + extern "C" fn callback(_: *const crate::closure::ClosureHeader) -> f64 { + CALLED.fetch_add(1, Ordering::Relaxed); + 0.0 + } + crate::closure::js_register_closure_arity(callback as *const u8, 0); + let callback = crate::closure::js_closure_alloc(callback as *const u8, 0); + crate::builtins::js_queue_next_tick(callback as i64); + assert_eq!(js_promise_run_promise_jobs(), 0); + assert_eq!(CALLED.load(Ordering::Relaxed), 0); + assert!(crate::builtins::queued_microtasks_pending()); + assert!(js_promise_run_before_exit_checkpoint() > 0); + assert_eq!(CALLED.load(Ordering::Relaxed), 1); + assert!(!crate::builtins::queued_microtasks_pending()); + }); + } +} diff --git a/crates/perry-runtime/src/promise/mod.rs b/crates/perry-runtime/src/promise/mod.rs index 51254b37d3..5f93d06ba9 100644 --- a/crates/perry-runtime/src/promise/mod.rs +++ b/crates/perry-runtime/src/promise/mod.rs @@ -53,7 +53,10 @@ pub use combinators::{ js_promise_all_settled, js_promise_any, js_promise_new_with_executor, js_promise_race, js_promise_rejected, js_promise_schedule_resolve, js_promise_try, js_value_is_promise, }; -pub use microtasks::{js_promise_run_microtasks, js_promise_run_microtasks_event_loop}; +pub use microtasks::{ + js_promise_run_before_exit_checkpoint, js_promise_run_microtasks, + js_promise_run_microtasks_event_loop, +}; pub use native_async::{ js_native_async_completion_attach_handle, js_native_async_completion_cancel, js_native_async_completion_new, js_native_async_completion_promise, @@ -150,6 +153,7 @@ pub(crate) fn is_definitely_primitive(value: f64) -> bool { // Instrumentation counters (set PERRY_MT_PROFILE=1 to print at exit). pub static MT_RUN_COUNT: AtomicU64 = AtomicU64::new(0); pub static MT_DRAIN_COUNT: AtomicU64 = AtomicU64::new(0); +pub static MT_EMPTY_DRAIN_COUNT: AtomicU64 = AtomicU64::new(0); pub static MT_THENABLE_PROBE_COUNT: AtomicU64 = AtomicU64::new(0); pub static MT_PROMISE_NEW_COUNT: AtomicU64 = AtomicU64::new(0); pub static MT_PROMISE_THEN_COUNT: AtomicU64 = AtomicU64::new(0); @@ -207,6 +211,10 @@ extern "C" fn mt_profile_atexit() { if std::env::var_os("PERRY_MT_PROFILE").is_none() { return; } + eprintln!( + "[mt-profile] empty_drains={}", + MT_EMPTY_DRAIN_COUNT.load(Ordering::Relaxed) + ); eprintln!( "[mt-profile] runs={} resolved={} then={} new={} unwrap={} thenable_probe={} thenable_fast={}", MT_RUN_COUNT.load(Ordering::Relaxed), diff --git a/crates/perry-runtime/src/promise/native_async.rs b/crates/perry-runtime/src/promise/native_async.rs index 98264fbc76..5ac8ffe2e7 100644 --- a/crates/perry-runtime/src/promise/native_async.rs +++ b/crates/perry-runtime/src/promise/native_async.rs @@ -108,6 +108,14 @@ fn registry() -> &'static Mutex { REGISTRY.get_or_init(|| Mutex::new(NativeAsyncRegistry::default())) } +pub(super) fn completion_work_pending() -> bool { + REGISTRY.get().is_some_and(|registry| { + !crate::gc::lock_gc_root_registry(registry) + .pending + .is_empty() + }) +} + fn current_thread_id() -> u64 { let mut hasher = std::collections::hash_map::DefaultHasher::new(); std::thread::current().id().hash(&mut hasher); diff --git a/crates/perry-runtime/src/promise/rejection.rs b/crates/perry-runtime/src/promise/rejection.rs index 1233d7d824..459e1a587a 100644 --- a/crates/perry-runtime/src/promise/rejection.rs +++ b/crates/perry-runtime/src/promise/rejection.rs @@ -77,6 +77,13 @@ struct RejectionTracker { /// `'rejectionHandled'` if a handler shows up thousands of rejections later. const MAX_REPORTED_REJECTIONS: usize = 1024; +pub(super) fn checkpoint_work_pending() -> bool { + REJECTIONS.with(|tracker| { + let tracker = tracker.borrow(); + !tracker.unhandled.is_empty() || !tracker.pending_handled.is_empty() + }) +} + /// Mark a promise as internally handled (Node's `markPromiseAsHandled`): a /// later rejection of it is never reported as unhandled. Used by the WHATWG /// stream implementation for the internal `closed` / `closeRequest` promises it diff --git a/crates/perry-runtime/src/proxy.rs b/crates/perry-runtime/src/proxy.rs index 7c3761159e..8c2c6dbd2a 100644 --- a/crates/perry-runtime/src/proxy.rs +++ b/crates/perry-runtime/src/proxy.rs @@ -2825,6 +2825,10 @@ mod tests { /// stale typed-layout state must all reject. #[test] fn object_array_numeric_write_guard_requires_complete_uniform_proof() { + // This proof honours the process-wide class-field inline gate, and any + // earlier test that ran `js_gc_init` (typed feedback is always on in + // test builds) leaves that gate disabled. Establish the premise. + crate::object::descriptor_state::test_reset_class_field_inline_guard(); let packed = b"a\0b\0c\0d\0"; let keys = crate::object::js_build_class_keys_array( 0x6809_01, @@ -3114,6 +3118,10 @@ mod tests { /// probe and the objects it inspects (#7635). #[test] fn json_parse_receivers_are_admitted_to_the_whole_loop_write_clone() { + // This proof honours the process-wide class-field inline gate, and any + // earlier test that ran `js_gc_init` (typed feedback is always on in + // test builds) leaves that gate disabled. Establish the premise. + crate::object::descriptor_state::test_reset_class_field_inline_guard(); let src = br#"{"x":0,"y":0}"#; let mut receivers = Vec::new(); for _ in 0..4 { diff --git a/crates/perry-runtime/src/regex.rs b/crates/perry-runtime/src/regex.rs index 740cc5ea15..d4eeda3326 100644 --- a/crates/perry-runtime/src/regex.rs +++ b/crates/perry-runtime/src/regex.rs @@ -28,6 +28,8 @@ pub(crate) mod match_all; #[cfg(feature = "regex-engine")] pub(crate) mod perex_api; #[cfg(feature = "regex-engine")] +pub(crate) mod perex_cache; +#[cfg(feature = "regex-engine")] mod perex_construct; #[cfg(feature = "regex-engine")] pub(crate) mod perex_dispatch; @@ -48,6 +50,8 @@ pub(crate) mod perex_owner; #[cfg(feature = "regex-engine")] pub(crate) mod perex_position_hint; #[cfg(feature = "regex-engine")] +mod perex_remove; +#[cfg(feature = "regex-engine")] pub(crate) mod perex_replace; #[cfg(feature = "regex-engine")] pub(crate) mod perex_replace_direct; @@ -525,8 +529,11 @@ pub const REGEXP_MAGIC: u64 = 0x5845_4745_5259_5250; /// clamped to ≥ 0. #[cfg(feature = "regex-engine")] pub(crate) fn regex_last_index_offset(re: *const RegExpHeader) -> usize { - let scope = crate::gc::RuntimeHandleScope::new(); let stored = f64::from_bits(unsafe { (*re).last_index }); + if crate::value::JSValue::from_bits(stored.to_bits()).is_number() { + return stored.max(0.0).floor().min(9_007_199_254_740_991.0) as usize; + } + let scope = crate::gc::RuntimeHandleScope::new(); let stored = scope.root_nanbox_f64(stored); perex_api::finish(perex_dispatch::to_length(&stored)) as usize } diff --git a/crates/perry-runtime/src/regex/flags.rs b/crates/perry-runtime/src/regex/flags.rs index 95103d85cf..617427120a 100644 --- a/crates/perry-runtime/src/regex/flags.rs +++ b/crates/perry-runtime/src/regex/flags.rs @@ -6,7 +6,7 @@ use super::throw_regexp_syntax_error; /// The canonical flags held inline. Validation borrows only the original /// bytes; no heap allocation or JS throw occurs inside that borrowed view. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub(crate) struct CanonicalFlags { buf: [u8; 8], len: u8, diff --git a/crates/perry-runtime/src/regex/perex_api.rs b/crates/perry-runtime/src/regex/perex_api.rs index 7d7e4c5f74..a3cc464fe2 100644 --- a/crates/perry-runtime/src/regex/perex_api.rs +++ b/crates/perry-runtime/src/regex/perex_api.rs @@ -126,6 +126,9 @@ pub(crate) fn bind_program<'s>( None => owner, }; let bound = BoundProgram::new(owner, budget).map_err(|e| EngineError::Program(e.error))?; + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_with(|d| d.perex_validations += 1); + } GcProgram::record_witness(&root, bound.witness()); Ok(bound) } @@ -429,7 +432,18 @@ pub(crate) fn execute_output( let scope = RuntimeHandleScope::new(); let receiver = scope.root_raw_mut_ptr(receiver); let input = scope.root_string_ptr(input); - let last_index = caught(|| receiver.with_const_ptr(|p| super::regex_last_index_offset(p)))?; + let stored = receiver.with_const_ptr::(|r| unsafe { + crate::value::JSValue::from_bits((*r).last_index) + }); + let last_index = if stored.is_number() { + stored + .as_number() + .max(0.0) + .floor() + .min(9_007_199_254_740_991.0) as usize + } else { + caught(|| receiver.with_const_ptr(|p| super::regex_last_index_offset(p)))? + }; let (stateful, has_indices) = receiver.with_const_ptr::(|r| unsafe { ((*r).global || (*r).sticky, (*r).has_indices) }); diff --git a/crates/perry-runtime/src/regex/perex_cache.rs b/crates/perry-runtime/src/regex/perex_cache.rs new file mode 100644 index 0000000000..fef242a8d3 --- /dev/null +++ b/crates/perry-runtime/src/regex/perex_cache.rs @@ -0,0 +1,265 @@ +//! Bounded LRU of immutable Perex programs. Identity hits read no source bytes; +//! different string allocations use a content hash followed by exact equality. +//! Both GC pointers are mutable roots. No RegExp header or lastIndex is cached. +use super::flags::CanonicalFlags; +use super::perex_owner::GcProgram; +use super::perex_runtime::EngineError; +use crate::gc::{RuntimeHandle, RuntimeHandleScope, RuntimeRootVisitor}; +use crate::string::StringHeader; +use perex::binding::ImmutableProgram; +use std::cell::RefCell; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; + +const CAPACITY: usize = 512; +const BYTE_LIMIT: usize = 32 * 1024 * 1024; +type ContentKey = (u64, CanonicalFlags); + +struct Entry { + source: *const StringHeader, + program: *const u8, + key: ContentKey, + bytes: usize, + used: u64, +} + +#[derive(Default)] +struct Cache { + registered: bool, + entries: Vec>, + identities: HashMap<(usize, CanonicalFlags), usize>, + contents: HashMap>, + clock: u64, + bytes: usize, +} + +crate::perry_thread_local! { + static REGEX_CACHE: RefCell = RefCell::new(Cache::default()); +} + +fn fingerprint(bytes: &[u8]) -> u64 { + let mut hash = std::collections::hash_map::DefaultHasher::new(); + bytes.hash(&mut hash); + hash.finish() +} + +impl Cache { + fn hit(&mut self, index: usize) -> *const u8 { + self.clock += 1; + let entry = self.entries[index].as_mut().unwrap(); + entry.used = self.clock; + entry.program + } + + fn remove(&mut self, index: usize) { + let entry = self.entries[index].take().unwrap(); + self.identities + .remove(&(entry.source as usize, entry.key.1)); + let bucket = self.contents.get_mut(&entry.key).unwrap(); + bucket.retain(|&i| i != index); + if bucket.is_empty() { + self.contents.remove(&entry.key); + } + self.bytes -= entry.bytes; + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_counters(|d| d.cache_evictions += 1); + } + } + + fn insert(&mut self, mut entry: Entry) { + if entry.bytes > BYTE_LIMIT { + return; + } + while self.identities.len() >= CAPACITY || self.bytes + entry.bytes > BYTE_LIMIT { + let oldest = self + .entries + .iter() + .enumerate() + .filter_map(|(i, e)| e.as_ref().map(|e| (i, e.used))) + .min_by_key(|&(_, used)| used) + .unwrap() + .0; + self.remove(oldest); + } + self.clock += 1; + entry.used = self.clock; + let index = self + .entries + .iter() + .position(Option::is_none) + .unwrap_or(self.entries.len()); + self.identities + .insert((entry.source as usize, entry.key.1), index); + self.contents.entry(entry.key).or_default().push(index); + self.bytes += entry.bytes; + if index == self.entries.len() { + self.entries.push(Some(entry)); + } else { + self.entries[index] = Some(entry); + } + } +} + +pub(super) fn get_or_compile<'s>( + scope: &'s RuntimeHandleScope, + source: &RuntimeHandle<'s>, + flags: CanonicalFlags, + compile: impl FnOnce() -> Result, EngineError>, +) -> Result, EngineError> { + // Publication retains OriginalSource even on a content hit where this + // particular string has never been bound by HeapSubject before. + source.with_mut_ptr::(|p| crate::string::js_string_addref(p)); + if crate::hot_diag::regex_on() { + source.with_const_ptr::(|p| { + crate::hot_diag::regex_with(|d| { + d.note_new( + p as usize, + super::string_as_bytes(p), + flags.as_str(), + false, + false, + ) + }); + }); + } + let identity = source.with_const_ptr::(|p| (p as usize, flags)); + let hit = REGEX_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + if !cache.registered { + // Register before the first compilation can poll or publish roots. + // Programs that never construct a RegExp pay no cache scan cost. + crate::gc::gc_register_named_mutable_root_scanner( + "regex.program_cache", + scan_roots_mut, + ); + cache.registered = true; + } + cache + .identities + .get(&identity) + .copied() + .map(|index| cache.hit(index)) + }); + if let Some(program) = hit { + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_counters(|d| d.program_identity_hits += 1); + } + // No collecting operation between lookup and establishing this root. + return Ok(unsafe { GcProgram::from_cached(scope, program) }); + } + let hash = unsafe { source.with_string_bytes(fingerprint) }; + let key = (hash, flags); + let hit = REGEX_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + let index = cache.contents.get(&key).and_then(|bucket| { + bucket.iter().copied().find(|&i| { + let entry = cache.entries[i].as_ref().unwrap(); + unsafe { + source.with_string_bytes(|bytes| bytes == super::string_as_bytes(entry.source)) + } + }) + }); + index.map(|index| cache.hit(index)) + }); + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_counters(|d| { + d.program_hash_bytes += + source.with_const_ptr::(|p| unsafe { (*p).byte_len as u64 }); + d.program_content_hits += u64::from(hit.is_some()); + }); + } + if let Some(program) = hit { + return Ok(unsafe { GcProgram::from_cached(scope, program) }); + } + let program = compile()?; + let bytes = program + .with_words(|words| words.len() * 4) + .map_err(|e| EngineError::Subject(perex::binding::SubjectError::Resource(e)))? + + source.with_const_ptr::(|p| unsafe { (*p).byte_len as usize }); + let mut entry = Entry { + source: std::ptr::null(), + program: std::ptr::null(), + key, + bytes, + used: 0, + }; + // GC_STORE_AUDIT(ROOT): both slots become roots in REGEX_CACHE and are + // visited by scan_roots_mut. Stores/barriers do not collect. + unsafe { + source.with_mut_ptr::(|p| { + crate::string::js_string_addref(p); + crate::gc::runtime_store_root_raw_mut_ptr_slot( + &mut entry.source as *mut *const StringHeader as *mut *mut StringHeader, + p, + ); + }); + program.with_ptr(|p| { + crate::gc::runtime_store_root_raw_mut_ptr_slot( + &mut entry.program as *mut *const u8 as *mut *mut u8, + p as *mut u8, + ) + }); + } + REGEX_CACHE.with(|cache| cache.borrow_mut().insert(entry)); + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_with(|d| d.perex_compiles += 1); + } + Ok(program) +} + +pub(crate) fn scan_roots_mut(visitor: &mut RuntimeRootVisitor<'_>) { + REGEX_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + let mut moved = false; + for entry in cache.entries.iter_mut().flatten() { + let old = entry.source; + visitor.visit_tagged_raw_const_ptr_slot(&mut entry.source, crate::value::STRING_TAG); + visitor.visit_raw_const_ptr_slot(&mut entry.program); + moved |= old != entry.source; + } + if moved { + let identities = cache + .entries + .iter() + .enumerate() + .filter_map(|(i, entry)| entry.as_ref().map(|e| ((e.source as usize, e.key.1), i))) + .collect(); + cache.identities = identities; + } + }); +} + +pub(crate) fn census() -> crate::gc::census::SideTableRow { + REGEX_CACHE.with(|cache| { + let cache = cache.borrow(); + let native = cache.entries.capacity() * std::mem::size_of::>() + + cache.identities.capacity() * std::mem::size_of::<((usize, CanonicalFlags), usize)>() + + cache.contents.capacity() * std::mem::size_of::<(ContentKey, Vec)>() + + cache + .contents + .values() + .map(|bucket| bucket.capacity() * std::mem::size_of::()) + .sum::(); + ("regex.program_cache", cache.identities.len(), native) + }) +} + +#[cfg(test)] +pub(crate) fn clear_for_tests() { + REGEX_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + let registered = cache.registered; + *cache = Cache { + registered, + ..Cache::default() + }; + }); +} + +#[cfg(test)] +pub(crate) fn reset_registration_for_tests() { + REGEX_CACHE.with(|cache| cache.borrow_mut().registered = false); +} + +#[cfg(test)] +mod tests; diff --git a/crates/perry-runtime/src/regex/perex_cache/tests.rs b/crates/perry-runtime/src/regex/perex_cache/tests.rs new file mode 100644 index 0000000000..b593202379 --- /dev/null +++ b/crates/perry-runtime/src/regex/perex_cache/tests.rs @@ -0,0 +1,149 @@ +use super::*; +use crate::regex::RegExpHeader; + +struct Reset; +impl Reset { + fn new() -> Self { + clear_for_tests(); + Self + } +} +impl Drop for Reset { + fn drop(&mut self) { + clear_for_tests(); + } +} + +fn regex<'s>(scope: &'s RuntimeHandleScope, source: &str, flags: &str) -> RuntimeHandle<'s> { + let source = scope.root_string_ptr(crate::regex::js_string_from_str(source)); + let flags = scope.root_string_ptr(crate::regex::js_string_from_str(flags)); + scope.root_raw_mut_ptr( + source.with_const_ptr(|p| flags.with_const_ptr(|f| crate::regex::js_regexp_new(p, f))), + ) +} + +fn program(re: &RuntimeHandle<'_>) -> usize { + re.with_const_ptr(|p| crate::regex::test_regexp_program_address(p)) +} + +#[test] +fn identical_content_and_canonical_flags_share_programs() { + let _lock = crate::gc::global_side_table_test_lock(); + let _reset = Reset::new(); + let scope = RuntimeHandleScope::new(); + for pattern in ["abc", r"(?<=a)b", r"(a)\1", r"\p{Emoji}"] { + let a = regex(&scope, pattern, "ig"); + let b = regex(&scope, pattern, "gi"); + let c = regex(&scope, pattern, "g"); + assert_eq!(program(&a), program(&b)); + assert_ne!(program(&a), program(&c)); + assert_ne!( + a.with_const_ptr(|p: *const RegExpHeader| p), + b.with_const_ptr(|p: *const RegExpHeader| p) + ); + a.with_mut_ptr(|p| crate::regex::js_regexp_set_last_index(p, 17.0)); + assert_eq!( + b.with_const_ptr(|p| crate::regex::js_regexp_get_last_index(p)), + 0.0 + ); + } +} + +#[test] +fn identity_hit_does_not_compile_or_hash_the_pattern() { + let _lock = crate::gc::global_side_table_test_lock(); + let _reset = Reset::new(); + let scope = RuntimeHandleScope::new(); + let re = regex(&scope, "identity", "g"); + let source = scope + .root_string_ptr(re.with_const_ptr(|p: *const RegExpHeader| unsafe { (*p).pattern_ptr })); + let flags = CanonicalFlags::parse(b"g").unwrap(); + let cached = + get_or_compile(&scope, &source, flags, || panic!("identity hit recompiled")).unwrap(); + assert_eq!(cached.with_ptr(|p| p as usize), program(&re)); + // A corrupted content index is irrelevant to an identity hit. + REGEX_CACHE.with(|cache| cache.borrow_mut().contents.clear()); + get_or_compile(&scope, &source, flags, || { + panic!("identity hit used content index") + }) + .unwrap(); +} + +#[test] +fn lru_overflow_keeps_hot_program_and_evicts_one_at_a_time() { + let _lock = crate::gc::global_side_table_test_lock(); + let _reset = Reset::new(); + let scope = RuntimeHandleScope::new(); + let hot = regex(&scope, "hot", "g"); + let oldest = regex(&scope, "oldest", "g"); + for i in 0..CAPACITY * 2 { + let local = RuntimeHandleScope::new(); + let fresh = regex(&local, &format!("cold{i}"), "g"); + assert_ne!(program(&fresh), program(&hot)); + let same = regex(&local, "hot", "g"); + assert_eq!(program(&same), program(&hot), "hot entry evicted at {i}"); + REGEX_CACHE.with(|cache| { + let cache = cache.borrow(); + assert_eq!(cache.identities.len(), (i + 3).min(CAPACITY)); + assert!(cache.bytes <= BYTE_LIMIT); + }); + } + // An evicted program can still be owned by an existing RegExp. + let rebuilt = regex(&scope, "oldest", "g"); + assert_ne!(program(&oldest), program(&rebuilt)); +} + +#[test] +fn hash_collisions_do_not_share_different_sources() { + let _lock = crate::gc::global_side_table_test_lock(); + let _reset = Reset::new(); + let scope = RuntimeHandleScope::new(); + let wrong = regex(&scope, "collision-a", ""); + let key = ( + fingerprint(b"collision-b"), + CanonicalFlags::parse(b"").unwrap(), + ); + REGEX_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + let old_key = cache.entries[0].as_ref().unwrap().key; + let bucket = cache.contents.remove(&old_key).unwrap(); + cache.contents.insert(key, bucket); + cache.entries[0].as_mut().unwrap().key = key; + }); + let right = regex(&scope, "collision-b", ""); + assert_ne!(program(&wrong), program(&right)); + let again = regex(&scope, "collision-b", ""); + assert_eq!(program(&right), program(&again)); +} + +#[test] +fn invalid_pattern_never_enters_the_cache() { + let _lock = crate::gc::global_side_table_test_lock(); + let _reset = Reset::new(); + let scope = RuntimeHandleScope::new(); + let source = scope.root_string_ptr(crate::regex::js_string_from_str("(")); + let flags = scope.root_string_ptr(crate::regex::js_string_from_str("g")); + assert!(source + .with_const_ptr(|p| flags.with_const_ptr(|f| crate::regex::perex_construct::new(p, f))) + .is_err()); + REGEX_CACHE.with(|cache| assert!(cache.borrow().entries.is_empty())); +} + +#[test] +fn content_hit_seals_the_new_original_source_against_unique_append() { + let _lock = crate::gc::global_side_table_test_lock(); + let _reset = Reset::new(); + let scope = RuntimeHandleScope::new(); + let first = regex(&scope, "shared-source", ""); + let source = scope.root_string_ptr(crate::regex::js_string_from_str("shared-source")); + source.with_mut_ptr::(|p| unsafe { (*p).refcount = 1 }); + let flags = scope.root_string_ptr(crate::regex::js_string_from_str("")); + let second = scope.root_raw_mut_ptr( + source.with_const_ptr(|p| flags.with_const_ptr(|f| crate::regex::js_regexp_new(p, f))), + ); + assert_eq!(program(&first), program(&second)); + assert_eq!( + source.with_const_ptr::(|p| unsafe { (*p).refcount }), + 0 + ); +} diff --git a/crates/perry-runtime/src/regex/perex_construct.rs b/crates/perry-runtime/src/regex/perex_construct.rs index b13d326e41..5713a137ff 100644 --- a/crates/perry-runtime/src/regex/perex_construct.rs +++ b/crates/perry-runtime/src/regex/perex_construct.rs @@ -42,6 +42,16 @@ fn compile<'s>( scope: &'s RuntimeHandleScope, source: RuntimeHandle<'s>, flags: CanonicalFlags, +) -> Result, EngineError> { + super::perex_cache::get_or_compile(scope, &source, flags, || { + compile_uncached(scope, source, flags) + }) +} + +fn compile_uncached<'s>( + scope: &'s RuntimeHandleScope, + source: RuntimeHandle<'s>, + flags: CanonicalFlags, ) -> Result, EngineError> { let source = BoundSubject::new( unsafe { HeapSubject::new(source) } diff --git a/crates/perry-runtime/src/regex/perex_dispatch.rs b/crates/perry-runtime/src/regex/perex_dispatch.rs index 9be7cf0f25..f22949f98c 100644 --- a/crates/perry-runtime/src/regex/perex_dispatch.rs +++ b/crates/perry-runtime/src/regex/perex_dispatch.rs @@ -115,6 +115,18 @@ pub(crate) fn execute( reuse: Option<&api::Reuse<'_, '_>>, ) -> Result, EngineError> { host::charge(budget, 1)?; + if crate::object::regex_canonical::exec(receiver.get_nanbox_f64()) { + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_counters(|d| d.perex_canonical_execs += 1); + } + let re = + crate::value::js_nanbox_get_pointer(receiver.get_nanbox_f64()) as *mut RegExpHeader; + return input + .with_const_ptr(|input| { + api::execute_with_resources(re, input, materialize, budget, memory, poll, reuse) + }) + .map(|result| result.map(ExecResult::Builtin)); + } require_object(receiver.get_nanbox_f64())?; input.with_mut_ptr::(|input| crate::string::js_string_addref(input)); let scope = RuntimeHandleScope::new(); diff --git a/crates/perry-runtime/src/regex/perex_owner.rs b/crates/perry-runtime/src/regex/perex_owner.rs index 05a7188fe3..06513b74a0 100644 --- a/crates/perry-runtime/src/regex/perex_owner.rs +++ b/crates/perry-runtime/src/regex/perex_owner.rs @@ -48,6 +48,24 @@ impl std::fmt::Debug for GcProgram<'_> { } impl<'scope> GcProgram<'scope> { + /// Root an immutable program retrieved from the construction cache. + /// + /// # Safety + /// `program` must be a live cell emitted here, with no collecting operation + /// between the cache lookup and establishing this independent root. + pub(crate) unsafe fn from_cached( + scope: &'scope RuntimeHandleScope, + program: *const u8, + ) -> Self { + Self { + root: scope.root_raw_const_ptr(program), + } + } + + pub(crate) fn with_ptr(&self, f: impl FnOnce(*const u8) -> T) -> T { + self.root.with_const_ptr(f) + } + /// Consume a prepared compiler plan with no retained pattern/flag views. /// No host callback or collecting operation occurs during emission. pub(crate) fn emit( diff --git a/crates/perry-runtime/src/regex/perex_remove.rs b/crates/perry-runtime/src/regex/perex_remove.rs new file mode 100644 index 0000000000..8814857c43 --- /dev/null +++ b/crates/perry-runtime/src/regex/perex_remove.rs @@ -0,0 +1,129 @@ +//! Empty-string replacement on a proven builtin RegExp needs only match spans. +//! No match returns the original string; matches assemble gaps without exec +//! arrays, capture strings, flags getters or a call through @@replace. +use super::perex_api as api; +use super::perex_match_search::{advance, subject}; +use super::perex_memory::MemoryBudget; +use super::perex_replace_storage::{boxed, length, Pieces}; +use super::perex_runtime::{self as host, CaptureMode, EngineError}; +use super::RegExpHeader; +use crate::gc::RuntimeHandleScope; +use crate::string::StringHeader; +use crate::value::{js_nanbox_string, JSValue}; +use perex::Budget; + +/// None declines without observable work. All string decoding below happens +/// after admission, with every argument rooted before any materialization. +pub(super) fn try_remove( + receiver: f64, + search: f64, + replacement: f64, +) -> Result, EngineError> { + let replacement_value = JSValue::from_bits(replacement.to_bits()); + // This cheap first guard leaves non-empty replacement workloads alone. + let empty = if replacement_value.is_string() { + unsafe { (*replacement_value.as_string_ptr()).byte_len == 0 } + } else if replacement_value.is_short_string() { + replacement_value.short_string_len() == 0 + } else { + false + }; + if !empty + || !JSValue::from_bits(receiver.to_bits()).is_any_string() + || !crate::object::regex_canonical::replace(search) + { + return Ok(None); + } + let re = crate::value::js_nanbox_get_pointer(search) as *mut RegExpHeader; + let (global, sticky, unicode, stored) = unsafe { + ( + (*re).global, + (*re).sticky, + (*re).unicode, + JSValue::from_bits((*re).last_index), + ) + }; + if !stored.is_number() { + return Ok(None); + } + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_with(|d| d.perex_removes += 1); + } + let scope = RuntimeHandleScope::new(); + let re = scope.root_raw_mut_ptr(re); + let receiver = scope.root_nanbox_f64(receiver); + let input = + api::caught(|| crate::value::js_get_string_pointer_unified(receiver.get_nanbox_f64()))?; + let input = scope.root_string_ptr(input as *const StringHeader); + let input_length = length(&input); + let mut start = if global || !sticky { + 0 + } else { + stored + .as_number() + .max(0.0) + .floor() + .min(9_007_199_254_740_991.0) as usize + }; + if global { + re.with_mut_ptr(|re| super::store_last_index_number(re, 0)); + } + let mut budget = Budget::new(api::WORK); + let memory = MemoryBudget::new(api::SCRATCH_BYTES); + let program = api::program(&scope, &re, &mut budget, &memory, &mut host::poll)?; + let subject = subject(input)?; + let mut output: Option> = None; + let mut copied = 0; + let mut near = None; + loop { + let found = if start > input_length { + None + } else { + let (found, position) = host::find_near( + &program, + &subject, + start, + near, + CaptureMode::Full, + &mut budget, + &memory, + api::QUANTUM, + &mut host::poll, + )?; + near = Some(position); + found + }; + if global || sticky { + re.with_mut_ptr(|re| { + super::store_last_index_number(re, found.as_ref().map_or(0, |m| m.full.end())) + }); + } + let Some(found) = found else { + break; + }; + let output = match &mut output { + Some(output) => output, + slot @ None => slot.insert(Pieces::new(&scope)?), + }; + output.append(&input, copied, found.full.start(), &mut budget)?; + copied = found.full.end(); + if !global { + break; + } + start = if found.full.is_empty() { + advance(&subject, copied as f64, input_length, unicode, &mut budget)? as usize + } else { + copied + }; + if found.full.is_empty() { + re.with_mut_ptr(|re| super::store_last_index_number(re, start)); + } + } + let Some(mut output) = output else { + return Ok(Some(boxed(&input))); + }; + output.append(&input, copied, input_length, &mut budget)?; + output + .finish(&input, None, &mut budget) + .map(|s| Some(js_nanbox_string(s as i64))) +} diff --git a/crates/perry-runtime/src/regex/perex_replace.rs b/crates/perry-runtime/src/regex/perex_replace.rs index 91dbed8d25..0c6e82dea1 100644 --- a/crates/perry-runtime/src/regex/perex_replace.rs +++ b/crates/perry-runtime/src/regex/perex_replace.rs @@ -214,6 +214,11 @@ pub(crate) fn string( replacement: f64, ) -> Result { coercible(receiver)?; + if !all { + if let Some(result) = super::perex_remove::try_remove(receiver, search, replacement)? { + return Ok(result); + } + } let scope = RuntimeHandleScope::new(); let receiver = scope.root_nanbox_f64(receiver); let search = scope.root_nanbox_f64(search); diff --git a/crates/perry-runtime/src/regex/perex_runtime.rs b/crates/perry-runtime/src/regex/perex_runtime.rs index 1f97ce41ff..41d51b4617 100644 --- a/crates/perry-runtime/src/regex/perex_runtime.rs +++ b/crates/perry-runtime/src/regex/perex_runtime.rs @@ -175,6 +175,9 @@ struct MatchBuffers<'a> { impl<'a> MatchBuffers<'a> { fn new(memory: &'a MemoryBudget, size: ScratchRequirements) -> Result { + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_with(|d| d.perex_scratch_allocs += 1); + } Ok(Self { registers: Slots::new(memory, size.registers)?, frames: Slots::new(memory, size.frames)?, @@ -251,6 +254,9 @@ pub(crate) fn find_near<'mem, S: ImmutableSubject>( quantum: usize, poll: &mut impl FnMut() -> Result<(), EngineError>, ) -> Result<(Option>, Position), EngineError> { + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_with(|d| d.perex_searches += 1); + } if quantum == 0 { return Err(EngineError::InvalidQuantum); } @@ -297,6 +303,9 @@ pub(crate) fn find_near<'mem, S: ImmutableSubject>( } Ok(Progress::Pending) => poll()?, Err(SearchError::Execution(ExecError::Frames | ExecError::Undo)) => { + if crate::hot_diag::regex_on() { + crate::hot_diag::regex_with(|d| d.perex_scratch_grows += 1); + } let required = search.required_scratch(); if required.frames > size.frames { size.frames = required diff --git a/crates/perry-runtime/src/startup_memory_profile.rs b/crates/perry-runtime/src/startup_memory_profile.rs new file mode 100644 index 0000000000..6ac0a635bd --- /dev/null +++ b/crates/perry-runtime/src/startup_memory_profile.rs @@ -0,0 +1,71 @@ +//! Opt-in policy for small Linux processes. Configure the allocator before +//! Rust startup can allocate; setting this from `js_gc_init` is too late. +//! +//! `mi_option_set_default` leaves mimalloc's own environment parsing intact, +//! including its lowercase option spelling. No Rust allocation, environment +//! mutation, or lock belongs on this constructor path. + +#[cfg(all( + target_os = "linux", + target_pointer_width = "64", + feature = "alloc-mimalloc" +))] +mod linux { + extern "C" { + fn perry_retain_memory_profile_init(); + #[cfg(test)] + fn perry_memory_profile_allow_thp() -> libc::c_long; + } + + #[inline(never)] + pub(super) fn retain_constructor() { + // Pull the C constructor's archive member into native programs even + // with multiple codegen units. This function does not apply a policy + // late; its constructor must already have run before Rust startup. + unsafe { perry_retain_memory_profile_init() }; + } + + #[cfg(test)] + mod tests { + #[test] + fn allocator_policy_is_selected_before_gc_init() { + // The runner launches this test in a fresh process for each + // profile/override. Rust's test harness has already allocated; + // js_gc_init is deliberately never called here. + let profile = std::env::var("PERRY_MEMORY_PROFILE").ok(); + let explicit = std::env::var("MIMALLOC_ALLOW_THP") + .or_else(|_| std::env::var("mimalloc_allow_thp")) + .ok(); + let expected = match explicit.as_deref() { + Some("0") => 0, + Some("1") => 1, + None => i64::from(profile.as_deref() != Some("small")), + value => panic!("probe expects an absent, 0, or 1 override: {value:?}"), + }; + let actual = unsafe { super::perry_memory_profile_allow_thp() }; + assert_eq!(actual as i64, expected); + // On Linux this is the process-wide effect of allow_thp=0. The + // option value alone would pass even if the constructor ran too + // late for mimalloc's OS initialization to apply the policy. + let disabled = unsafe { libc::prctl(libc::PR_GET_THP_DISABLE, 0, 0, 0, 0) }; + assert!(disabled >= 0, "PR_GET_THP_DISABLE must be available"); + if expected == 0 { + assert_eq!( + disabled, 1, + "THP policy must already be applied before gc_init" + ); + } + } + } +} + +/// Keep the pre-main constructor in a statically linked Perry executable. +#[inline(never)] +pub(crate) fn retain_constructor() { + #[cfg(all( + target_os = "linux", + target_pointer_width = "64", + feature = "alloc-mimalloc" + ))] + linux::retain_constructor(); +} diff --git a/crates/perry-runtime/src/text.rs b/crates/perry-runtime/src/text.rs index 98fb16f1ae..78af01adfd 100644 --- a/crates/perry-runtime/src/text.rs +++ b/crates/perry-runtime/src/text.rs @@ -655,8 +655,11 @@ static KEEP_TEXT_DECODER_IGNORE_BOM: extern "C" fn(f64) -> f64 = js_text_decoder /// `text_handle_property` no longer TAKES the caller's pointer at all, so the /// bug is not merely fixed here, it is unwritable. pub(crate) fn text_decoder_method_name_static(key: &[u8]) -> Option<&'static [u8]> { + // A named static has one identity across codegen units. Anonymous byte + // literals may be duplicated by thin LTO even within this lookup. + static DECODE: [u8; 6] = *b"decode"; match key { - b"decode" => Some(b"decode"), + b"decode" => Some(&DECODE), _ => None, } } @@ -664,9 +667,11 @@ pub(crate) fn text_decoder_method_name_static(key: &[u8]) -> Option<&'static [u8 /// A `TextEncoder.prototype` method key, as a `'static` byte string. See /// [`text_decoder_method_name_static`]. pub(crate) fn text_encoder_method_name_static(key: &[u8]) -> Option<&'static [u8]> { + static ENCODE: [u8; 6] = *b"encode"; + static ENCODE_INTO: [u8; 10] = *b"encodeInto"; match key { - b"encode" => Some(b"encode"), - b"encodeInto" => Some(b"encodeInto"), + b"encode" => Some(&ENCODE), + b"encodeInto" => Some(&ENCODE_INTO), _ => None, } } diff --git a/crates/perry-runtime/src/timer.rs b/crates/perry-runtime/src/timer.rs index e07fb14677..e99d6bb8c0 100644 --- a/crates/perry-runtime/src/timer.rs +++ b/crates/perry-runtime/src/timer.rs @@ -412,8 +412,8 @@ mod ref_states; #[cfg(test)] // #7680: not re-exported; reach via `crate::timer::test_shared_queues::` pub(crate) mod test_shared_queues; -pub(crate) use ownership::purge_agent_timers; use ownership::{has_refed_callback_timer, has_refed_interval_timer, has_refed_promise_timer}; +pub(crate) use ownership::{purge_agent_timers, timer_phase_work_pending}; pub(crate) use gc_scan::{new_timer_root_scan_state, scan_timer_roots_mut_step}; use ref_states::{TimerRefStates, TIMER_REF_STATES_CAP}; diff --git a/crates/perry-runtime/src/timer/ownership.rs b/crates/perry-runtime/src/timer/ownership.rs index ea882b2a1a..21918a9ca7 100644 --- a/crates/perry-runtime/src/timer/ownership.rs +++ b/crates/perry-runtime/src/timer/ownership.rs @@ -10,6 +10,19 @@ use super::{timer_has_ref_state, CALLBACK_TIMERS, INTERVAL_TIMERS, TIMER_QUEUE}; +/// Any entry needs the ordinary timer phase, including unref timers and +/// cleared entries whose cleanup has not run. Foreign entries conservatively +/// select the full pump; this predicate never changes agent liveness. +pub(crate) fn timer_phase_work_pending() -> bool { + if !TIMER_QUEUE.lock().unwrap().is_empty() { + return true; + } + if !CALLBACK_TIMERS.lock().unwrap().is_empty() { + return true; + } + !INTERVAL_TIMERS.lock().unwrap().is_empty() +} + // ── Per-agent event-loop liveness ──────────────────────────────────────────── // // #6185: a timer owned by another agent can never be fired by this one, so it diff --git a/crates/perry-runtime/src/tls_hot.rs b/crates/perry-runtime/src/tls_hot.rs index 7a1c7a7e18..49148ef3f5 100644 --- a/crates/perry-runtime/src/tls_hot.rs +++ b/crates/perry-runtime/src/tls_hot.rs @@ -100,6 +100,13 @@ //! every thread falls back to `_tlv_get_addr`, permanently and silently //! correctly. It cannot degrade into reading a wrong address. +// Android uses pooled storage with the same try_with failure semantics. Other +// platforms keep std's storage and exact AccessError type. +#[cfg(target_os = "android")] +pub use crate::tls_os_pool::AccessError; +#[cfg(not(target_os = "android"))] +pub use std::thread::AccessError; + use std::cell::{Cell, UnsafeCell}; /// How many generic [`HotKey`] slots one thread's cache can hold. @@ -263,6 +270,7 @@ impl HotTls { }; } +#[cfg(not(target_os = "android"))] thread_local! { /// `const`-initialised on purpose: a lazily-initialised `thread_local!` /// pays a "has this been initialised / has this been dropped" check on @@ -272,6 +280,12 @@ thread_local! { static HOT: UnsafeCell = const { UnsafeCell::new(HotTls::EMPTY) }; } +// Keep the cache in the pool too. It must outlive pooled value destructors, +// whose SlotGuard clears cached addresses while the pool is being torn down. +#[cfg(target_os = "android")] +static HOT: crate::tls_os_pool::LocalKey> = + crate::tls_os_pool::LocalKey::new(|| UnsafeCell::new(HotTls::EMPTY)); + /// Resolve every cached address for this thread. Cold: runs once per thread. /// /// Each `…_hot_addr()` touches its own `thread_local!` exactly as any other @@ -794,15 +808,15 @@ impl Drop for SlotGuard { /// A thread-local whose address is cached in this thread's [`HotTls`]. /// -/// Drop-in for `std::thread::LocalKey` at the call site: `with` and `try_with` -/// keep the same signatures, so converting a declaration converts every one of -/// its uses. +/// `with` and `try_with` accept the same closures as `std::thread::LocalKey`. +/// Android uses the pooled backend's [`AccessError`]; other platforms retain +/// std's exact error type. pub struct HotKey { slot: &'static SlotId, /// Resolves the owning `thread_local!` the ordinary way and returns the /// address of its *value*. Cold path only — never called once the slot is /// populated, so the indirect call never appears on a hot path. - resolve: fn() -> Result<*mut u8, std::thread::AccessError>, + resolve: fn() -> Result<*mut u8, AccessError>, /// Records the claimed index in this thread's teardown guard, if the value /// has one. Generated alongside the storage, so it knows the `GUARD` that /// `HotKey` deliberately does not. @@ -819,7 +833,7 @@ impl HotKey { #[doc(hidden)] pub const fn new( slot: &'static SlotId, - resolve: fn() -> Result<*mut u8, std::thread::AccessError>, + resolve: fn() -> Result<*mut u8, AccessError>, arm_guard: fn(u32), ) -> Self { Self { @@ -847,10 +861,14 @@ impl HotKey { /// As [`HotKey::with`], but reports rather than panics when this thread's /// value is being or has been destroyed. #[inline(always)] - pub fn try_with(&'static self, f: F) -> Result + pub fn try_with(&'static self, f: F) -> Result where F: FnOnce(&T) -> R, { + #[cfg(target_os = "android")] + if crate::tls_os_pool::is_destroyed() { + return Err(AccessError); + } let idx = self.slot.raw(); if (idx as usize) < HOT_SLOT_CAPACITY { let cell = hot().slot(idx); @@ -915,7 +933,7 @@ impl HotKey { /// resolve through the real `thread_local!` and publish it for this thread. #[cold] #[inline(never)] - fn resolve_and_cache(&'static self) -> Result<*mut u8, std::thread::AccessError> { + fn resolve_and_cache(&'static self) -> Result<*mut u8, AccessError> { // Claim before resolving storage: another provider can already have // published this declaration in the shared cache. Do not construct or // overwrite a second copy. The claim lock is released before any TLS @@ -1013,7 +1031,7 @@ macro_rules! __perry_thread_local_one { // a cached address could otherwise outlive the value. type Storage = $crate::tls_hot::HotCell<$t, { ::core::mem::needs_drop::<$t>() as usize }>; $crate::__perry_thread_local_storage!(Storage, $($init)+); - fn resolve() -> ::core::result::Result<*mut u8, ::std::thread::AccessError> { + fn resolve() -> ::core::result::Result<*mut u8, $crate::tls_hot::AccessError> { STORAGE.try_with(|cell| cell.value_addr()) } fn arm_guard(idx: u32) { @@ -1028,14 +1046,22 @@ macro_rules! __perry_thread_local_one { #[macro_export] macro_rules! __perry_thread_local_storage { ($storage:ty, const $init:block) => { + #[cfg(not(target_os = "android"))] ::std::thread_local! { static STORAGE: $storage = const { <$storage>::new($init) }; } + #[cfg(target_os = "android")] + static STORAGE: $crate::tls_os_pool::LocalKey<$storage> = + $crate::tls_os_pool::LocalKey::new(|| <$storage>::new($init)); }; ($storage:ty, expr ($init:expr)) => { + #[cfg(not(target_os = "android"))] ::std::thread_local! { static STORAGE: $storage = <$storage>::new($init); } + #[cfg(target_os = "android")] + static STORAGE: $crate::tls_os_pool::LocalKey<$storage> = + $crate::tls_os_pool::LocalKey::new(|| <$storage>::new($init)); }; } diff --git a/crates/perry-runtime/src/tls_hot/provider_tests.rs b/crates/perry-runtime/src/tls_hot/provider_tests.rs index eea3fd83f9..2038878079 100644 --- a/crates/perry-runtime/src/tls_hot/provider_tests.rs +++ b/crates/perry-runtime/src/tls_hot/provider_tests.rs @@ -21,10 +21,17 @@ fn provider_copies_share_storage_without_initializing_a_second_value() { } } type Storage = super::HotCell; + #[cfg(not(target_os = "android"))] thread_local! { static FIRST_STORAGE: Storage = Storage::new(Probe::new()); static SECOND_STORAGE: Storage = Storage::new(Probe::new()); } + #[cfg(target_os = "android")] + static FIRST_STORAGE: crate::tls_os_pool::LocalKey = + crate::tls_os_pool::LocalKey::new(|| Storage::new(Probe::new())); + #[cfg(target_os = "android")] + static SECOND_STORAGE: crate::tls_os_pool::LocalKey = + crate::tls_os_pool::LocalKey::new(|| Storage::new(Probe::new())); static FIRST_SLOT: super::SlotId = super::SlotId::named("provider-test::shared"); static SECOND_SLOT: super::SlotId = super::SlotId::named("provider-test::shared"); static FIRST: super::HotKey = super::HotKey::new( diff --git a/crates/perry-runtime/src/tls_os_pool.rs b/crates/perry-runtime/src/tls_os_pool.rs new file mode 100644 index 0000000000..8fdeda1931 --- /dev/null +++ b/crates/perry-runtime/src/tls_os_pool.rs @@ -0,0 +1,291 @@ +//! Android storage for `perry_thread_local!` (#10219). +//! +//! Rust's Android TLS backend consumes one of bionic's 128 pthread keys for +//! every declaration it touches, including const, non-Drop declarations. The +//! runtime alone can exhaust that process-wide pool while starting the UI pump. +//! This backend owns one pthread key and lazily allocates typed values behind +//! it. The existing HotKey cache still supplies the steady-state fast path. +//! +//! Entries and values have stable System-allocated addresses. No collection +//! borrow survives an initializer or destructor. Destructors run in reverse +//! initialization order and can access other live keys or initialize new keys; +//! destroyed keys cannot be resurrected. Non-Drop values, including HotTls, +//! remain alive until all user destructors have run. The pthread destructor +//! retains a tombstone through later POSIX destructor passes. +use std::alloc::{GlobalAlloc, Layout, System}; +use std::marker::PhantomData; +use std::ptr; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Mutex, OnceLock}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AccessError; +impl std::fmt::Display for AccessError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("thread-local value is being or has been destroyed") + } +} +impl std::error::Error for AccessError {} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum State { + Vacant, + Initializing, + Ready, + Destroyed, +} +struct Entry { + state: State, + value: *mut u8, + destroy: unsafe fn(*mut u8), + next: *mut Entry, + drop_next: *mut Entry, +} +struct Pool { + entries: *mut Entry, + drops: *mut Entry, + slots: *mut *mut Entry, + capacity: usize, +} +static NEXT_ID: Mutex = Mutex::new(0); +static KEY: OnceLock = OnceLock::new(); +const DESTROYED: *mut libc::c_void = ptr::without_provenance_mut(1); + +// System allocation avoids recursing through a global allocator's own TLS. +unsafe fn alloc(value: T) -> *mut T { + let layout = Layout::new::(); + let out = if layout.size() == 0 { + ptr::NonNull::::dangling().as_ptr() + } else { + let out = System.alloc(layout).cast::(); + if out.is_null() { + std::alloc::handle_alloc_error(layout); + } + out + }; + out.write(value); + out +} +unsafe fn destroy(value: *mut u8) { + let value = value.cast::(); + ptr::drop_in_place(value); + if std::mem::size_of::() != 0 { + System.dealloc(value.cast(), Layout::new::()); + } +} +fn key() -> libc::pthread_key_t { + *KEY.get_or_init(|| { + let mut key = 0; + if unsafe { libc::pthread_key_create(&mut key, Some(drop_pool)) } != 0 { + panic!("cannot allocate the shared thread-local key"); + } + key + }) +} +/// HotKey must report teardown before consulting its cached HotTls pointer. +#[cfg(target_os = "android")] +pub(crate) fn is_destroyed() -> bool { + KEY.get() + .is_some_and(|key| unsafe { libc::pthread_getspecific(*key) } == DESTROYED) +} + +fn pool() -> Result<*mut Pool, AccessError> { + let key = key(); + let value = unsafe { libc::pthread_getspecific(key) }; + if value == DESTROYED { + return Err(AccessError); + } + if !value.is_null() { + return Ok(value.cast()); + } + let value = unsafe { + alloc(Pool { + entries: ptr::null_mut(), + drops: ptr::null_mut(), + slots: ptr::null_mut(), + capacity: 0, + }) + }; + if unsafe { libc::pthread_setspecific(key, value.cast()) } != 0 { + unsafe { + destroy::(value.cast()); + } + panic!("cannot publish the shared thread-local storage"); + } + Ok(value) +} +// The index table grows without moving any Entry or T. Its allocation also +// uses System so lookup remains safe inside a global allocator's TLS setup. +unsafe fn grow_index_table(pool: *mut Pool, index: usize) { + if index < (*pool).capacity { + return; + } + let capacity = index + .checked_add(1) + .and_then(usize::checked_next_power_of_two) + .expect("thread-local index table overflow") + .max(8); + let layout = Layout::array::<*mut Entry>(capacity).expect("thread-local index table layout"); + let slots = System.alloc(layout).cast::<*mut Entry>(); + if slots.is_null() { + std::alloc::handle_alloc_error(layout); + } + for offset in 0..capacity { + // GC_STORE_AUDIT(POINTER_FREE): an empty native metadata slot has no heap edge. + slots.add(offset).write(ptr::null_mut()); + } + if (*pool).capacity != 0 { + ptr::copy_nonoverlapping((*pool).slots, slots, (*pool).capacity); + System.dealloc( + (*pool).slots.cast(), + Layout::array::<*mut Entry>((*pool).capacity).unwrap(), + ); + } + (*pool).slots = slots; + (*pool).capacity = capacity; +} + +unsafe extern "C" fn drop_pool(value: *mut libc::c_void) { + let key = *KEY.get().unwrap(); + // Keep a tombstone through every POSIX destructor pass: another library's + // destructor must not resurrect storage that we have already released. + if value == DESTROYED { + libc::pthread_setspecific(key, DESTROYED); + return; + } + let result = std::panic::catch_unwind(|| { + let pool = value.cast::(); + if libc::pthread_setspecific(key, value) != 0 { + std::process::abort(); + } + while !(*pool).drops.is_null() { + let entry = (*pool).drops; + (*pool).drops = (*entry).drop_next; + (*entry).state = State::Destroyed; + ((*entry).destroy)((*entry).value); + } + // No caller may rediscover the pool once its final allocations are freed. + if libc::pthread_setspecific(key, DESTROYED) != 0 { + std::process::abort(); + } + // Values without Drop stay available to the destructors above. This + // includes the hot-pointer cache used to retire cached slot addresses. + let mut entry = (*pool).entries; + while !entry.is_null() { + let next = (*entry).next; + if (*entry).state == State::Ready { + ((*entry).destroy)((*entry).value); + } + destroy::(entry.cast()); + entry = next; + } + if (*pool).capacity != 0 { + System.dealloc( + (*pool).slots.cast(), + Layout::array::<*mut Entry>((*pool).capacity).unwrap(), + ); + } + destroy::(pool.cast()); + }); + if result.is_err() { + std::process::abort(); + } +} + +pub struct LocalKey { + index: AtomicUsize, + initialize: fn() -> T, + marker: PhantomData, +} +// Handles contain no T; each access resolves the calling thread's storage. +unsafe impl Sync for LocalKey {} +impl LocalKey { + pub const fn new(initialize: fn() -> T) -> Self { + Self { + index: AtomicUsize::new(usize::MAX), + initialize, + marker: PhantomData, + } + } + fn index(&self) -> usize { + let index = self.index.load(Ordering::Relaxed); + if index != usize::MAX { + return index; + } + // Claim once per declaration, not per racing first-touching thread. + // Release the lock before any allocation, initializer, or destructor. + let mut next = NEXT_ID.lock().unwrap_or_else(|error| error.into_inner()); + let index = self.index.load(Ordering::Relaxed); + if index != usize::MAX { + return index; + } + let index = *next; + *next = next + .checked_add(1) + .expect("thread-local declaration index overflow"); + self.index.store(index, Ordering::Relaxed); + index + } + pub fn with(&'static self, f: F) -> R + where + F: FnOnce(&T) -> R, + { + self.try_with(f) + .expect("cannot access destroyed thread-local storage") + } + pub fn try_with(&'static self, f: F) -> Result + where + F: FnOnce(&T) -> R, + { + let pool = pool()?; + let index = self.index(); + unsafe { + grow_index_table(pool, index); + let mut entry = *(*pool).slots.add(index); + if entry.is_null() { + entry = alloc(Entry { + state: State::Vacant, + value: ptr::null_mut(), + destroy: destroy::, + next: (*pool).entries, + drop_next: ptr::null_mut(), + }); + // GC_STORE_AUDIT(POINTER_FREE): links System-allocated TLS + // metadata, not a Perry heap reference. The values retain + // their existing type-specific root scanners. + (*pool).entries = entry; + (*pool).slots.add(index).write(entry); + } + match (*entry).state { + State::Destroyed => return Err(AccessError), + State::Initializing => panic!("recursive thread-local initialization"), + State::Ready => {} + State::Vacant => { + (*entry).state = State::Initializing; + struct Reset(*mut Entry); + impl Drop for Reset { + fn drop(&mut self) { + unsafe { + (*self.0).state = State::Vacant; + } + } + } + let reset = Reset(entry); + // Hold no reference or collection borrow across user code. + let value = alloc((self.initialize)()); + (*entry).value = value.cast(); + (*entry).state = State::Ready; + if std::mem::needs_drop::() { + (*entry).drop_next = (*pool).drops; + (*pool).drops = entry; + } + std::mem::forget(reset); + } + } + Ok(f(&*(*entry).value.cast::())) + } + } +} + +#[cfg(test)] +mod tests; diff --git a/crates/perry-runtime/src/tls_os_pool/tests.rs b/crates/perry-runtime/src/tls_os_pool/tests.rs new file mode 100644 index 0000000000..50b7a66d46 --- /dev/null +++ b/crates/perry-runtime/src/tls_os_pool/tests.rs @@ -0,0 +1,127 @@ +use super::*; +use std::cell::Cell; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex; +static ORDER: Mutex> = Mutex::new(Vec::new()); +struct Probe(u8); +static FIRST: LocalKey = LocalKey::new(|| Probe(1)); +static SECOND: LocalKey = LocalKey::new(|| Probe(2)); +static THIRD: LocalKey = LocalKey::new(|| Probe(3)); +static PLAIN: LocalKey> = LocalKey::new(|| Cell::new(99)); +impl Drop for Probe { + fn drop(&mut self) { + PLAIN.with(|v| assert_eq!(v.get(), 99)); + match self.0 { + 2 => { + assert!(SECOND.try_with(|_| ()).is_err()); + FIRST.with(|v| assert_eq!(v.0, 1)); + } + 1 => { + assert!(SECOND.try_with(|_| ()).is_err()); + THIRD.with(|v| assert_eq!(v.0, 3)); + } + 3 => assert!(FIRST.try_with(|_| ()).is_err()), + _ => unreachable!(), + } + ORDER.lock().unwrap().push(self.0); + } +} +#[test] +fn cleanup_supports_other_values_new_initializers_and_destroyed_errors() { + std::thread::spawn(|| { + PLAIN.with(|_| ()); + FIRST.with(|_| ()); + SECOND.with(|_| ()); + }) + .join() + .unwrap(); + assert_eq!(*ORDER.lock().unwrap(), [2, 1, 3]); +} +#[test] +fn initializer_unwind_leaves_the_key_retryable() { + static ATTEMPTS: AtomicUsize = AtomicUsize::new(0); + static RETRY: LocalKey = LocalKey::new(|| { + assert_ne!( + ATTEMPTS.fetch_add(1, Ordering::SeqCst), + 0, + "first attempt fails" + ); + 17 + }); + std::thread::spawn(|| { + assert!(std::panic::catch_unwind(|| RETRY.with(|_| ())).is_err()); + RETRY.with(|value| assert_eq!(*value, 17)); + }) + .join() + .unwrap(); + assert_eq!(ATTEMPTS.load(Ordering::SeqCst), 2); +} +#[test] +fn recursive_initializer_is_rejected_without_poisoning_other_keys() { + static RECURSIVE: LocalKey = LocalKey::new(|| RECURSIVE.with(|_| 1)); + std::thread::spawn(|| { + assert!(std::panic::catch_unwind(|| RECURSIVE.with(|_| ())).is_err()); + PLAIN.with(|v| assert_eq!(v.get(), 99)); + }) + .join() + .unwrap(); +} +#[test] +fn aligned_values_are_released_at_worker_exit() { + static DROPS: AtomicUsize = AtomicUsize::new(0); + #[repr(align(4096))] + struct Aligned(u8); + impl Drop for Aligned { + fn drop(&mut self) { + assert_eq!(self.0, 41); + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + static ALIGNED: LocalKey = LocalKey::new(|| Aligned(41)); + for _ in 0..32 { + std::thread::spawn(|| ALIGNED.with(|v| assert_eq!((v as *const _ as usize) % 4096, 0))) + .join() + .unwrap(); + } + assert_eq!(DROPS.load(Ordering::SeqCst), 32); +} + +#[test] +fn hundreds_of_keys_stay_isolated_across_worker_turnover() { + static KEYS: [LocalKey>; 512] = [const { LocalKey::new(|| Cell::new(0)) }; 512]; + KEYS[0].with(|first| { + first.set(1234); + for key in KEYS.iter().skip(1) { + key.with(|_| ()); + } + assert_eq!( + first.get(), + 1234, + "growing the index table moved a borrowed value" + ); + }); + for (i, key) in KEYS.iter().enumerate() { + key.with(|v| v.set(i + 1)); + } + let workers: Vec<_> = (0..8) + .map(|worker| { + std::thread::spawn(move || { + for (i, key) in KEYS.iter().enumerate() { + key.with(|v| { + assert_eq!(v.get(), 0); + v.set(worker + i); + }); + } + for (i, key) in KEYS.iter().enumerate() { + key.with(|v| assert_eq!(v.get(), worker + i)); + } + }) + }) + .collect(); + for worker in workers { + worker.join().unwrap(); + } + for (i, key) in KEYS.iter().enumerate() { + key.with(|v| assert_eq!(v.get(), i + 1)); + } +} diff --git a/crates/perry-runtime/src/weakref/test_support.rs b/crates/perry-runtime/src/weakref/test_support.rs index f5288903e1..b61e18a886 100644 --- a/crates/perry-runtime/src/weakref/test_support.rs +++ b/crates/perry-runtime/src/weakref/test_support.rs @@ -75,3 +75,54 @@ pub(crate) fn weak_entry_extent(map: f64) -> u32 { super::js_array_length(super::entries_array(map)) } } + +#[test] +fn empty_checkpoint_delivers_recorded_finalization_job() { + let result = std::thread::spawn(|| { + use super::*; + use std::sync::atomic::{AtomicU64, Ordering}; + static DELIVERED: AtomicU64 = AtomicU64::new(0); + extern "C" fn cleanup(_: *const crate::closure::ClosureHeader, held: f64) -> f64 { + DELIVERED.store(held.to_bits(), Ordering::Relaxed); + 0.0 + } + crate::gc::js_gc_init(); + crate::closure::js_register_closure_arity(cleanup as *const u8, 1); + let closure = crate::closure::js_closure_alloc(cleanup as *const u8, 0); + let scope = crate::gc::RuntimeHandleScope::new(); + let callback = scope.root_nanbox_f64(crate::value::js_nanbox_pointer(closure as i64)); + let registry = scope.root_nanbox_f64(crate::value::js_nanbox_pointer(js_finreg_new( + callback.get_nanbox_f64(), + ) as i64)); + let target = crate::object::js_object_alloc(0, 0); + js_finreg_register( + registry.get_nanbox_f64(), + crate::value::js_nanbox_pointer(target as i64), + 41.0, + f64::from_bits(TAG_UNDEFINED), + ); + // Seed the post-collection delivery boundary with a real registry + // record. Collection/weak liveness itself is covered by the weak-GC + // tests; here no Promise or nextTick has been queued yet. + let registry_ptr = js_nanbox_get_pointer(registry.get_nanbox_f64()) as *mut ObjectHeader; + let entries = unsafe { object_field_bits(registry_ptr, FINREG_ENTRIES_FIELD) }; + let record = js_array_get_f64((entries & POINTER_MASK) as *mut ArrayHeader, 0); + PENDING_FINALIZATION_JOBS.with(|jobs| { + jobs.borrow_mut().push(PendingFinalizationJob { + registry: registry.get_nanbox_f64(), + record, + callback: callback.get_nanbox_f64(), + held: 41.0, + }) + }); + assert!(!crate::promise::microtasks::empty_checkpoint_eligible_for_test()); + assert!(crate::promise::js_promise_run_microtasks_event_loop() > 0); + assert_eq!(DELIVERED.load(Ordering::Relaxed), 41.0f64.to_bits()); + assert_eq!(pending_finalization_jobs_count(), 0); + }) + .join(); + // `js_gc_init` disables the process-wide class-field inline gate in test + // builds; the spawned thread does not isolate that, so restore it. + crate::object::descriptor_state::test_reset_class_field_inline_guard(); + result.unwrap(); +} diff --git a/crates/perry-runtime/src/webassembly.rs b/crates/perry-runtime/src/webassembly.rs index 4af8fc234d..3b89751543 100644 --- a/crates/perry-runtime/src/webassembly.rs +++ b/crates/perry-runtime/src/webassembly.rs @@ -138,6 +138,12 @@ fn emit_error_to_stderr(prefix: &str, err: *mut c_char) { } } +pub(crate) fn drop_host_extern_handle(handle: usize) { + if handle != 0 { + unsafe { perry_wasm_host_extern_drop(handle as *mut c_void) }; + } +} + /// Consume (and free) a host error C-string into a `WebAssembly.`- /// shaped error value: an ordinary `ErrorHeader` whose `.name` is /// `CompileError` / `LinkError` — the same shape the graceful-fail @@ -163,6 +169,18 @@ fn wasm_type_error_value(message: &str) -> f64 { crate::value::js_nanbox_pointer(error as i64) } +fn resolved_promise_value(value: f64) -> f64 { + let scope = crate::gc::RuntimeHandleScope::new(); + let value = scope.root_nanbox_f64(value); + let promise = scope.root_raw_mut_ptr(crate::promise::js_promise_new()); + promise.with_mut_ptr(|promise: *mut crate::promise::Promise| { + crate::promise::js_promise_resolve(promise, value.get_nanbox_f64()) + }); + promise.with_mut_ptr(|promise: *mut crate::promise::Promise| { + crate::value::js_nanbox_pointer(promise as i64) + }) +} + fn rejected_promise_value(reason: f64) -> f64 { let scope = crate::gc::RuntimeHandleScope::new(); let reason = scope.root_nanbox_f64(reason); @@ -818,27 +836,27 @@ fn wasm_import_value( } let scope = crate::gc::RuntimeHandleScope::new(); let imports = scope.root_nanbox_f64(instance_imports(context)); - let imports_value = JSValue::from_bits(imports.get_nanbox_f64().to_bits()); - if !imports_value.is_pointer() { + if !JSValue::from_bits(imports.get_nanbox_f64().to_bits()).is_pointer() { return nanbox_undefined(); } let module_bytes = unsafe { std::slice::from_raw_parts(module, module_len) }; let module_key = scope.root_string_ptr(named_key(module_bytes)); let module_value = scope.root_nanbox_f64(module_key.with_const_ptr( |k: *const crate::string::StringHeader| { + let imports_value = JSValue::from_bits(imports.get_nanbox_f64().to_bits()); crate::object::js_object_get_field_by_name_f64( imports_value.as_pointer::(), k, ) }, )); - let module_object = JSValue::from_bits(module_value.get_nanbox_f64().to_bits()); - if !module_object.is_pointer() { + if !JSValue::from_bits(module_value.get_nanbox_f64().to_bits()).is_pointer() { return nanbox_undefined(); } let name_bytes = unsafe { std::slice::from_raw_parts(name, name_len) }; let name_key = scope.root_string_ptr(named_key(name_bytes)); name_key.with_const_ptr(|k: *const crate::string::StringHeader| { + let module_object = JSValue::from_bits(module_value.get_nanbox_f64().to_bits()); crate::object::js_object_get_field_by_name_f64( module_object.as_pointer::(), k, @@ -1033,6 +1051,7 @@ fn make_export_function( .get_raw_mut_ptr::() .is_null() { + drop_host_extern_handle(external as usize); return nanbox_undefined(); } crate::closure::js_register_closure_arity(func_ptr, declared_arity); @@ -1049,7 +1068,9 @@ fn make_export_function( closure_ptr, std::str::from_utf8(name).unwrap_or("wasm"), ); - crate::value::js_nanbox_pointer(closure_ptr as i64) + closure.with_mut_ptr(|closure: *mut crate::closure::ClosureHeader| { + crate::value::js_nanbox_pointer(closure as i64) + }) } fn table_method_context<'scope>( @@ -1159,8 +1180,9 @@ extern "C" fn js_wasm_table_get(closure: *const crate::closure::ClosureHeader, i } else { f64::from_bits(bits) }; - let values_ptr = - values_value.as_pointer::() as *mut crate::array::ArrayHeader; + let values_ptr = JSValue::from_bits(values.get_nanbox_f64().to_bits()) + .as_pointer::() + as *mut crate::array::ArrayHeader; crate::array::js_array_set_f64(values_ptr, index as u32, value); value } @@ -1314,22 +1336,38 @@ fn make_table_method( fn make_table_function(external: *mut c_void) -> f64 { let arity = unsafe { perry_wasm_host_func_arity(external) }; if arity == usize::MAX { + drop_host_extern_handle(external as usize); return nanbox_undefined(); } let (func_ptr, declared_arity) = wasm_export_call_shim_for_arity(arity); - let closure = crate::closure::js_closure_alloc(func_ptr, 7); - if closure.is_null() { + let scope = crate::gc::RuntimeHandleScope::new(); + let closure = scope.root_raw_mut_ptr(crate::closure::js_closure_alloc(func_ptr, 7)); + if closure.with_mut_ptr(|closure: *mut crate::closure::ClosureHeader| closure.is_null()) { + drop_host_extern_handle(external as usize); return nanbox_undefined(); } crate::closure::js_register_closure_arity(func_ptr, declared_arity); for index in 0..6 { - crate::closure::js_closure_set_capture_f64(closure, index, nanbox_undefined()); + closure.with_mut_ptr(|closure: *mut crate::closure::ClosureHeader| { + crate::closure::js_closure_set_capture_f64(closure, index, nanbox_undefined()) + }); } - crate::closure::js_closure_set_capture_f64(closure, 0, 0.0); - crate::closure::js_closure_set_capture_f64(closure, 5, 0.0); - crate::closure::js_closure_set_capture_f64(closure, 6, external as usize as f64); - crate::object::set_bound_native_closure_name(closure, "wasm-table-function"); - crate::value::js_nanbox_pointer(closure as i64) + closure.with_mut_ptr(|closure: *mut crate::closure::ClosureHeader| { + crate::closure::js_closure_set_capture_f64(closure, 0, 0.0) + }); + closure.with_mut_ptr(|closure: *mut crate::closure::ClosureHeader| { + crate::closure::js_closure_set_capture_f64(closure, 5, 0.0) + }); + closure.with_mut_ptr(|closure: *mut crate::closure::ClosureHeader| { + crate::closure::js_closure_set_capture_f64(closure, 6, external as usize as f64) + }); + closure.with_mut_ptr(|closure: *mut crate::closure::ClosureHeader| { + crate::object::set_bound_native_closure_name(closure, "wasm-table-function") + }); + closure.with_mut_ptr(|closure: *mut crate::closure::ClosureHeader| { + crate::closure::register_wasm_funcref_external(closure as usize, external as usize); + crate::value::js_nanbox_pointer(closure as i64) + }) } fn make_table_object(external: *mut c_void, inst: *mut c_void, name: f64, receiver: f64) -> f64 { @@ -1820,9 +1858,10 @@ pub extern "C" fn js_webassembly_instance_new( ) } -/// `WebAssembly.instantiate(bytes, imports?)` returns the standard instance -/// result shape. Imported numeric functions are resolved from the JS imports -/// object by module/name and called synchronously by the wasmi host. +/// `WebAssembly.instantiate(source, imports?)` resolves to an `Instance` for a +/// compiled `Module`, or the standard `{ module, instance }` result for bytes. +/// Imported numeric functions are resolved from the JS imports object by +/// module/name and called synchronously by the wasmi host. #[no_mangle] pub extern "C" fn js_webassembly_instantiate(bytes_jsval: f64, imports_jsval: f64) -> f64 { let scope = crate::gc::RuntimeHandleScope::new(); @@ -1845,7 +1884,12 @@ pub extern "C" fn js_webassembly_instantiate(bytes_jsval: f64, imports_jsval: f6 "WebAssembly.instantiate(): instantiation failed", )); } - return make_instance_value(module, inst, imports.get_nanbox_f64(), nanbox_undefined()); + return resolved_promise_value(make_instance_value( + module, + inst, + imports.get_nanbox_f64(), + nanbox_undefined(), + )); } let Some((ptr, len)) = extract_bytes(bytes_jsval) else { return rejected_promise_value(wasm_type_error_value( diff --git a/crates/perry-runtime/src/webassembly_calls.rs b/crates/perry-runtime/src/webassembly_calls.rs index 73f8826c19..07d1a5e376 100644 --- a/crates/perry-runtime/src/webassembly_calls.rs +++ b/crates/perry-runtime/src/webassembly_calls.rs @@ -17,10 +17,18 @@ extern "C" fn js_wasm_instance_result_then( 1, ) }); + let (fulfilled, value) = match outcome { + Ok(result) => (true, result), + Err(reason) => (false, reason), + }; + let value = scope.root_nanbox_f64(value); let promise = scope.root_raw_mut_ptr(crate::promise::js_promise_new()); - promise.with_mut_ptr(|p: *mut crate::promise::Promise| match outcome { - Ok(result) => crate::promise::js_promise_resolve(p, result), - Err(reason) => crate::promise::js_promise_reject(p, reason), + promise.with_mut_ptr(|p: *mut crate::promise::Promise| { + if fulfilled { + crate::promise::js_promise_resolve(p, value.get_nanbox_f64()); + } else { + crate::promise::js_promise_reject(p, value.get_nanbox_f64()); + } }); promise .with_mut_ptr(|p: *mut crate::promise::Promise| crate::value::js_nanbox_pointer(p as i64)) diff --git a/crates/perry-runtime/src/webassembly_host.rs b/crates/perry-runtime/src/webassembly_host.rs index d33233b389..16d47c5c84 100644 --- a/crates/perry-runtime/src/webassembly_host.rs +++ b/crates/perry-runtime/src/webassembly_host.rs @@ -45,6 +45,7 @@ extern "C" { out_err: *mut *mut c_char, ) -> *mut c_void; pub(super) fn perry_wasm_host_module_drop(module: *mut c_void); + pub(super) fn perry_wasm_host_extern_drop(handle: *mut c_void); pub(super) fn perry_wasm_host_module_exports_len(module: *mut c_void) -> usize; pub(super) fn perry_wasm_host_module_export_at( module: *mut c_void, diff --git a/crates/perry-runtime/tests/android_tls_pool.rs b/crates/perry-runtime/tests/android_tls_pool.rs new file mode 100644 index 0000000000..e5ffd0a5dd --- /dev/null +++ b/crates/perry-runtime/tests/android_tls_pool.rs @@ -0,0 +1,135 @@ +//! #10219: more live Perry declarations than Android's entire pthread key +//! budget. The same binary checks thread isolation and real value destruction. +#![cfg(unix)] +#![recursion_limit = "512"] + +use std::cell::RefCell; +use std::sync::atomic::{AtomicUsize, Ordering}; +static DROPS: AtomicUsize = AtomicUsize::new(0); +struct Probe(usize); +impl Drop for Probe { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } +} +macro_rules! declarations { + ($($key:ident),+ $(,)?) => { + perry_runtime::perry_thread_local! { + $(static $key: RefCell> = RefCell::new(Vec::new());)+ + } + fn visit(mut f: impl FnMut(usize, &'static perry_runtime::tls_hot::HotKey>>)) { + for (i, key) in [$(&$key),+].into_iter().enumerate() { f(i, key); } + } + }; +} +declarations!( + KEY_000, KEY_001, KEY_002, KEY_003, KEY_004, KEY_005, KEY_006, KEY_007, KEY_008, KEY_009, + KEY_010, KEY_011, KEY_012, KEY_013, KEY_014, KEY_015, KEY_016, KEY_017, KEY_018, KEY_019, + KEY_020, KEY_021, KEY_022, KEY_023, KEY_024, KEY_025, KEY_026, KEY_027, KEY_028, KEY_029, + KEY_030, KEY_031, KEY_032, KEY_033, KEY_034, KEY_035, KEY_036, KEY_037, KEY_038, KEY_039, + KEY_040, KEY_041, KEY_042, KEY_043, KEY_044, KEY_045, KEY_046, KEY_047, KEY_048, KEY_049, + KEY_050, KEY_051, KEY_052, KEY_053, KEY_054, KEY_055, KEY_056, KEY_057, KEY_058, KEY_059, + KEY_060, KEY_061, KEY_062, KEY_063, KEY_064, KEY_065, KEY_066, KEY_067, KEY_068, KEY_069, + KEY_070, KEY_071, KEY_072, KEY_073, KEY_074, KEY_075, KEY_076, KEY_077, KEY_078, KEY_079, + KEY_080, KEY_081, KEY_082, KEY_083, KEY_084, KEY_085, KEY_086, KEY_087, KEY_088, KEY_089, + KEY_090, KEY_091, KEY_092, KEY_093, KEY_094, KEY_095, KEY_096, KEY_097, KEY_098, KEY_099, + KEY_100, KEY_101, KEY_102, KEY_103, KEY_104, KEY_105, KEY_106, KEY_107, KEY_108, KEY_109, + KEY_110, KEY_111, KEY_112, KEY_113, KEY_114, KEY_115, KEY_116, KEY_117, KEY_118, KEY_119, + KEY_120, KEY_121, KEY_122, KEY_123, KEY_124, KEY_125, KEY_126, KEY_127, KEY_128, KEY_129, + KEY_130, KEY_131, KEY_132, KEY_133, KEY_134, KEY_135, KEY_136, KEY_137, KEY_138, KEY_139, + KEY_140, KEY_141, KEY_142, KEY_143, KEY_144, KEY_145, KEY_146, KEY_147, KEY_148, KEY_149, + KEY_150, KEY_151, KEY_152, KEY_153, KEY_154, KEY_155, KEY_156, KEY_157, KEY_158, KEY_159, + KEY_160, KEY_161, KEY_162, KEY_163, KEY_164, KEY_165, KEY_166, KEY_167, KEY_168, KEY_169, + KEY_170, KEY_171, KEY_172, KEY_173, KEY_174, KEY_175, KEY_176, KEY_177, KEY_178, KEY_179, + KEY_180, KEY_181, KEY_182, KEY_183, KEY_184, KEY_185, KEY_186, KEY_187, KEY_188, KEY_189, + KEY_190, KEY_191, +); + +#[test] +fn hundreds_of_perry_declarations_initialize_and_drop_on_multiple_threads() { + visit(|i, key| key.with(|value| value.borrow_mut().push(Probe(1000 + i)))); + let barrier = std::sync::Arc::new(std::sync::Barrier::new(8)); + let workers: Vec<_> = (0..8) + .map(|worker| { + let barrier = barrier.clone(); + std::thread::spawn(move || { + visit(|i, key| { + key.with(|value| { + assert!( + value.borrow().is_empty(), + "a worker inherited another thread's storage" + ); + value.borrow_mut().push(Probe(worker * 1000 + i)); + }) + }); + barrier.wait(); + visit(|i, key| { + key.with(|value| assert_eq!(value.borrow()[0].0, worker * 1000 + i)) + }); + }) + }) + .collect(); + for worker in workers { + worker.join().unwrap(); + } + assert_eq!( + DROPS.load(Ordering::SeqCst), + 192 * 8, + "worker values must be destroyed exactly once" + ); + visit(|i, key| key.with(|value| assert_eq!(value.borrow()[0].0, 1000 + i))); +} + +// A destructor owned by another library can run after Perry's entire pool. +// Keep try_with fallible then, including across repeated POSIX destructor passes. +#[cfg(target_os = "android")] +#[test] +fn later_pthread_destructors_cannot_resurrect_the_hot_cache() { + use std::sync::atomic::AtomicU8; + static OBSERVED: AtomicU8 = AtomicU8::new(0); + perry_runtime::perry_thread_local! { + static LATE_PROBE: RefCell> = RefCell::new(Vec::new()); + } + struct Callback { + key: libc::pthread_key_t, + round: u8, + } + unsafe extern "C" fn callback(raw: *mut libc::c_void) { + let state = &mut *raw.cast::(); + state.round += 1; + if state.round < 3 { + assert_eq!(libc::pthread_setspecific(state.key, raw), 0); + return; + } + let observed = match std::panic::catch_unwind(|| LATE_PROBE.try_with(|_| ())) { + Ok(Err(_)) => 1, + Ok(Ok(_)) => 2, + Err(_) => 3, + }; + OBSERVED.store(observed, Ordering::SeqCst); + drop(Box::from_raw(raw.cast::())); + } + // By the third callback pass the pool has been destroyed, regardless of + // which key the platform visits first within each pass. + let key = std::thread::spawn(|| { + LATE_PROBE.with(|value| value.borrow_mut().push(1)); + let mut key = 0; + assert_eq!( + unsafe { libc::pthread_key_create(&mut key, Some(callback)) }, + 0 + ); + let state = Box::into_raw(Box::new(Callback { key, round: 0 })); + assert_eq!(unsafe { libc::pthread_setspecific(key, state.cast()) }, 0); + key + }) + .join() + .unwrap(); + unsafe { + libc::pthread_key_delete(key); + } + assert_eq!( + OBSERVED.load(Ordering::SeqCst), + 1, + "1=AccessError, 2=resurrected value, 3=panic, 0=callback never ran" + ); +} diff --git a/crates/perry-runtime/tests/fixtures/memory_profile_probe.c b/crates/perry-runtime/tests/fixtures/memory_profile_probe.c new file mode 100644 index 0000000000..2e6c554638 --- /dev/null +++ b/crates/perry-runtime/tests/fixtures/memory_profile_probe.c @@ -0,0 +1,29 @@ +/* Linked with the production policy object and the pinned mimalloc source. + * Allocate in a constructor before main to catch policies applied too late. */ +#include +#include +#include +#include + +extern void perry_retain_memory_profile_init(void); +extern long perry_memory_profile_allow_thp(void); +static void *early_allocation; +static int early_thp_disabled = -1; + +__attribute__((constructor(102))) +static void allocate_before_main(void) { + early_allocation = mi_malloc(64); + if (early_allocation != NULL) { + ((volatile unsigned char *)early_allocation)[0] = 42; + } + early_thp_disabled = prctl(PR_GET_THP_DISABLE, 0, 0, 0, 0); +} + +int main(int argc, char **argv) { + perry_retain_memory_profile_init(); + if (argc != 3 || early_allocation == NULL) return 2; + const long option = perry_memory_profile_allow_thp(); + printf("allow_thp=%ld early_thp_disabled=%d\n", option, early_thp_disabled); + mi_free(early_allocation); + return option == strtol(argv[1], NULL, 10) && early_thp_disabled == atoi(argv[2]) ? 0 : 1; +} diff --git a/crates/perry-ui-android/src/ffi/tabbar_layout.rs b/crates/perry-ui-android/src/ffi/tabbar_layout.rs index c85e89f4cd..ceb49c7ae8 100644 --- a/crates/perry-ui-android/src/ffi/tabbar_layout.rs +++ b/crates/perry-ui-android/src/ffi/tabbar_layout.rs @@ -375,3 +375,7 @@ pub extern "C" fn perry_ui_app_set_activation_policy(_app_handle: i64, _value_pt /// Issue #1280 — Android apps run in a single full-screen Activity. Stub. #[no_mangle] pub extern "C" fn perry_ui_app_set_window_state(_app_handle: i64, _value_ptr: i64) {} + +/// Frame persistence only applies to repositionable desktop windows. +#[no_mangle] +pub extern "C" fn perry_ui_app_set_frame_autosave_name(_app_handle: i64, _value_ptr: i64) {} diff --git a/crates/perry-ui-gtk4/src/app.rs b/crates/perry-ui-gtk4/src/app.rs index 3904b7ab43..30758956db 100644 --- a/crates/perry-ui-gtk4/src/app.rs +++ b/crates/perry-ui-gtk4/src/app.rs @@ -73,6 +73,7 @@ struct AppEntry { activation_policy: Option, /// Issue #1280 — "maximized" | "fullscreen" | None (= "normal"). window_state: Option, + frame_autosave_name: Option, } extern "C" { @@ -122,6 +123,7 @@ pub fn app_create(title_ptr: *const u8, width: f64, height: f64) -> i64 { vibrancy: None, activation_policy: PENDING_ACTIVATION_POLICY.with(|p| p.borrow().clone()), window_state: None, + frame_autosave_name: None, }); apps.len() as i64 // 1-based handle }) @@ -291,8 +293,12 @@ pub fn app_run(_app_handle: i64) { // Issue #1280 — initial window state. GTK4 needs maximize() / // fullscreen() called before `present()` so the window appears // already in the requested state rather than flickering. - if let Some(ref state) = entry.window_state { - match state.as_str() { + let restored_state = entry + .frame_autosave_name + .as_deref() + .and_then(|name| crate::frame_persistence::install(&window, app, name)); + if let Some(state) = restored_state.or(entry.window_state.as_deref()) { + match state { "maximized" => window.maximize(), "fullscreen" => window.fullscreen(), _ => {} @@ -578,6 +584,19 @@ pub fn app_set_activation_policy(app_handle: i64, value_ptr: *const u8) { }); } +/// Record the stable name; restore after window setup and before presentation. +pub fn app_set_frame_autosave_name(app_handle: i64, value_ptr: *const u8) { + let name = unsafe { str_from_header(value_ptr) }; + APPS.with(|apps| { + if let Some(entry) = apps + .borrow_mut() + .get_mut(app_handle.saturating_sub(1) as usize) + { + entry.frame_autosave_name = (!name.is_empty()).then_some(name); + } + }); +} + /// Issue #1280 — initial window state. value_ptr points at a StringHeader /// for one of "normal" | "maximized" | "fullscreen". Anything else is /// silently ignored; the state is applied just before `window.present()`. diff --git a/crates/perry-ui-gtk4/src/ffi/app_window.rs b/crates/perry-ui-gtk4/src/ffi/app_window.rs index d4aeadda49..0bdb349abd 100644 --- a/crates/perry-ui-gtk4/src/ffi/app_window.rs +++ b/crates/perry-ui-gtk4/src/ffi/app_window.rs @@ -143,3 +143,9 @@ pub extern "C" fn perry_ui_window_set_size(window_handle: i64, width: f64, heigh pub extern "C" fn perry_ui_window_on_focus_lost(window_handle: i64, callback: f64) { window::on_focus_lost(window_handle, callback); } + +/// Opt in to desktop window frame persistence with an application-local key. +#[no_mangle] +pub extern "C" fn perry_ui_app_set_frame_autosave_name(app_handle: i64, value_ptr: i64) { + app::app_set_frame_autosave_name(app_handle, value_ptr as *const u8); +} diff --git a/crates/perry-ui-gtk4/src/frame_persistence.rs b/crates/perry-ui-gtk4/src/frame_persistence.rs new file mode 100644 index 0000000000..7877f1c88c --- /dev/null +++ b/crates/perry-ui-gtk4/src/frame_persistence.rs @@ -0,0 +1,56 @@ +use gtk4::prelude::*; +use gtk4::{Application, ApplicationWindow}; +use perry_ui::frame::{FrameStore, WindowFrame, WindowState}; + +fn save(window: &ApplicationWindow, store: &FrameStore) { + // GTK4 updates the default size when users resize, and preserves the + // normal size while maximized/fullscreen. Allocation includes overrides. + let (width, height) = window.default_size(); + let state = if window.is_fullscreen() { + WindowState::Fullscreen + } else if window.is_maximized() { + WindowState::Maximized + } else { + WindowState::Normal + }; + let _ = store.save(WindowFrame { + x: 0, + y: 0, + width, + height, + state, + }); +} + +pub(crate) fn install( + window: &ApplicationWindow, + app: &Application, + name: &str, +) -> Option<&'static str> { + let store = FrameStore::new(name)?; + let restored = store.load(); + if let Some(frame) = restored { + window.set_default_size(frame.width, frame.height); + } + let close_store = store.clone(); + window.connect_close_request(move |window| { + save(window, &close_store); + gtk4::glib::Propagation::Proceed + }); + // Application.quit() need not emit close-request. Use a weak reference + // so this callback does not keep a closed window alive. + let weak_window = window.downgrade(); + app.connect_shutdown(move |_| { + if let Some(window) = weak_window.upgrade() { + // A previously closed window was already saved by close-request. + if window.is_visible() { + save(&window, &store); + } + } + }); + restored.map(|frame| match frame.state { + WindowState::Normal => "normal", + WindowState::Maximized => "maximized", + WindowState::Fullscreen => "fullscreen", + }) +} diff --git a/crates/perry-ui-gtk4/src/lib.rs b/crates/perry-ui-gtk4/src/lib.rs index f51ea76796..40e8974d46 100644 --- a/crates/perry-ui-gtk4/src/lib.rs +++ b/crates/perry-ui-gtk4/src/lib.rs @@ -9,6 +9,7 @@ pub mod deeplinks_stub; pub mod dialog; pub mod drag_drop; pub mod file_dialog; +mod frame_persistence; mod gc; pub mod issue_552_stub; pub mod keyboard; diff --git a/crates/perry-ui-ios/src/ffi/dialogs_lifecycle.rs b/crates/perry-ui-ios/src/ffi/dialogs_lifecycle.rs index 3a2aa435fb..6329836afc 100644 --- a/crates/perry-ui-ios/src/ffi/dialogs_lifecycle.rs +++ b/crates/perry-ui-ios/src/ffi/dialogs_lifecycle.rs @@ -273,3 +273,7 @@ pub extern "C" fn perry_ui_toolbar_add_item( #[no_mangle] pub extern "C" fn perry_ui_toolbar_attach(_toolbar: i64) {} + +/// Frame persistence only applies to repositionable desktop windows. +#[no_mangle] +pub extern "C" fn perry_ui_app_set_frame_autosave_name(_app_handle: i64, _value_ptr: i64) {} diff --git a/crates/perry-ui-macos/src/app.rs b/crates/perry-ui-macos/src/app.rs index 3818a0e584..0b9163a948 100644 --- a/crates/perry-ui-macos/src/app.rs +++ b/crates/perry-ui-macos/src/app.rs @@ -52,6 +52,7 @@ pub(crate) struct AppEntry { /// Issue #1280 — initial window state applied on `app_run`. `zoom:` / /// `toggleFullScreen:` need the window to be key+ordered front first. pub(crate) window_state: Option, + frame_autosave_name: Option, } /// Issue #1280 — initial window state for the main app window. @@ -120,6 +121,7 @@ pub fn app_create(title_ptr: *const u8, width: f64, height: f64) -> i64 { window, _root_widget: None, window_state: None, + frame_autosave_name: None, }); apps.len() as i64 // 1-based handle }) @@ -445,7 +447,17 @@ pub fn app_run(_app_handle: i64) { APPS.with(|a| { let apps = a.borrow(); for entry in apps.iter() { - entry.window.center(); + // Restore after body/style configuration and before showing. Centering + // unconditionally here used to discard the saved position (#10170). + let restored = entry.frame_autosave_name.as_ref().is_some_and(|name| { + let name = NSString::from_str(name); + let restored = entry.window.setFrameUsingName(&name); + let _ = entry.window.setFrameAutosaveName(&name); + restored + }); + if !restored { + entry.window.center(); + } // Validate window is on a visible screen — if the position was // restored from a previous session with a different display setup, @@ -502,7 +514,7 @@ pub fn app_run(_app_handle: i64) { // toggleFullScreen: path enters native fullscreen on its own // Space. Both need the window to be key+ordered front, which is // why this runs here rather than in the setter. - if let Some(state) = entry.window_state { + if let Some(state) = entry.window_state.filter(|_| !restored) { unsafe { match state { WindowState::Maximized => { @@ -698,6 +710,20 @@ pub fn set_max_size(app_handle: i64, w: f64, h: f64) { }); } +/// Record the stable name; restore after window setup and before presentation. +pub fn set_frame_autosave_name(app_handle: i64, value_ptr: *const u8) { + let name = unsafe { str_from_header(value_ptr) }; + let key = perry_ui::frame::autosave_key(&name); + APPS.with(|apps| { + if let Some(entry) = apps + .borrow_mut() + .get_mut(app_handle.saturating_sub(1) as usize) + { + entry.frame_autosave_name = key; + } + }); +} + /// Issue #1280 — record the requested initial window state. Applied in /// `app_run` after the window is key+ordered front (zoom: / toggleFullScreen: /// don't take effect on a window that hasn't been shown yet). diff --git a/crates/perry-ui-macos/src/lib_ffi/core_widgets.rs b/crates/perry-ui-macos/src/lib_ffi/core_widgets.rs index 4dad8178bb..6107210f34 100644 --- a/crates/perry-ui-macos/src/lib_ffi/core_widgets.rs +++ b/crates/perry-ui-macos/src/lib_ffi/core_widgets.rs @@ -532,3 +532,9 @@ pub extern "C" fn perry_ui_button_set_content_tint_color( pub extern "C" fn perry_ui_button_set_image_position(handle: i64, position: i64) { widgets::button::set_image_position(handle, position); } + +/// Opt in to desktop window frame persistence with an application-local key. +#[no_mangle] +pub extern "C" fn perry_ui_app_set_frame_autosave_name(app_handle: i64, value_ptr: i64) { + app::set_frame_autosave_name(app_handle, value_ptr as *const u8); +} diff --git a/crates/perry-ui-tvos/src/ffi/app_keychain.rs b/crates/perry-ui-tvos/src/ffi/app_keychain.rs index b4bea08c2b..e7fc4b8ff0 100644 --- a/crates/perry-ui-tvos/src/ffi/app_keychain.rs +++ b/crates/perry-ui-tvos/src/ffi/app_keychain.rs @@ -163,3 +163,7 @@ pub extern "C" fn perry_system_keychain_delete(key_ptr: i64) { SecItemDelete(&*query as *const _ as *const std::ffi::c_void); } } + +/// Frame persistence only applies to repositionable desktop windows. +#[no_mangle] +pub extern "C" fn perry_ui_app_set_frame_autosave_name(_app_handle: i64, _value_ptr: i64) {} diff --git a/crates/perry-ui-visionos/src/ffi_system.rs b/crates/perry-ui-visionos/src/ffi_system.rs index 24e0d276d9..7d1a98127c 100644 --- a/crates/perry-ui-visionos/src/ffi_system.rs +++ b/crates/perry-ui-visionos/src/ffi_system.rs @@ -690,3 +690,7 @@ pub extern "C" fn perry_ui_toolbar_add_item( #[no_mangle] pub extern "C" fn perry_ui_toolbar_attach(_toolbar: i64) {} + +/// Frame persistence only applies to repositionable desktop windows. +#[no_mangle] +pub extern "C" fn perry_ui_app_set_frame_autosave_name(_app_handle: i64, _value_ptr: i64) {} diff --git a/crates/perry-ui-watchos/src/lib.rs b/crates/perry-ui-watchos/src/lib.rs index 8b3dbc3e0d..f17bd64ad6 100644 --- a/crates/perry-ui-watchos/src/lib.rs +++ b/crates/perry-ui-watchos/src/lib.rs @@ -1960,3 +1960,7 @@ pub extern "C" fn perry_ui_canvas_draw_image( _dh: f64, ) { } + +/// Frame persistence only applies to repositionable desktop windows. +#[no_mangle] +pub extern "C" fn perry_ui_app_set_frame_autosave_name(_app_handle: i64, _value_ptr: i64) {} diff --git a/crates/perry-ui-windows-winui/src/app.rs b/crates/perry-ui-windows-winui/src/app.rs index 49963528d3..8c18fb45f2 100644 --- a/crates/perry-ui-windows-winui/src/app.rs +++ b/crates/perry-ui-windows-winui/src/app.rs @@ -3,6 +3,7 @@ use std::cell::RefCell; use std::time::Duration; +use perry_ui_windows::frame_persistence::{self, FrameStore, WindowState}; use windows::Win32::Foundation::HWND; use windows_reactor::winui::host::PresenterKind; use windows_reactor::{App, Backdrop, DispatcherTimer, InnerConstraints}; @@ -27,6 +28,8 @@ struct AppState { min_size: Option<(f64, f64)>, max_size: Option<(f64, f64)>, presenter: PresenterKind, + maximized: bool, + frame_autosave_name: Option, } thread_local! { @@ -168,6 +171,39 @@ pub fn app_run(app_handle: i64) { .inner_constraints(constraints) .presenter(state.presenter) .backdrop(Backdrop::Mica); + let store = state + .frame_autosave_name + .as_deref() + .and_then(FrameStore::new); + if store.is_some() || state.maximized { + app = app.on_window_created(move |host| { + let Ok(handle) = host.window_handle() else { + return; + }; + let hwnd = HWND(handle as *mut _); + let fallback = if state.presenter == PresenterKind::FullScreen { + WindowState::Fullscreen + } else if state.maximized { + WindowState::Maximized + } else { + WindowState::Normal + }; + let restored = store.map_or(fallback, |store| { + frame_persistence::install(hwnd, store, fallback) + }); + host.set_presenter(if restored == WindowState::Fullscreen { + PresenterKind::FullScreen + } else { + PresenterKind::Default + }); + if restored == WindowState::Maximized { + use windows::Win32::UI::WindowsAndMessaging::{ShowWindow, SW_SHOWMAXIMIZED}; + unsafe { + let _ = ShowWindow(hwnd, SW_SHOWMAXIMIZED); + } + } + }); + } if app_callback(&ON_TERMINATE) != 0 { app = app.on_exit(move || invoke_app_callback(&ON_TERMINATE)); } @@ -230,7 +266,21 @@ pub fn set_window_state(app_handle: i64, value_ptr: *const u8) { } else { PresenterKind::Default }; - with_app_mut(app_handle, |app| app.presenter = presenter); + with_app_mut(app_handle, |app| { + app.presenter = presenter; + app.maximized = value.eq_ignore_ascii_case("maximized"); + }); +} + +pub fn set_frame_autosave_name(app_handle: i64, value_ptr: *const u8) { + if !is_fluent() { + perry_ui_windows::app::set_frame_autosave_name(app_handle, value_ptr); + return; + } + let name = unsafe { perry_ffi::copy_string_from_raw(value_ptr) }; + with_app_mut(app_handle, |app| { + app.frame_autosave_name = (!name.is_empty()).then_some(name); + }); } pub fn set_timer(interval_ms: f64, callback: f64) { diff --git a/crates/perry-ui-windows/src/app.rs b/crates/perry-ui-windows/src/app.rs index 51792a6444..bc45b50132 100644 --- a/crates/perry-ui-windows/src/app.rs +++ b/crates/perry-ui-windows/src/app.rs @@ -337,6 +337,7 @@ pub(crate) struct AppEntry { /// Issue #1280 — initial window state requested by App({ windowState }). /// Applied at app_run time; None / "normal" => SW_SHOW. window_state: Option, + frame_autosave_name: Option, /// Activation policy ("regular" | "accessory" | "background"). For /// accessory/background apps the launch window is suppressed (the tray /// owns presentation), mirroring the macOS/GTK4 backends. @@ -516,6 +517,7 @@ pub fn app_create(title_ptr: *const u8, width: f64, height: f64) -> i64 { min_size: None, max_size: None, window_state: None, + frame_autosave_name: None, activation_policy: PENDING_ACTIVATION_POLICY.with(|p| p.borrow().clone()), }); apps.len() as i64 @@ -534,6 +536,7 @@ pub fn app_create(title_ptr: *const u8, width: f64, height: f64) -> i64 { min_size: None, max_size: None, window_state: None, + frame_autosave_name: None, activation_policy: PENDING_ACTIVATION_POLICY.with(|p| p.borrow().clone()), }); apps.len() as i64 @@ -629,71 +632,93 @@ pub fn app_run(app_handle: i64) { #[cfg(target_os = "windows")] { - APPS.with(|apps| { + // Native placement/show calls synchronously dispatch messages. Release + // the app table borrow first so callbacks can read or mutate app state. + let config = APPS.with(|apps| { let apps = apps.borrow(); - let idx = (app_handle - 1) as usize; - if idx < apps.len() { - let hwnd = apps[idx].hwnd; - let state = apps[idx].window_state; - // Accessory/background apps stay window-less at launch (the - // tray owns presentation); open windows on demand instead. - // Mirrors the macOS/GTK4 backends. - let suppress_window = matches!( - apps[idx].activation_policy.as_deref(), - Some("accessory") | Some("background") - ); - // Pre-App() `appSetActivationPolicy` couldn't apply the - // taskbar style (the hwnd didn't exist yet); do it here. - if suppress_window { - unsafe { - let ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE) as u32; - let new_style = (ex_style & !WS_EX_APPWINDOW.0) | WS_EX_TOOLWINDOW.0; - SetWindowLongW(hwnd, GWL_EXSTYLE, new_style as i32); - } + let entry = apps.get(app_handle.saturating_sub(1) as usize)?; + Some(( + entry.hwnd, + entry.window_state, + entry.activation_policy.clone(), + entry.frame_autosave_name.clone(), + )) + }); + if let Some((hwnd, mut state, activation_policy, frame_autosave_name)) = config { + if let Some(store) = frame_autosave_name + .as_deref() + .and_then(crate::frame_persistence::FrameStore::new) + { + use crate::frame_persistence::WindowState as SavedState; + let fallback = match state { + Some(WindowState::Maximized) => SavedState::Maximized, + Some(WindowState::Fullscreen) => SavedState::Fullscreen, + None => SavedState::Normal, + }; + state = match crate::frame_persistence::install(hwnd, store, fallback) { + SavedState::Maximized => Some(WindowState::Maximized), + SavedState::Fullscreen => Some(WindowState::Fullscreen), + SavedState::Normal => None, + }; + } + // Accessory/background apps stay window-less at launch (the + // tray owns presentation); open windows on demand instead. + // Mirrors the macOS/GTK4 backends. + let suppress_window = matches!( + activation_policy.as_deref(), + Some("accessory") | Some("background") + ); + // Pre-App() `appSetActivationPolicy` couldn't apply the + // taskbar style (the hwnd didn't exist yet); do it here. + if suppress_window { + unsafe { + let ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE) as u32; + let new_style = (ex_style & !WS_EX_APPWINDOW.0) | WS_EX_TOOLWINDOW.0; + SetWindowLongW(hwnd, GWL_EXSTYLE, new_style as i32); } - if !suppress_window { - unsafe { - // Issue #1280 — apply requested initial window state. - // Fullscreen on Win32 = drop WS_OVERLAPPEDWINDOW frame and - // resize to the monitor's full rect (not just the work - // area, which excludes the taskbar). Maximized = standard - // SW_SHOWMAXIMIZED which respects the taskbar. - match state { - Some(WindowState::Fullscreen) => { - let style = GetWindowLongW(hwnd, GWL_STYLE) as u32; - let new_style = style & !WS_OVERLAPPEDWINDOW.0; - SetWindowLongW(hwnd, GWL_STYLE, new_style as i32); - let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - let mut mi = MONITORINFO { - cbSize: std::mem::size_of::() as u32, - ..Default::default() - }; - if GetMonitorInfoW(monitor, &mut mi).as_bool() { - let r = mi.rcMonitor; - let _ = SetWindowPos( - hwnd, - Some(HWND_TOP), - r.left, - r.top, - r.right - r.left, - r.bottom - r.top, - SWP_NOOWNERZORDER | SWP_FRAMECHANGED, - ); - } - let _ = ShowWindow(hwnd, SW_SHOW); - } - Some(WindowState::Maximized) => { - let _ = ShowWindow(hwnd, SW_SHOWMAXIMIZED); - } - None => { - let _ = ShowWindow(hwnd, SW_SHOW); + } + if !suppress_window { + unsafe { + // Issue #1280 — apply requested initial window state. + // Fullscreen on Win32 = drop WS_OVERLAPPEDWINDOW frame and + // resize to the monitor's full rect (not just the work + // area, which excludes the taskbar). Maximized = standard + // SW_SHOWMAXIMIZED which respects the taskbar. + match state { + Some(WindowState::Fullscreen) => { + let style = GetWindowLongW(hwnd, GWL_STYLE) as u32; + let new_style = style & !WS_OVERLAPPEDWINDOW.0; + SetWindowLongW(hwnd, GWL_STYLE, new_style as i32); + let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + let mut mi = MONITORINFO { + cbSize: std::mem::size_of::() as u32, + ..Default::default() + }; + if GetMonitorInfoW(monitor, &mut mi).as_bool() { + let r = mi.rcMonitor; + let _ = SetWindowPos( + hwnd, + Some(HWND_TOP), + r.left, + r.top, + r.right - r.left, + r.bottom - r.top, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED, + ); } + let _ = ShowWindow(hwnd, SW_SHOW); + } + Some(WindowState::Maximized) => { + let _ = ShowWindow(hwnd, SW_SHOWMAXIMIZED); + } + None => { + let _ = ShowWindow(hwnd, SW_SHOW); } - let _ = UpdateWindow(hwnd); } + let _ = UpdateWindow(hwnd); } } - }); + } // PERRY_UI_TEST_MODE: schedule a one-shot exit timer so CI can verify // that the app launched without a human keeping it open. @@ -1016,6 +1041,19 @@ pub fn set_max_size(app_handle: i64, w: f64, h: f64) { }); } +/// Record the stable name; restore after window setup and before presentation. +pub fn set_frame_autosave_name(app_handle: i64, value_ptr: *const u8) { + let name = unsafe { str_from_header(value_ptr) }; + APPS.with(|apps| { + if let Some(entry) = apps + .borrow_mut() + .get_mut(app_handle.saturating_sub(1) as usize) + { + entry.frame_autosave_name = (!name.is_empty()).then_some(name); + } + }); +} + /// Issue #1280 — record the requested initial window state. The state is /// applied in `app_run` (Win32 needs the window to exist + be ready before /// `ShowWindow(SW_SHOWMAXIMIZED)` or the fullscreen frame swap takes effect). diff --git a/crates/perry-ui-windows/src/ffi/app_window.rs b/crates/perry-ui-windows/src/ffi/app_window.rs index ba8bbc968e..52040a5dce 100644 --- a/crates/perry-ui-windows/src/ffi/app_window.rs +++ b/crates/perry-ui-windows/src/ffi/app_window.rs @@ -111,3 +111,9 @@ pub extern "C" fn perry_ui_window_set_size(window_handle: i64, width: f64, heigh pub extern "C" fn perry_ui_window_on_focus_lost(window_handle: i64, callback: f64) { window::on_focus_lost(window_handle, callback); } + +/// Opt in to desktop window frame persistence with an application-local key. +#[no_mangle] +pub extern "C" fn perry_ui_app_set_frame_autosave_name(app_handle: i64, value_ptr: i64) { + app::set_frame_autosave_name(app_handle, value_ptr as *const u8); +} diff --git a/crates/perry-ui-windows/src/frame_persistence.rs b/crates/perry-ui-windows/src/frame_persistence.rs new file mode 100644 index 0000000000..72d6e4c1f7 --- /dev/null +++ b/crates/perry-ui-windows/src/frame_persistence.rs @@ -0,0 +1,289 @@ +//! Native placement persistence shared by Win32 and WinUI windows. + +use std::cell::RefCell; +use std::collections::HashMap; + +use perry_ui::frame::WindowFrame; +pub use perry_ui::frame::{FrameStore, WindowState}; +use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM}; +use windows::Win32::UI::Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass}; +use windows::Win32::UI::WindowsAndMessaging::*; + +const SUBCLASS_ID: usize = 10170; + +struct SavedWindow { + store: FrameStore, + frame: WindowFrame, + fullscreen: bool, +} + +thread_local! { + static WINDOWS: RefCell> = RefCell::new(HashMap::new()); +} + +fn placement(hwnd: HWND) -> Option { + let mut placement = WINDOWPLACEMENT { + length: std::mem::size_of::() as u32, + ..Default::default() + }; + unsafe { + GetWindowPlacement(hwnd, &mut placement).ok()?; + } + Some(placement) +} + +fn frame_from_placement(value: &WINDOWPLACEMENT, previous: WindowState) -> Option { + let rect = value.rcNormalPosition; + Some(WindowFrame { + x: rect.left, + y: rect.top, + width: rect.right.checked_sub(rect.left)?, + height: rect.bottom.checked_sub(rect.top)?, + // Never reopen minimized. Preserve the last non-minimized state, + // including a maximized window that was minimized before quitting. + state: match SHOW_WINDOW_CMD(value.showCmd as i32) { + SW_SHOWMAXIMIZED => WindowState::Maximized, + SW_SHOWMINIMIZED | SW_MINIMIZE | SW_SHOWMINNOACTIVE => previous, + _ => WindowState::Normal, + }, + }) +} + +/// Restore while hidden, then observe placement changes. Returns the saved +/// state (if any); the caller applies it when showing the window. +pub fn install(hwnd: HWND, store: FrameStore, fallback: WindowState) -> WindowState { + let saved = store.load(); + let mut restored = None; + if let Some(frame) = saved { + let placement = WINDOWPLACEMENT { + length: std::mem::size_of::() as u32, + showCmd: SW_HIDE.0 as u32, + rcNormalPosition: RECT { + left: frame.x, + top: frame.y, + right: frame.x + frame.width, + bottom: frame.y + frame.height, + }, + ..Default::default() + }; + // SetWindowPlacement uses workspace coordinates and brings a frame + // back onto an available monitor after the display setup changes. + if unsafe { SetWindowPlacement(hwnd, &placement) }.is_ok() { + restored = Some(frame); + } + } + let state = restored.map_or(fallback, |frame| frame.state); + let normal_frame = placement(hwnd).and_then(|value| frame_from_placement(&value, state)); + if let Some(mut frame) = normal_frame { + frame.state = state; + WINDOWS.with(|windows| { + windows.borrow_mut().insert( + hwnd.0 as isize, + SavedWindow { + store, + frame, + fullscreen: state == WindowState::Fullscreen, + }, + ); + }); + if !unsafe { SetWindowSubclass(hwnd, Some(subclass_proc), SUBCLASS_ID, 0) }.as_bool() { + WINDOWS.with(|windows| windows.borrow_mut().remove(&(hwnd.0 as isize))); + } + } + state +} + +fn update(hwnd: HWND, persist: bool) { + let current = placement(hwnd); + WINDOWS.with(|windows| { + let mut windows = windows.borrow_mut(); + let Some(saved) = windows.get_mut(&(hwnd.0 as isize)) else { + return; + }; + // Win32 fullscreen replaces the normal rect with the monitor rect. + // Keep the pre-fullscreen placement so restore-down geometry survives. + if !saved.fullscreen { + if let Some(frame) = + current.and_then(|value| frame_from_placement(&value, saved.frame.state)) + { + saved.frame = frame; + } + } + if persist { + let _ = saved.store.save(saved.frame); + } + }); +} + +unsafe extern "system" fn subclass_proc( + hwnd: HWND, + message: u32, + wparam: WPARAM, + lparam: LPARAM, + _id: usize, + _data: usize, +) -> LRESULT { + // WinUI's Closed callback exits the process, so save before forwarding + // WM_CLOSE. WM_QUERYENDSESSION also covers an OS logout/shutdown. + if matches!( + message, + WM_CLOSE | WM_QUERYENDSESSION | WM_DESTROY | WM_EXITSIZEMOVE + ) { + update(hwnd, true); + } + if message == WM_NCDESTROY { + WINDOWS.with(|windows| windows.borrow_mut().remove(&(hwnd.0 as isize))); + let _ = RemoveWindowSubclass(hwnd, Some(subclass_proc), SUBCLASS_ID); + } + let result = DefSubclassProc(hwnd, message, wparam, lparam); + if message == WM_SIZE { + update(hwnd, false); + } + result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn native_close_and_reopen_restores_placement_before_showing() { + struct Fixture { + directory: std::path::PathBuf, + hwnd: HWND, + } + impl Fixture { + fn window(&mut self) -> HWND { + self.hwnd = unsafe { + CreateWindowExW( + WINDOW_EX_STYLE::default(), + windows::core::w!("STATIC"), + windows::core::w!("Perry frame persistence test"), + WS_OVERLAPPEDWINDOW, + 200, + 200, + 800, + 600, + None, + None, + None, + None, + ) + .unwrap() + }; + self.hwnd + } + fn close(&mut self) { + unsafe { + SendMessageW(self.hwnd, WM_CLOSE, None, None); + } + self.hwnd = HWND::default(); + } + } + impl Drop for Fixture { + fn drop(&mut self) { + if !self.hwnd.0.is_null() { + unsafe { + let _ = DestroyWindow(self.hwnd); + } + } + let key = perry_ui::frame::autosave_key("main").unwrap(); + let _ = std::fs::remove_file(self.directory.join(key)); + let _ = std::fs::remove_dir(&self.directory); + } + } + let directory = std::env::temp_dir().join(format!( + "perry-frame-test-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + let mut fixture = Fixture { + directory, + hwnd: HWND::default(), + }; + let store = FrameStore::in_directory(&fixture.directory, "main").unwrap(); + let hwnd = fixture.window(); + assert_eq!( + install(hwnd, store.clone(), WindowState::Normal), + WindowState::Normal + ); + assert_eq!( + store.load(), + None, + "opting in must not overwrite preferences during setup" + ); + unsafe { + SetWindowPos(hwnd, None, 120, 90, 640, 480, SWP_NOZORDER | SWP_NOACTIVATE).unwrap(); + } + let expected = + frame_from_placement(&placement(hwnd).unwrap(), WindowState::Normal).unwrap(); + fixture.close(); + assert_eq!(store.load(), Some(expected)); + + let hwnd = fixture.window(); + assert_eq!( + install(hwnd, store.clone(), WindowState::Maximized), + WindowState::Normal, + "saved normal state takes precedence over a maximized launch default" + ); + assert!( + !unsafe { IsWindowVisible(hwnd) }.as_bool(), + "restoring must not show the window" + ); + assert_eq!( + frame_from_placement(&placement(hwnd).unwrap(), WindowState::Normal), + Some(expected) + ); + fixture.close(); + + let fullscreen = WindowFrame { + state: WindowState::Fullscreen, + ..expected + }; + store.save(fullscreen).unwrap(); + let hwnd = fixture.window(); + assert_eq!( + install(hwnd, store.clone(), WindowState::Normal), + WindowState::Fullscreen + ); + // Mimic the fullscreen resize. It must not replace the normal frame. + unsafe { + SetWindowPos(hwnd, None, 0, 0, 1920, 1080, SWP_NOZORDER | SWP_NOACTIVATE).unwrap(); + } + fixture.close(); + assert_eq!(store.load(), Some(fullscreen)); + } + + #[test] + fn minimized_windows_keep_their_normal_frame_and_previous_state() { + let mut value = WINDOWPLACEMENT { + rcNormalPosition: RECT { + left: -900, + top: 40, + right: -100, + bottom: 640, + }, + showCmd: SW_SHOWMAXIMIZED.0 as u32, + ..Default::default() + }; + let maximized = frame_from_placement(&value, WindowState::Normal).unwrap(); + assert_eq!(maximized.state, WindowState::Maximized); + assert_eq!( + (maximized.x, maximized.width, maximized.height), + (-900, 800, 600) + ); + value.showCmd = SW_SHOWMINIMIZED.0 as u32; + assert_eq!( + frame_from_placement(&value, maximized.state), + Some(maximized) + ); + value.showCmd = SW_SHOWNORMAL.0 as u32; + assert_eq!( + frame_from_placement(&value, maximized.state).unwrap().state, + WindowState::Normal + ); + } +} diff --git a/crates/perry-ui-windows/src/lib.rs b/crates/perry-ui-windows/src/lib.rs index 0d2fcf825d..2a5f0e1c50 100644 --- a/crates/perry-ui-windows/src/lib.rs +++ b/crates/perry-ui-windows/src/lib.rs @@ -9,6 +9,7 @@ pub mod dpi_compat; pub mod drag_drop; #[cfg(target_os = "windows")] pub mod dwm; +pub mod frame_persistence; // `pub` so the opt-in WinUI backend (`perry-ui-windows-winui`) can chain this // crate's scanner. On the Fluent path that crate shadows `app_create`, so // `app::app_create` below never runs and would otherwise never register it, diff --git a/crates/perry-ui/Cargo.toml b/crates/perry-ui/Cargo.toml index c84fb014f7..76dbe2d853 100644 --- a/crates/perry-ui/Cargo.toml +++ b/crates/perry-ui/Cargo.toml @@ -13,3 +13,7 @@ crate-type = ["rlib"] [dependencies] perry-ffi.workspace = true perry-ui-model.workspace = true + +[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))'.dependencies] +dirs.workspace = true +tempfile.workspace = true diff --git a/crates/perry-ui/src/frame.rs b/crates/perry-ui/src/frame.rs new file mode 100644 index 0000000000..d99ea52875 --- /dev/null +++ b/crates/perry-ui/src/frame.rs @@ -0,0 +1,214 @@ +//! Opt-in desktop window persistence, scoped to the executable and window name. + +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum WindowState { + #[default] + Normal, + Maximized, + Fullscreen, +} + +/// The normal (unmaximized, non-fullscreen) frame, in backend coordinates. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct WindowFrame { + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, + pub state: WindowState, +} + +impl WindowFrame { + fn valid(self) -> bool { + self.width > 0 + && self.height > 0 + && self.x.checked_add(self.width).is_some() + && self.y.checked_add(self.height).is_some() + } + + fn decode(value: &str) -> Option { + let fields: Vec<_> = value.split_whitespace().collect(); + if fields.len() != 7 || fields[0] != "1" { + return None; + } + let frame = Self { + x: fields[1].parse().ok()?, + y: fields[2].parse().ok()?, + width: fields[3].parse().ok()?, + height: fields[4].parse().ok()?, + state: match fields[5] { + "normal" => WindowState::Normal, + "maximized" => WindowState::Maximized, + "fullscreen" => WindowState::Fullscreen, + _ => return None, + }, + }; + // A terminator detects truncated writes, including a partial state name. + (fields[6] == "end" && frame.valid()).then_some(frame) + } +} + +// FNV-1a is explicitly fixed so persistence keys survive Rust upgrades. +// The digest is only a filename component, not a security boundary. +fn digest(bytes: &[u8]) -> u64 { + bytes.iter().fold(0xcbf29ce484222325, |hash, byte| { + (hash ^ u64::from(*byte)).wrapping_mul(0x100000001b3) + }) +} + +fn key_for(executable: &Path, name: &str) -> Option { + if name.is_empty() { + return None; + } + Some(format!( + "perry-frame-{:016x}-{:016x}", + digest(executable.as_os_str().as_encoded_bytes()), + digest(name.as_bytes()) + )) +} + +/// Also used for AppKit's native frame autosave name. Do not key by title: +/// titles can change, and unrelated command-line apps share NSUserDefaults. +pub fn autosave_key(name: &str) -> Option { + key_for(&std::env::current_exe().ok()?, name) +} + +#[derive(Clone, Debug)] +pub struct FrameStore { + path: PathBuf, +} + +impl FrameStore { + pub fn new(name: &str) -> Option { + Self::in_directory( + &dirs::data_local_dir()?.join("perry").join("window-frames"), + name, + ) + } + + /// Use an explicit preferences directory (also useful for isolated tests). + pub fn in_directory(directory: &Path, name: &str) -> Option { + Some(Self { + path: directory.join(autosave_key(name)?), + }) + } + + /// Missing, invalid, or unreadable preferences leave launch defaults intact. + pub fn load(&self) -> Option { + let mut value = String::new(); + std::fs::File::open(&self.path) + .ok()? + .take(257) + .read_to_string(&mut value) + .ok()?; + if value.len() > 256 { + return None; + } + WindowFrame::decode(&value) + } + + /// Replace atomically, including on Windows. A failed write must not + /// truncate the previous frame or prevent the application from closing. + pub fn save(&self, frame: WindowFrame) -> std::io::Result<()> { + if !frame.valid() { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "invalid window frame", + )); + } + let parent = self.path.parent().expect("frame store has a parent"); + std::fs::create_dir_all(parent)?; + let mut file = tempfile::NamedTempFile::new_in(parent)?; + let state = match frame.state { + WindowState::Normal => "normal", + WindowState::Maximized => "maximized", + WindowState::Fullscreen => "fullscreen", + }; + writeln!( + file, + "1 {} {} {} {} {state} end", + frame.x, frame.y, frame.width, frame.height + )?; + file.persist(&self.path).map_err(|error| error.error)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn names_are_stable_isolated_and_cannot_escape_the_store() { + let exe = Path::new("/apps/editor"); + assert_eq!(digest(b"hello"), 0xa430d84680aabd0b); + assert_eq!(key_for(exe, ""), None); + assert_eq!(key_for(exe, "main"), key_for(exe, "main")); + assert_ne!(key_for(exe, "main"), key_for(exe, "settings")); + assert_ne!( + key_for(exe, "main"), + key_for(Path::new("/apps/other"), "main") + ); + let key = key_for(exe, "../../settings/窗口").unwrap(); + assert_eq!(Path::new(&key).components().count(), 1); + } + + #[test] + fn saves_replace_previous_frames_and_survive_reopening() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("app").join("main"); + let store = FrameStore { path: path.clone() }; + assert_eq!(store.load(), None); + for state in [ + WindowState::Normal, + WindowState::Maximized, + WindowState::Fullscreen, + ] { + let frame = WindowFrame { + x: -1200, + y: 40, + width: 900, + height: 650, + state, + }; + store.save(frame).unwrap(); + assert_eq!(FrameStore { path: path.clone() }.load(), Some(frame)); + } + } + + #[test] + fn invalid_preferences_fall_back_without_destroying_a_saved_frame() { + for value in [ + "", + "2 0 0 800 600 normal end", + "1 0 0 800 600 normal", + "1 0 0 0 600 normal end", + "1 0 0 800 -600 normal end", + "1 0 0 800 600 minimized end", + "1 2147483647 0 800 600 normal end", + "1 0 0 NaN 600 normal end", + "1 0 0 800 600 normal end trailing", + ] { + assert_eq!(WindowFrame::decode(value), None, "{value}"); + } + let dir = tempfile::tempdir().unwrap(); + let store = FrameStore { + path: dir.path().join("main"), + }; + let frame = WindowFrame { + x: 10, + y: 20, + width: 800, + height: 600, + state: WindowState::Normal, + }; + store.save(frame).unwrap(); + assert!(store.save(WindowFrame { width: 0, ..frame }).is_err()); + assert_eq!(store.load(), Some(frame)); + std::fs::write(&store.path, "invalid").unwrap(); + assert_eq!(store.load(), None); + } +} diff --git a/crates/perry-ui/src/lib.rs b/crates/perry-ui/src/lib.rs index 81fbc758f4..bb548e597d 100644 --- a/crates/perry-ui/src/lib.rs +++ b/crates/perry-ui/src/lib.rs @@ -1,3 +1,5 @@ +#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))] +pub mod frame; pub mod key_dispatch; pub mod keys; pub mod state; diff --git a/crates/perry-wasm-host/src/externals.rs b/crates/perry-wasm-host/src/externals.rs index 97296b6485..73bc2cee1c 100644 --- a/crates/perry-wasm-host/src/externals.rs +++ b/crates/perry-wasm-host/src/externals.rs @@ -46,6 +46,7 @@ pub extern "C" fn perry_wasm_host_global_new(kind: u8, mutable: i32, bits: u64) ); extern_handle(global.into()) }) + .unwrap_or(std::ptr::null_mut()) } #[no_mangle] @@ -70,6 +71,7 @@ pub extern "C" fn perry_wasm_host_global_get( } 1 }) + .unwrap_or(0) } #[no_mangle] @@ -78,7 +80,7 @@ pub extern "C" fn perry_wasm_host_global_set(handle: *mut c_void, kind: u8, bits return 0; }; let value = val_from_kind_bits(kind, bits); - with_host_runtime(|runtime| global.set(&mut runtime.store, value).is_ok() as i32) + with_host_runtime(|runtime| global.set(&mut runtime.store, value).is_ok() as i32).unwrap_or(0) } #[no_mangle] @@ -90,6 +92,7 @@ pub extern "C" fn perry_wasm_host_memory_new(initial: u32, maximum: u32) -> *mut .map(|memory| extern_handle(memory.into())) .unwrap_or(std::ptr::null_mut()) }) + .unwrap_or(std::ptr::null_mut()) } #[no_mangle] @@ -106,6 +109,7 @@ pub extern "C" fn perry_wasm_host_memory_span(handle: *mut c_void, out_len: *mut } memory.data_ptr(&runtime.store) }) + .unwrap_or(std::ptr::null_mut()) } #[no_mangle] @@ -119,6 +123,7 @@ pub extern "C" fn perry_wasm_host_memory_grow(handle: *mut c_void, delta: u32) - .map(|pages| pages as i64) .unwrap_or(-1) }) + .unwrap_or(-1) } #[no_mangle] @@ -139,6 +144,7 @@ pub extern "C" fn perry_wasm_host_table_new( .map(|table| extern_handle(table.into())) .unwrap_or(std::ptr::null_mut()) }) + .unwrap_or(std::ptr::null_mut()) } fn table_from_handle(handle: *mut c_void) -> Option { @@ -148,19 +154,19 @@ fn table_from_handle(handle: *mut c_void) -> Option
{ } } -fn table_value_for_store( - store: &mut Store<()>, +pub(crate) fn table_value_for_store( + mut store: impl AsContextMut, table: Table, bits: u64, is_null: i32, external: *mut c_void, ) -> Option { - let element = table.ty(&*store).element(); + let element = table.ty(&store).element(); if is_null != 0 { return Some(Val::default(element)); } match element { - ValType::ExternRef => Some(Val::from(ExternRef::new(store, bits))), + ValType::ExternRef => Some(Val::from(ExternRef::new(&mut store, bits))), ValType::FuncRef => match extern_from_handle(external) { Some(Extern::Func(function)) => Some(Val::FuncRef(Ref::Val(function))), _ => None, @@ -175,6 +181,7 @@ pub extern "C" fn perry_wasm_host_table_len(handle: *mut c_void) -> usize { return usize::MAX; }; with_host_runtime(|runtime| usize::try_from(table.size(&runtime.store)).unwrap_or(usize::MAX)) + .unwrap_or(usize::MAX) } #[no_mangle] @@ -215,6 +222,7 @@ pub extern "C" fn perry_wasm_host_table_get( } 1 }) + .unwrap_or(0) } #[no_mangle] @@ -235,6 +243,7 @@ pub extern "C" fn perry_wasm_host_table_set( }; table.set(&mut runtime.store, index as u64, value).is_ok() as i32 }) + .unwrap_or(0) } #[no_mangle] @@ -266,6 +275,7 @@ pub extern "C" fn perry_wasm_host_table_grow( unsafe { *out_old_len = old_len }; 1 }) + .unwrap_or(0) } #[no_mangle] @@ -273,7 +283,7 @@ pub extern "C" fn perry_wasm_host_func_arity(handle: *mut c_void) -> usize { let Some(Extern::Func(function)) = extern_from_handle(handle) else { return usize::MAX; }; - with_host_runtime(|runtime| function.ty(&runtime.store).params().len()) + with_host_runtime(|runtime| function.ty(&runtime.store).params().len()).unwrap_or(usize::MAX) } /// Invoke a function obtained from a funcref table. This is the generic @@ -361,6 +371,11 @@ pub extern "C" fn perry_wasm_host_func_call( }) }) .collect() + }) + .unwrap_or_else(|| { + Err(WasmHostError::Runtime( + "host runtime is already in use".into(), + )) }); let values = match call_result { Ok(values) => values, diff --git a/crates/perry-wasm-host/src/host_runtime.rs b/crates/perry-wasm-host/src/host_runtime.rs new file mode 100644 index 0000000000..80653e09c2 --- /dev/null +++ b/crates/perry-wasm-host/src/host_runtime.rs @@ -0,0 +1,139 @@ +//! Thread-local shared wasmi engine/store access. +//! +//! All WebAssembly objects in one JavaScript agent share an engine and store. +//! A JavaScript import can legitimately re-enter WebAssembly while an outer +//! call holds the store: Emscripten's `invoke_*`/`dynCall` trampolines call a +//! table function, and imports call exports such as `_malloc`. wasmi hands the +//! import its `Caller`, which is the only live mutable view of the store for +//! that call's extent, so nested host access is routed through the innermost +//! active `Caller` instead of borrowing the thread-local store a second time. + +use std::cell::{Cell, RefCell, UnsafeCell}; +use wasmi::{AsContextMut, Caller, Engine, Store, StoreContextMut}; + +/// The store context for one host operation: the thread-local store at the +/// outermost level, or the active import callback's `Caller` when nested. +pub(super) struct HostRuntime<'a> { + pub(super) engine: &'a Engine, + pub(super) store: StoreContextMut<'a, ()>, +} + +struct SharedRuntime { + engine: Engine, + store: Store<()>, +} + +/// One import callback's `Caller`, lifetime-erased. It is valid exactly while +/// its `ImportCallbackScope` is alive; `busy` marks a host operation currently +/// holding a mutable view through it. +struct CallerLevel { + caller: *mut (), + busy: bool, +} + +thread_local! { + static HOST_RUNTIME: UnsafeCell = UnsafeCell::new({ + let engine = Engine::default(); + let store = Store::new(&engine, ()); + SharedRuntime { engine, store } + }); + static ROOT_BUSY: Cell = const { Cell::new(false) }; + static CALLER_LEVELS: RefCell> = const { RefCell::new(Vec::new()) }; +} + +struct RootBusyGuard; + +impl Drop for RootBusyGuard { + fn drop(&mut self) { + ROOT_BUSY.with(|busy| busy.set(false)); + } +} + +struct CallerBusyGuard(usize); + +impl Drop for CallerBusyGuard { + fn drop(&mut self) { + CALLER_LEVELS.with(|levels| { + if let Some(level) = levels.borrow_mut().get_mut(self.0) { + level.busy = false; + } + }); + } +} + +/// Run `f` with a mutable view of this agent's store. Returns `None` only for +/// a genuine aliasing attempt: re-entry that is not inside an import callback +/// of the operation already holding the store. +pub(super) fn with_host_runtime(f: impl FnOnce(&mut HostRuntime<'_>) -> R) -> Option { + HOST_RUNTIME.with(|shared| { + let shared = shared.get(); + // Field projections only: the engine is shared, and the store is + // reached either here (outermost) or through a Caller (nested). + let engine = unsafe { &*std::ptr::addr_of!((*shared).engine) }; + let nested = CALLER_LEVELS.with(|levels| { + let mut levels = levels.borrow_mut(); + let index = levels.len().checked_sub(1)?; + let level = &mut levels[index]; + Some((!level.busy).then(|| { + level.busy = true; + (index, level.caller) + })) + }); + match nested { + Some(None) => None, + Some(Some((index, caller))) => { + let _busy = CallerBusyGuard(index); + let caller = unsafe { &mut *(caller as *mut Caller<'_, ()>) }; + let mut runtime = HostRuntime { + engine, + store: caller.as_context_mut(), + }; + Some(f(&mut runtime)) + } + None => { + if ROOT_BUSY.with(|busy| busy.replace(true)) { + return None; + } + let _busy = RootBusyGuard; + let store = unsafe { &mut *std::ptr::addr_of_mut!((*shared).store) }; + let mut runtime = HostRuntime { + engine, + store: store.as_context_mut(), + }; + Some(f(&mut runtime)) + } + } + }) +} + +/// The agent's store address, recorded by instances for their direct +/// accessors. Obtained without creating a reference to the store. +pub(super) fn host_store_ptr() -> *mut Store<()> { + HOST_RUNTIME.with(|shared| unsafe { std::ptr::addr_of_mut!((*shared.get()).store) }) +} + +/// Publishes an import callback's `Caller` as the innermost store context for +/// the duration of the JavaScript callback. The trampoline must not use +/// `caller` itself while the scope is alive. +pub(super) struct ImportCallbackScope; + +impl ImportCallbackScope { + pub(super) fn enter(caller: &mut Caller<'_, ()>) -> Self { + let caller = caller as *mut Caller<'_, ()> as *mut (); + CALLER_LEVELS.with(|levels| { + levels.borrow_mut().push(CallerLevel { + caller, + busy: false, + }) + }); + Self + } +} + +impl Drop for ImportCallbackScope { + fn drop(&mut self) { + CALLER_LEVELS.with(|levels| { + levels.borrow_mut().pop(); + }); + } +} diff --git a/crates/perry-wasm-host/src/lib.rs b/crates/perry-wasm-host/src/lib.rs index 642be0a1b2..af5b329ede 100644 --- a/crates/perry-wasm-host/src/lib.rs +++ b/crates/perry-wasm-host/src/lib.rs @@ -11,7 +11,7 @@ //! That keeps the wasmi version surface small and lets us swap engines //! (wasmtime, etc.) behind the same shape later. -use std::cell::{RefCell, UnsafeCell}; +use std::cell::RefCell; use std::collections::HashMap; use std::sync::{ atomic::{AtomicI32, AtomicU64, Ordering}, @@ -19,10 +19,13 @@ use std::sync::{ }; use wasmi::{ - Engine, Extern, ExternRef, ExternType, Func, Global, Linker, Memory, MemoryType, Module, - Mutability, Ref, Store, Table, TableType, Val, ValType, + AsContext, AsContextMut, Engine, Extern, ExternRef, ExternType, Func, Global, Linker, Memory, + MemoryType, Module, Mutability, Ref, Store, Table, TableType, Val, ValType, }; +mod host_runtime; +use host_runtime::with_host_runtime; + /// Numeric WebAssembly value used by the public Rust call API. JavaScript /// import callbacks additionally marshal `externref`, while `funcref` values /// cross the C boundary as opaque external handles. @@ -86,31 +89,6 @@ fn trace_module(module: &ModuleInner, event: &str) { ); } -/// All WebAssembly objects in one JavaScript agent share an engine and store. -/// -/// Emscripten side modules import the main module's memory, table, functions, -/// and mutable globals. wasmi external handles can only be linked into the -/// store that owns them, so the former one-store-per-instance layout could -/// never represent that graph. JavaScript execution in Perry is thread-local; -/// mirroring that here gives each Worker an independent WebAssembly agent -/// while allowing every instance on a worker to exchange externals. -struct HostRuntime { - engine: Engine, - store: Store<()>, -} - -thread_local! { - static HOST_RUNTIME: UnsafeCell = UnsafeCell::new({ - let engine = Engine::default(); - let store = Store::new(&engine, ()); - HostRuntime { engine, store } - }); -} - -fn with_host_runtime(f: impl FnOnce(&mut HostRuntime) -> R) -> R { - HOST_RUNTIME.with(|runtime| unsafe { f(&mut *runtime.get()) }) -} - /// Opaque instance backed by its JavaScript agent's shared store. Wasm /// instances still own their defined state, while imported externals retain /// identity across an Emscripten main/side-module graph. @@ -200,20 +178,17 @@ thread_local! { static ACTIVE_INSTANCE_TABLES: RefCell> = const { RefCell::new(Vec::new()) }; } -fn begin_instance_call(inst: &mut WasmInstanceHandle) { +fn begin_instance_call(inst: &mut WasmInstanceHandle, store: &impl AsContext) { let instance_id = inst as *mut WasmInstanceHandle as usize; let mut lengths = HashMap::new(); for export in inst.inner._module.0.module.exports() { - let ExternType::Table(table_type) = export.ty() else { + let ExternType::Table(_) = export.ty() else { continue; }; - if table_type.element() != ValType::ExternRef { - continue; - } - let Some(table) = inst.inner.instance.get_table(inst.store(), export.name()) else { + let Some(table) = inst.inner.instance.get_table(store, export.name()) else { continue; }; - if let Ok(len) = usize::try_from(table.size(inst.store())) { + if let Ok(len) = usize::try_from(table.size(store)) { lengths.insert(export.name().to_string(), len); } } @@ -353,7 +328,7 @@ impl std::error::Error for WasmHostError {} /// Cheap byte-level magic check (`\0asm\01\0\0\0`). Mirrors `WebAssembly.validate` /// — for the MVP we delegate to wasmi's full module decode. pub fn validate(bytes: &[u8]) -> bool { - with_host_runtime(|runtime| Module::new(&runtime.engine, bytes).is_ok()) + with_host_runtime(|runtime| Module::new(runtime.engine, bytes).is_ok()).unwrap_or(false) } /// Compile bytes to a module. No imports resolved at this stage. @@ -371,6 +346,11 @@ pub fn compile(bytes: &[u8]) -> Result { trace_module(&inner, "compiled"); Ok(WasmModuleHandle(inner)) }) + .unwrap_or_else(|| { + Err(WasmHostError::Compile( + "host runtime is already in use".into(), + )) + }) } /// Instantiate with the module's imported numeric functions routed through an @@ -397,7 +377,6 @@ fn instantiate_with_import_callbacks( import_context_value: u64, ) -> Result { trace_module(&module.0, "instantiating"); - let store = with_host_runtime(|runtime| &mut runtime.store as *mut Store<()>); let import_context = Arc::new(AtomicU64::new(import_context_value)); // i32::MIN is not a valid WASI process status and acts as "not exited". let exit_code = Arc::new(AtomicI32::new(i32::MIN)); @@ -499,6 +478,9 @@ fn instantiate_with_import_callbacks( } } if numeric { + // JavaScript may re-enter WebAssembly from here; + // nested host access borrows through `caller`. + let scope = host_runtime::ImportCallbackScope::enter(&mut caller); let called = unsafe { callback( callback_context.load(Ordering::Relaxed), @@ -514,6 +496,7 @@ fn instantiate_with_import_callbacks( result_bits.len(), ) }; + drop(scope); if called != 0 { for ((result, kind), bits) in results .iter_mut() @@ -542,10 +525,15 @@ fn instantiate_with_import_callbacks( ) .map_err(|e| WasmHostError::Link(e.to_string()))?; } - let instance = linker - .instantiate_and_start(unsafe { &mut *store }, &module.0.module) - .map_err(|e| WasmHostError::Link(e.to_string()))?; - let memory = instance.get_memory(unsafe { &*store }, "memory"); + let (store, instance, memory) = with_host_runtime(|runtime| { + let store = &mut runtime.store; + let instance = linker + .instantiate_and_start(&mut *store, &module.0.module) + .map_err(|e| WasmHostError::Link(e.to_string()))?; + let memory = instance.get_memory(&*store, "memory"); + Ok((host_runtime::host_store_ptr(), instance, memory)) + }) + .unwrap_or_else(|| Err(WasmHostError::Link("host runtime is already in use".into())))?; trace_module(&module.0, "instantiated"); Ok(WasmInstanceHandle { inner: Box::new(InstanceInner { @@ -586,22 +574,26 @@ fn coerce_numeric_value(value: WasmVal, expected: ValType) -> Option { /// once per instance. `None` when the instance has no function export by that /// name. fn resolve_export(inst: &mut WasmInstanceHandle, name: &str) -> Option { - if let Some(&index) = inst.inner.export_handles.get(name) { - return Some(index); - } - let func = inst.inner.instance.get_func(inst.store(), name)?; - let ty = func.ty(inst.store()); - let index = inst.inner.exports.len(); - inst.inner.exports.push(CachedExport { - name: name.to_string(), - func, - params: ty.params().to_vec().into_boxed_slice(), - results: ty.results().to_vec().into_boxed_slice(), - args: Vec::new(), - outs: Vec::new(), - }); - inst.inner.export_handles.insert(name.to_string(), index); - Some(index) + with_host_runtime(|runtime| { + debug_assert_eq!(inst.inner.store, host_runtime::host_store_ptr()); + if let Some(&index) = inst.inner.export_handles.get(name) { + return Some(index); + } + let func = inst.inner.instance.get_func(&runtime.store, name)?; + let ty = func.ty(&runtime.store); + let index = inst.inner.exports.len(); + inst.inner.exports.push(CachedExport { + name: name.to_string(), + func, + params: ty.params().to_vec().into_boxed_slice(), + results: ty.results().to_vec().into_boxed_slice(), + args: Vec::new(), + outs: Vec::new(), + }); + inst.inner.export_handles.insert(name.to_string(), index); + Some(index) + }) + .flatten() } /// Call a previously resolved export. Results are left in the cached entry's @@ -641,20 +633,28 @@ fn call_resolved_export( .extend(entry.results.iter().copied().map(Val::default)); } - begin_instance_call(inst); - let call_result = { + let call_result = with_host_runtime(|runtime| { + begin_instance_call(inst, &runtime.store); // Split the borrow: the call needs the store mutably while reading the // cached argument buffer and filling the cached result buffer, and all // three are disjoint fields of the same `InstanceInner`. - let store = inst.inner.store; + debug_assert_eq!(inst.inner.store, host_runtime::host_store_ptr()); let inner = &mut *inst.inner; let CachedExport { func, args, outs, .. } = &mut inner.exports[index]; - func.call(unsafe { &mut *store }, args, outs) - }; + func.call(&mut runtime.store, args, outs) + }) + .map_or_else( + || { + Err(WasmHostError::Runtime( + "host runtime is already in use".into(), + )) + }, + |result| result.map_err(|e| WasmHostError::Runtime(e.to_string())), + ); let table_result = finish_instance_call(inst); - call_result.map_err(|e| WasmHostError::Runtime(e.to_string()))?; + call_result?; table_result?; Ok(()) } @@ -1032,6 +1032,14 @@ pub(crate) fn extern_from_handle(handle: *mut c_void) -> Option { (!handle.is_null()).then(|| unsafe { (*(handle as *const WasmExternHandle)).item }) } +/// Release an opaque external handle after its JavaScript wrapper dies. +#[no_mangle] +pub extern "C" fn perry_wasm_host_extern_drop(handle: *mut c_void) { + if !handle.is_null() { + unsafe { drop(Box::from_raw(handle as *mut WasmExternHandle)) }; + } +} + mod externals; /// Return the byte length of the exported `memory`, or zero when absent. @@ -1342,7 +1350,7 @@ mod tests { ]; /// `(module (import "env" "f" (func $f (result f64))) /// (func (export "call") (result f64) call $f))`. - const IMPORT_F64_RESULT_WASM: &[u8] = &[ + pub(super) const IMPORT_F64_RESULT_WASM: &[u8] = &[ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7c, 0x02, 0x09, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x01, 0x66, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x63, 0x61, 0x6c, 0x6c, 0x00, 0x01, 0x0a, 0x06, 0x01, 0x04, 0x00, @@ -1973,5 +1981,7 @@ mod tests { } } +#[cfg(test)] +mod reentrancy_tests; #[cfg(test)] mod shared_import_tests; diff --git a/crates/perry-wasm-host/src/reentrancy_tests.rs b/crates/perry-wasm-host/src/reentrancy_tests.rs new file mode 100644 index 0000000000..2fc8d89186 --- /dev/null +++ b/crates/perry-wasm-host/src/reentrancy_tests.rs @@ -0,0 +1,159 @@ +//! A JavaScript import may re-enter WebAssembly while the outer call holds the +//! shared store (Emscripten `invoke_*`/`dynCall`, imports calling `_malloc`). +//! Nested host access must route through the active import's `Caller`. +use super::*; +use std::cell::Cell; + +/// `(module (import "env" "cb" (func $cb (result f64))) +/// (table $t (export "t") 1 funcref) (elem (i32.const 0) $inner) +/// (func (export "outer") (result f64) call $cb) +/// (func $inner (export "inner") (result f64) f64.const 7))`. +const REENTRANT_WASM: &[u8] = &[ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7c, 0x02, + 0x0a, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x02, 0x63, 0x62, 0x00, 0x00, 0x03, 0x03, 0x02, 0x00, 0x00, + 0x04, 0x04, 0x01, 0x70, 0x00, 0x01, 0x07, 0x15, 0x03, 0x01, 0x74, 0x01, 0x00, 0x05, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x00, 0x01, 0x05, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x02, 0x09, 0x07, 0x01, + 0x00, 0x41, 0x00, 0x0b, 0x01, 0x02, 0x0a, 0x12, 0x02, 0x04, 0x00, 0x10, 0x00, 0x0b, 0x0b, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x40, 0x0b, +]; + +#[derive(Clone, Copy, PartialEq, Debug)] +enum Nested { + Export, + TableFunction, + StandaloneGlobal, +} + +thread_local! { + static INSTANCE: Cell<*mut WasmInstanceHandle> = const { Cell::new(std::ptr::null_mut()) }; + static MODE: Cell = const { Cell::new(Nested::Export) }; + static NESTED: Cell> = const { Cell::new(None) }; +} + +unsafe fn nested_table_function_call(inst: *mut WasmInstanceHandle) -> Option { + let table = externals::perry_wasm_host_instance_export_extern(inst, c"t".as_ptr(), 1); + let mut bits = 0u64; + let mut is_null = 0i32; + let mut function = std::ptr::null_mut(); + let got = + externals::perry_wasm_host_table_get(table, 0, &mut bits, &mut is_null, &mut function); + perry_wasm_host_extern_drop(table); + if got == 0 || is_null != 0 || function.is_null() { + return None; + } + let mut out_kind = 0u8; + let mut out_bits = 0u64; + let mut out_count = 0usize; + let mut err = std::ptr::null_mut(); + // Non-null empty argument buffers, as the runtime's caller passes them. + let no_kinds = [0u8; 1]; + let no_bits = [0u64; 1]; + let called = externals::perry_wasm_host_func_call( + function, + no_kinds.as_ptr(), + no_bits.as_ptr(), + 0, + &mut out_kind, + &mut out_bits, + 1, + &mut out_count, + &mut err, + ); + perry_wasm_host_extern_drop(function); + (called != 0 && out_count == 1 && out_kind == WASM_VAL_KIND_F64) + .then(|| f64::from_bits(out_bits)) +} + +unsafe extern "C" fn reenters_wasm( + _context: u64, + _module: *const u8, + _module_len: usize, + _name: *const u8, + _name_len: usize, + _arg_kinds: *const u8, + _arg_bits: *const u64, + _arg_count: usize, + _result_kinds: *const u8, + result_bits: *mut u64, + result_count: usize, +) -> i32 { + assert_eq!(result_count, 1); + let inst = INSTANCE.with(Cell::get); + let nested = match MODE.with(Cell::get) { + Nested::Export => match call_export(&mut *inst, "inner", &[]) { + Ok(values) => match values.as_slice() { + [WasmVal::F64(value)] => Some(*value), + _ => None, + }, + Err(_) => None, + }, + Nested::TableFunction => nested_table_function_call(inst), + Nested::StandaloneGlobal => { + let global = + externals::perry_wasm_host_global_new(WASM_VAL_KIND_F64, 0, 5.0f64.to_bits()); + let mut kind = 0u8; + let mut bits = 0u64; + let read = !global.is_null() + && externals::perry_wasm_host_global_get(global, &mut kind, &mut bits) != 0; + perry_wasm_host_extern_drop(global); + read.then(|| f64::from_bits(bits)) + } + }; + NESTED.with(|slot| slot.set(nested)); + *result_bits = (nested.unwrap_or(-100.0) + 1.0).to_bits(); + 1 +} + +fn outer_call_with(mode: Nested) -> (Option, Vec) { + let module = compile(REENTRANT_WASM).expect("compile re-entrant module"); + let mut instance = instantiate_with_import_callback(&module, Some(reenters_wasm), 0) + .expect("instantiate re-entrant module"); + // Resolve the nested export first: the export cache must not grow while + // the outer call borrows its entry. + assert_eq!( + call_export(&mut instance, "inner", &[]).expect("warm inner"), + [WasmVal::F64(7.0)] + ); + INSTANCE.with(|slot| slot.set(&mut instance as *mut WasmInstanceHandle)); + MODE.with(|slot| slot.set(mode)); + NESTED.with(|slot| slot.set(None)); + let outer = call_export(&mut instance, "outer", &[]).expect("outer call"); + INSTANCE.with(|slot| slot.set(std::ptr::null_mut())); + (NESTED.with(Cell::get), outer) +} + +#[test] +fn import_callback_can_call_another_export() { + let (nested, outer) = outer_call_with(Nested::Export); + assert_eq!(nested, Some(7.0)); + assert_eq!(outer, [WasmVal::F64(8.0)]); +} + +#[test] +fn import_callback_can_call_a_funcref_table_function() { + let (nested, outer) = outer_call_with(Nested::TableFunction); + assert_eq!(nested, Some(7.0)); + assert_eq!(outer, [WasmVal::F64(8.0)]); +} + +#[test] +fn import_callback_can_use_standalone_externals() { + let (nested, outer) = outer_call_with(Nested::StandaloneGlobal); + assert_eq!(nested, Some(5.0)); + assert_eq!(outer, [WasmVal::F64(6.0)]); + // The outermost store is free again once the call returns. + let global = externals::perry_wasm_host_global_new(WASM_VAL_KIND_I32, 1, 0); + assert!( + !global.is_null(), + "the store must be released after the call" + ); + perry_wasm_host_extern_drop(global); +} + +#[test] +fn overlapping_access_without_an_import_boundary_is_refused() { + let nested = + host_runtime::with_host_runtime(|_| host_runtime::with_host_runtime(|_| ()).is_none()); + assert_eq!(nested, Some(true)); + assert!(host_runtime::with_host_runtime(|_| ()).is_some()); +} diff --git a/crates/perry-wasm-host/src/tables.rs b/crates/perry-wasm-host/src/tables.rs index 3e277be3c6..bdbe57f1ac 100644 --- a/crates/perry-wasm-host/src/tables.rs +++ b/crates/perry-wasm-host/src/tables.rs @@ -65,7 +65,10 @@ pub extern "C" fn perry_wasm_host_instance_table_get( *out_bits = value.bits; *out_is_null = value.is_null as i32; if !out_external.is_null() { - *out_external = value.external; + *out_external = match extern_from_handle(value.external) { + Some(Extern::Func(function)) => extern_handle(function.into()), + _ => std::ptr::null_mut(), + }; } } return 1; @@ -115,18 +118,7 @@ pub(crate) fn table_value( is_null: i32, external: *mut c_void, ) -> Option { - let element = table.ty(inst.store()).element(); - if is_null != 0 { - return Some(Val::default(element)); - } - match element { - ValType::ExternRef => Some(Val::from(ExternRef::new(inst.store_mut(), bits))), - ValType::FuncRef => match extern_from_handle(external) { - Some(Extern::Func(function)) => Some(Val::FuncRef(Ref::Val(function))), - _ => None, - }, - _ => None, - } + crate::externals::table_value_for_store(inst.store_mut(), table, bits, is_null, external) } #[no_mangle] diff --git a/crates/perry/src/commands/compile.rs b/crates/perry/src/commands/compile.rs index 91d91ae29a..2c93e9c202 100644 --- a/crates/perry/src/commands/compile.rs +++ b/crates/perry/src/commands/compile.rs @@ -52,6 +52,7 @@ mod precompile_capture; mod reachability; mod size_report; mod solid_config; +mod tiny_program; mod typed_feedback_profile; mod update_config; mod windows_target; diff --git a/crates/perry/src/commands/compile/bootstrap.rs b/crates/perry/src/commands/compile/bootstrap.rs index 7642feccc3..66530ef3de 100644 --- a/crates/perry/src/commands/compile/bootstrap.rs +++ b/crates/perry/src/commands/compile/bootstrap.rs @@ -211,6 +211,7 @@ pub(super) fn rerun_collect_with_class_field_types( ctx.cross_module_class_field_types = field_map; ctx.cross_module_class_accessors = accessor_map; ctx.native_modules.clear(); + ctx.reexport_pruner = Default::default(); visited.clear(); *next_class_id = 1; collect_modules( @@ -972,10 +973,11 @@ pub(super) fn run_post_collect_preflight( match format { OutputFormat::Text => { println!( - "Found {} module(s): {} native, {} JavaScript", + "Found {} module(s): {} native, {} JavaScript, {} pruned as unreferenced side-effect-free re-exports", total_modules, ctx.native_modules.len(), - ctx.js_modules.len() + ctx.js_modules.len(), + ctx.reexport_pruner.pruned ); } OutputFormat::Json => {} diff --git a/crates/perry/src/commands/compile/build_cache.rs b/crates/perry/src/commands/compile/build_cache.rs index a027c99175..2aa0e7c1b6 100644 --- a/crates/perry/src/commands/compile/build_cache.rs +++ b/crates/perry/src/commands/compile/build_cache.rs @@ -127,6 +127,7 @@ const BUILD_CACHE_ENV_VARS: &[&str] = &[ // cache entries. "PERRY_TARGET_CPU", "PERRY_NO_AUTO_OPTIMIZE", + "PERRY_NO_REEXPORT_PRUNE", "PERRY_DISABLE_WELL_KNOWN", "PERRY_FORCE_WELL_KNOWN", // Both switches change native-vs-JavaScript module routing and therefore @@ -589,6 +590,21 @@ impl BuildCacheProbe { if verify_files(&manifest.sources).is_err() { return miss("source"); } + // A newly added nested package.json can override a pruning contract. + // Verifying only previously existing files misses that graph change. + let config_paths = + config_inputs_for(&manifest.sources, &self.project_root, &self.cache_root) + .iter() + .map(|path| absolute_identity(path)) + .collect::>(); + let recorded_config_paths = manifest + .config_inputs + .iter() + .map(|input| input.path.clone()) + .collect::>(); + if config_paths != recorded_config_paths { + return miss("config-paths"); + } if verify_files(&manifest.config_inputs).is_err() { return miss("config"); } @@ -838,7 +854,11 @@ fn eligibility(args: &CompileArgs, project_root: &Path) -> Result<(), String> { if args.type_check { return Err("type-check".to_string()); } - if args.print_hir || args.trace.is_some() || args.focus.is_some() { + if args.print_hir + || args.trace.is_some() + || args.focus.is_some() + || std::env::var("PERRY_COLLECT_ONLY").ok().as_deref() == Some("1") + { return Err("diagnostic-mode".to_string()); } if args.typed_feedback_profile.is_some() || args.typed_feedback_sites.is_some() { diff --git a/crates/perry/src/commands/compile/cjs_wrap/detect.rs b/crates/perry/src/commands/compile/cjs_wrap/detect.rs index 36666cfcc5..76b979e912 100644 --- a/crates/perry/src/commands/compile/cjs_wrap/detect.rs +++ b/crates/perry/src/commands/compile/cjs_wrap/detect.rs @@ -62,7 +62,11 @@ pub(in crate::commands::compile) fn is_commonjs(source: &str) -> bool { // — no `exports.X =`, no `require(`. Without this arm they fall // through to the ESM pipeline, where the bare `exports` identifier // throws a ReferenceError at module init. - || stripped.contains("defineProperty(exports,") + || perry_perex::tooling::Regex::new( + r"(?:^|[^A-Za-z0-9_$.])Object\s*\.\s*defineProperty\s*\(\s*(?:module\s*\.\s*)?exports\s*,", + ) + .unwrap() + .is_match(&stripped) { return true; } diff --git a/crates/perry/src/commands/compile/cjs_wrap/extract_exports.rs b/crates/perry/src/commands/compile/cjs_wrap/extract_exports.rs index bf8faf1689..82ebad74d2 100644 --- a/crates/perry/src/commands/compile/cjs_wrap/extract_exports.rs +++ b/crates/perry/src/commands/compile/cjs_wrap/extract_exports.rs @@ -385,7 +385,8 @@ pub fn extract_object_literal_exports_from_require(source: &str) -> Vec<(String, out } -/// Extract named-export patterns from CJS source. Three shapes are matched: +/// Extract named-export patterns from CJS source. These are value bindings, +/// including properties installed by accessors or by later function calls. /// /// 1. `exports.X = ...` and `module.exports.X = ...` — the canonical CJS /// named-export form. Skips `__esModule` (the interop marker injected @@ -463,6 +464,27 @@ pub fn extract_exports_from_source(source: &str) -> Vec { } } + // #10153: Babel emits accessor re-exports with defineProperty rather + // than an assignment. A descriptor's `get` or `value` may hold a function, + // but that is not a function declaration in this module. Surface the name + // through the same value-export path as ordinary exports assignments. + let descriptor_re = perry_perex::tooling::Regex::new( + r#"(?:^|[^A-Za-z0-9_$.])(Object\s*\.\s*defineProperty\s*\(\s*(?:module\s*\.\s*)?exports\s*,\s*['"]([A-Za-z_$][A-Za-z0-9_$]*)['"]\s*,)"#, + ) + .unwrap(); + let stripped = detect::strip_comments_and_strings(source); + for cap in descriptor_re.captures_iter(source) { + let call = cap.get(1).unwrap(); + // Ignore examples embedded in comments and strings. + if stripped + .as_bytes() + .get(call.start()..) + .is_some_and(|rest| rest.starts_with(b"Object")) + { + push_unique(&mut names, cap.get(2).unwrap().as_str()); + } + } + // Shape 2: `module.exports = { ... }` — extract every key from the // object literal body. Brace-balanced scan because the body may contain // nested braces (`module.exports = { fn: function() {} }`). Two key diff --git a/crates/perry/src/commands/compile/cjs_wrap/mod.rs b/crates/perry/src/commands/compile/cjs_wrap/mod.rs index e9ef420884..815617bb51 100644 --- a/crates/perry/src/commands/compile/cjs_wrap/mod.rs +++ b/crates/perry/src/commands/compile/cjs_wrap/mod.rs @@ -21,7 +21,8 @@ //! `_req_N` bindings, runs the original code, and returns //! `module.exports`. The IIFE result is bound to `_cjs`. //! 3. Emit `export default _cjs;` plus `export const X = _cjs.X;` for each -//! detected named export. +//! detected named export. Codegen turns these synthetic property exports +//! into live getters on `_cjs`, without evaluating a snapshot at init. //! //! Two named-export sources are unioned: //! diff --git a/crates/perry/src/commands/compile/cjs_wrap/tests/source_graph.rs b/crates/perry/src/commands/compile/cjs_wrap/tests/source_graph.rs index c11538364d..22c5e81eb4 100644 --- a/crates/perry/src/commands/compile/cjs_wrap/tests/source_graph.rs +++ b/crates/perry/src/commands/compile/cjs_wrap/tests/source_graph.rs @@ -1,5 +1,51 @@ use super::{extract_exports_from_source, wrap_commonjs, PathBuf}; +#[test] +fn detects_whitespace_separated_descriptor_exports() { + let source = "Object . defineProperty (\n exports , 'answer', { value: 42 });"; + assert!(super::is_commonjs(source)); + assert_eq!(extract_exports_from_source(source), ["answer"]); +} + +#[test] +fn descriptor_and_late_exports_are_values_not_declared_functions() { + let source = r#" +Object.defineProperty(exports, "__esModule", { value: true }); +Object.defineProperty(exports, "transform", { + enumerable: true, get: function () { return impl.transform; } +}); +Object.defineProperty(module.exports, 'value', { value: function () { return 42; } }); +exports.update = function () { exports.late = function () { return 43; }; }; +// Object.defineProperty(exports, "comment", { value: 0 }); +var text = 'Object.defineProperty(exports, "text", { value: 0 });'; +other.Object.defineProperty(exports, "other", { value: 0 }); +Object.defineProperty(other.exports, "inner", { value: 0 }); +"#; + let names = extract_exports_from_source(source); + assert_eq!(names, ["update", "late", "transform", "value"]); + let wrapped = wrap_commonjs(source, &PathBuf::from("/tmp/descriptor/index.cjs")); + for name in &names { + assert!(wrapped.contains(&format!("export const {name} = _cjs.{name};"))); + assert!(!wrapped.contains(&format!("export function {name}"))); + } + let ast = perry_parser::parse_typescript(&wrapped, "index.cjs").unwrap(); + let hir = perry_hir::lower_module(&ast, "index", "/tmp/descriptor/index.cjs").unwrap(); + for name in names { + assert!(hir + .exported_objects + .iter() + .any(|exported| exported == &name)); + assert!(!hir + .functions + .iter() + .any(|function| function.is_exported && function.name == name)); + assert!(!hir + .exported_functions + .iter() + .any(|(exported, _)| exported == &name)); + } +} + #[test] fn extracts_esbuild_export_helper_keys() { let src = r#" diff --git a/crates/perry/src/commands/compile/collect_modules.rs b/crates/perry/src/commands/compile/collect_modules.rs index 026683081f..30ff194fc2 100644 --- a/crates/perry/src/commands/compile/collect_modules.rs +++ b/crates/perry/src/commands/compile/collect_modules.rs @@ -41,6 +41,7 @@ mod import_meta_resolve; mod json_module; mod native_addon; mod parse_error; +pub(crate) mod reexport_prune; mod script_string; mod static_require_transform; #[cfg(test)] @@ -541,6 +542,9 @@ fn collect_module_one( let ast_module = defined_module.as_ref().unwrap_or(ast_module); let resolved_module = import_meta_resolve::resolve_static(ast_module, &canonical, ctx)?; let ast_module = resolved_module.as_ref().unwrap_or(ast_module); + let forwarding_module = + reexport_prune::normalize_forwarding_barrel(ast_module, entry_path, ctx); + let ast_module = forwarding_module.as_ref().unwrap_or(ast_module); let file_loader_sources = file_loader_import_sources(ast_module); let source_file_path = canonical.to_string_lossy().to_string(); @@ -1797,8 +1801,10 @@ fn collect_module_one( } } + ctx.reexport_pruner.imports(&hir_module.imports); + // Process re-exports - for export in &hir_module.exports { + for (export_index, export) in hir_module.exports.iter().enumerate() { let source = match export { perry_hir::Export::ReExport { source, .. } => Some(source), perry_hir::Export::ExportAll { source } => Some(source), @@ -1925,7 +1931,18 @@ fn collect_module_one( } match kind { - ModuleKind::NativeCompiled => pending.push(source_path), + ModuleKind::NativeCompiled => { + if reexport_prune::record( + ctx, + &canonical, + export_index, + export, + &resolved_path, + &source_path, + ) { + pending.push(source_path); + } + } ModuleKind::Interpreted => { // JS runtime (V8) support was removed, so interpreted // node_modules dependencies are not followed. A direct diff --git a/crates/perry/src/commands/compile/collect_modules/reexport_prune.rs b/crates/perry/src/commands/compile/collect_modules/reexport_prune.rs new file mode 100644 index 0000000000..ec729cb5cd --- /dev/null +++ b/crates/perry/src/commands/compile/collect_modules/reexport_prune.rs @@ -0,0 +1,264 @@ +//! Collection-time, monotone export demand. Ordinary imports are retained. An +//! unused re-export is postponed only when its entire static dependency tree +//! is covered by package sideEffects contracts. Later importers can reactivate +//! it; HIR edges are removed only after every collection root has settled. + +use std::collections::{HashMap, HashSet}; +use std::path::{Path, PathBuf}; + +use perry_hir::{Export, Import, ImportSpecifier}; + +use super::super::CompilationContext; + +mod forwarding; +mod scan; +mod side_effects; +pub(super) use forwarding::normalize as normalize_forwarding_barrel; + +#[derive(Clone, Default)] +struct Demand { + all: bool, + names: HashSet, +} + +impl Demand { + fn all() -> Self { + Self { + all: true, + names: HashSet::new(), + } + } + + fn name(name: &str) -> Self { + Self { + all: false, + names: HashSet::from([name.to_owned()]), + } + } + + fn contains(&self, name: &str) -> bool { + self.all || self.names.contains(name) + } +} + +struct Edge { + from: PathBuf, + index: usize, + target: PathBuf, + source_path: PathBuf, + export: Export, + safe: bool, + active: bool, +} + +#[derive(Default)] +pub(crate) struct ReexportPruner { + demands: HashMap, + edges: Vec, + scan: scan::Scanner, + pub(crate) pruned: usize, +} + +fn enabled() -> bool { + std::env::var("PERRY_NO_REEXPORT_PRUNE").ok().as_deref() != Some("1") +} + +impl ReexportPruner { + fn demand(&mut self, path: &Path, incoming: Demand) -> bool { + let current = self.demands.entry(path.to_owned()).or_default(); + if current.all { + return false; + } + if incoming.all { + *current = incoming; + return true; + } + let before = current.names.len(); + current.names.extend(incoming.names); + current.names.len() != before + } + + pub(crate) fn root(&mut self, path: &Path) { + self.demand(path, Demand::all()); + } + + pub(super) fn implicit_root(&mut self, path: &Path) { + if !self.demands.contains_key(path) { + self.root(path); + } + } + + pub(crate) fn imports(&mut self, imports: &[Import]) { + if !enabled() { + return; + } + for import in imports { + if import.type_only || import.runtime_erased { + continue; + } + let Some(path) = &import.resolved_path else { + continue; + }; + let mut demand = Demand::default(); + // A dynamic import exposes the complete namespace, even when the + // same source also has a named static import. require interop and + // namespaces likewise cannot be narrowed to named imports. + demand.all = import.is_dynamic || import.is_dynamic_target || import.is_adopted_require; + for spec in &import.specifiers { + match spec { + ImportSpecifier::Named { imported, .. } => { + demand.names.insert(imported.clone()); + } + ImportSpecifier::Default { .. } => { + demand.names.insert("default".into()); + } + ImportSpecifier::Namespace { .. } => demand.all = true, + } + } + self.demand(Path::new(path), demand); + } + } + + fn forwarded(&mut self, edge: &Edge, ctx: &mut CompilationContext) -> Option { + let needed = self.demands.get(&edge.from).cloned().unwrap_or_default(); + // An effectful edge retains its complete original export surface, so + // namespace getters cannot refer to missing transitive bindings. + let force = !edge.safe || edge.from == edge.target; + match &edge.export { + Export::ReExport { + imported, exported, .. + } => (force || needed.contains(exported)).then(|| Demand::name(imported)), + Export::NamespaceReExport { name, .. } => { + (force || needed.contains(name)).then(Demand::all) + } + Export::ExportAll { .. } => { + if force { + return Some(Demand::all()); + } + if needed.all { + // `export *` forwards neither default nor erased TS + // declarations, even to a consumer of the full namespace. + return self + .scan + .may_have_star_exports(&edge.source_path, ctx) + .then(Demand::all); + } + let names: HashSet<_> = needed + .names + .into_iter() + .filter(|name| { + name != "default" && self.scan.might_export(&edge.source_path, name, ctx) + }) + .collect(); + (!names.is_empty()).then_some(Demand { all: false, names }) + } + Export::Named { .. } => None, + } + } +} + +/// Return whether the ordinary DFS should visit this target immediately. +pub(super) fn record( + ctx: &mut CompilationContext, + from: &Path, + index: usize, + export: &Export, + target: &Path, + source_path: &Path, +) -> bool { + if !enabled() { + return true; + } + let mut state = std::mem::take(&mut ctx.reexport_pruner); + let safe = state.scan.declared_pure(from) && state.scan.can_drop_tree(source_path, ctx); + let mut edge = Edge { + from: from.to_owned(), + index, + target: target.to_owned(), + source_path: source_path.to_owned(), + export: export.clone(), + safe, + active: false, + }; + if let Some(demand) = state.forwarded(&edge, ctx) { + state.demand(target, demand); + edge.active = true; + } + let active = edge.active; + state.edges.push(edge); + ctx.reexport_pruner = state; + active +} + +/// Revisit postponed edges after later direct/dynamic imports add demand. +/// This also forwards new names across already-active barrel edges. Demand +/// only grows, so cycles converge without depending on DFS visitation order. +pub(super) fn settle(ctx: &mut CompilationContext) -> Vec { + let mut state = std::mem::take(&mut ctx.reexport_pruner); + let mut edges = std::mem::take(&mut state.edges); + let mut pending = Vec::new(); + loop { + let mut changed = false; + for edge in &mut edges { + if let Some(demand) = state.forwarded(edge, ctx) { + changed |= state.demand(&edge.target, demand); + if !edge.active { + edge.active = true; + pending.push(edge.source_path.clone()); + } + } + } + if !changed { + break; + } + } + state.edges = edges; + ctx.reexport_pruner = state; + pending +} + +pub(crate) fn finish(ctx: &mut CompilationContext) { + let mut state = std::mem::take(&mut ctx.reexport_pruner); + let mut removed: HashMap> = HashMap::new(); + let mut omitted = HashSet::new(); + for edge in &state.edges { + if !edge.active { + removed + .entry(edge.from.clone()) + .or_default() + .insert(edge.index); + state.scan.static_tree(&edge.source_path, &mut omitted); + } + } + for (path, indices) in removed { + if let Some(module) = ctx.native_modules.get_mut(&path) { + let mut index = 0; + module.exports.retain(|_| { + let keep = !indices.contains(&index); + index += 1; + keep + }); + } + } + state.pruned = omitted + .iter() + .filter(|p| !ctx.native_modules.contains_key(*p)) + .count(); + ctx.reexport_pruner = state; +} + +pub(crate) fn write_graph(ctx: &mut CompilationContext, entry: &Path) -> anyhow::Result<()> { + super::super::init_order::classify_eager_modules(ctx, entry); + let modules: Vec<_> = ctx.native_modules.iter().map(|(path, module)| { + serde_json::json!({ + "path": path, + "init": if module.init_kind == perry_hir::ModuleInitKind::Eager { "eager" } else { "deferred" }, + }) + }).collect(); + let graph = serde_json::json!({"modules": modules, "pruned": ctx.reexport_pruner.pruned}); + std::fs::write( + ctx.cache_dir.join("module-graph.json"), + serde_json::to_vec_pretty(&graph)?, + )?; + Ok(()) +} diff --git a/crates/perry/src/commands/compile/collect_modules/reexport_prune/forwarding.rs b/crates/perry/src/commands/compile/collect_modules/reexport_prune/forwarding.rs new file mode 100644 index 0000000000..ccad9a82f0 --- /dev/null +++ b/crates/perry/src/commands/compile/collect_modules/reexport_prune/forwarding.rs @@ -0,0 +1,197 @@ +//! Published barrels (notably Remeda) spell re-exports as imports followed by +//! `export { local as public }`. Normalize only modules whose runtime imports +//! are named bindings forwarded through local export lists, with an entirely +//! side-effect-free static dependency tree. Converting the complete import +//! group preserves dependency order even when pure initializers form a cycle. + +use std::collections::{HashMap, HashSet}; +use std::path::Path; + +use swc_ecma_ast as ast; + +use super::CompilationContext; + +pub(crate) fn normalize( + module: &ast::Module, + path: &Path, + ctx: &mut CompilationContext, +) -> Option { + if !super::enabled() + || !module.body.iter().all(|item| { + matches!( + item, + ast::ModuleItem::ModuleDecl( + ast::ModuleDecl::Import(_) + | ast::ModuleDecl::ExportNamed(_) + | ast::ModuleDecl::ExportAll(_) + ) | ast::ModuleItem::Stmt(ast::Stmt::Empty(_)) + ) + }) + { + return None; + } + // Import aliases/attributes can change resolution or select a file loader. + // Re-export collection does not share those paths; retain such imports. + if module.body.iter().any(|item| { + matches!(item, + ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(import)) + if import.phase != ast::ImportPhase::Evaluation + || import.with.is_some() + || ctx.package_aliases.contains_key(import.src.value.to_string_lossy().as_ref()) + ) + }) { + return None; + } + + let mut exports: HashMap> = HashMap::new(); + for item in &module.body { + let ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed(export)) = item else { + continue; + }; + if export.src.is_some() || export.type_only { + continue; + } + for spec in &export.specifiers { + let ast::ExportSpecifier::Named(named) = spec else { + return None; + }; + if named.is_type_only { + continue; + } + let ast::ModuleExportName::Ident(local) = &named.orig else { + return None; + }; + let mut single = export.clone(); + let mut named = named.clone(); + // Renaming orig to the imported name below must not rename the + // public export when the original list omitted `as public`. + named.exported = Some(named.exported.clone().unwrap_or_else(|| named.orig.clone())); + single.specifiers = vec![ast::ExportSpecifier::Named(named)]; + exports + .entry(local.sym.to_string()) + .or_default() + .push(single); + } + } + let candidates: HashSet = module + .body + .iter() + .filter_map(|item| match item { + ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(import)) if !import.type_only => { + Some(import) + } + _ => None, + }) + .flat_map(|import| import.specifiers.iter()) + .filter_map(|spec| match spec { + ast::ImportSpecifier::Named(named) + if !named.is_type_only && exports.contains_key(named.local.sym.as_ref()) => + { + Some(named.local.sym.to_string()) + } + _ => None, + }) + .collect(); + if candidates.is_empty() { + return None; + } + + // Collection visits imports before re-exports. Moving only part of that + // group (or mixing it with existing re-exports) can reverse the entry into + // a cycle: even sideEffects:false modules can read each other's exported + // `var` initializers. Require the entire runtime group to move together. + if module.body.iter().any(|item| match item { + ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(import)) if !import.type_only => { + import.specifiers.is_empty() + || import.specifiers.iter().any(|spec| match spec { + ast::ImportSpecifier::Named(named) => { + !named.is_type_only && !candidates.contains(named.local.sym.as_ref()) + } + _ => true, + }) + } + ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed(export)) => export.src.is_some(), + ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportAll(_)) => true, + _ => false, + }) { + return None; + } + + let mut state = std::mem::take(&mut ctx.reexport_pruner); + let safe = state.scan.can_drop_tree(path, ctx); + ctx.reexport_pruner = state; + if !safe { + return None; + } + + let mut result = module.clone(); + result.body.clear(); + for item in &module.body { + match item { + ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(import)) if !import.type_only => { + let mut remaining = import.clone(); + let mut forwarded = Vec::new(); + remaining.specifiers.retain(|spec| { + let ast::ImportSpecifier::Named(named) = spec else { + return true; + }; + if named.is_type_only || !candidates.contains(named.local.sym.as_ref()) { + return true; + } + let imported = named + .imported + .clone() + .unwrap_or_else(|| ast::ModuleExportName::Ident(named.local.clone())); + for template in &exports[named.local.sym.as_ref()] { + let mut export = template.clone(); + export.span = import.span; + export.src = Some(import.src.clone()); + export.with = import.with.clone(); + let ast::ExportSpecifier::Named(spec) = &mut export.specifiers[0] else { + unreachable!(); + }; + spec.orig = imported.clone(); + forwarded.push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed( + export, + ))); + } + false + }); + // Preserve genuine bare imports, even under sideEffects:false. + if !remaining.specifiers.is_empty() || forwarded.is_empty() { + result + .body + .push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import( + remaining, + ))); + } + result.body.extend(forwarded); + } + ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed(export)) + if export.src.is_none() && !export.type_only => + { + let mut remaining = export.clone(); + remaining.specifiers.retain(|spec| match spec { + ast::ExportSpecifier::Named(named) if !named.is_type_only => { + match &named.orig { + ast::ModuleExportName::Ident(local) => { + !candidates.contains(local.sym.as_ref()) + } + _ => true, + } + } + _ => true, + }); + if !remaining.specifiers.is_empty() { + result + .body + .push(ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed( + remaining, + ))); + } + } + _ => result.body.push(item.clone()), + } + } + Some(result) +} diff --git a/crates/perry/src/commands/compile/collect_modules/reexport_prune/scan.rs b/crates/perry/src/commands/compile/collect_modules/reexport_prune/scan.rs new file mode 100644 index 0000000000..a24c61736c --- /dev/null +++ b/crates/perry/src/commands/compile/collect_modules/reexport_prune/scan.rs @@ -0,0 +1,341 @@ +//! Lightweight AST summaries, never HIR lowering or code generation. A +//! negative export lookup is valid only after traversing every export-star +//! branch; unknown syntax/resolution keeps the edge. Static effect proofs +//! include dependencies outside the declaring package (and terminate cycles). + +use std::collections::{HashMap, HashSet}; +use std::path::{Path, PathBuf}; + +use swc_ecma_ast as ast; +use swc_ecma_visit::{Visit, VisitWith}; + +use super::super::super::CompilationContext; +use super::super::import_helpers::cached_resolve_import_with_lexical_base; +use super::side_effects::Contracts; + +#[derive(Clone, Default)] +struct Summary { + names: HashSet, + stars: Vec, + dependencies: Vec, + unknown_exports: bool, + unknown_dependencies: bool, +} + +#[derive(Default)] +pub(super) struct Scanner { + summaries: HashMap, + droppable: HashMap, + exports: HashMap<(PathBuf, Option), bool>, + contracts: Contracts, +} + +impl Scanner { + pub(super) fn declared_pure(&mut self, path: &Path) -> bool { + self.contracts.is_pure(path) + } + + fn summary(&mut self, path: &Path, ctx: &mut CompilationContext) -> Summary { + let canonical = path.canonicalize().unwrap_or_else(|_| path.to_owned()); + if let Some(summary) = self.summaries.get(&canonical) { + return summary.clone(); + } + // Omitted source still participates in the build-cache proof: edits + // can add a requested export or an effectful dependency. Its enclosing + // package manifests are fingerprinted by config_inputs_for as well. + ctx.resolve_inputs.insert(canonical.clone()); + let mut result = Summary::default(); + let parsed = std::fs::read_to_string(path).ok().and_then(|source| { + if super::super::super::cjs_wrap::is_commonjs(&source) { + return None; + } + perry_parser::parse_typescript(&source, &path.to_string_lossy()).ok() + }); + if let Some(module) = parsed { + let defined = ctx.parsed_defines.apply(&module); + let module = defined.as_ref().unwrap_or(&module); + let mut opaque = OpaqueLoads::default(); + module.visit_with(&mut opaque); + result.unknown_dependencies = opaque.0; + for item in &module.body { + let ast::ModuleItem::ModuleDecl(decl) = item else { + continue; + }; + let mut dependency = None; + let mut star = false; + match decl { + ast::ModuleDecl::Import(import) if !import.type_only => { + if import.specifiers.is_empty() + || import.specifiers.iter().any( + |s| !matches!(s, ast::ImportSpecifier::Named(n) if n.is_type_only), + ) + { + dependency = Some(&import.src); + } + } + ast::ModuleDecl::ExportAll(export) if !export.type_only => { + dependency = Some(&export.src); + star = true; + } + ast::ModuleDecl::ExportNamed(export) if !export.type_only => { + let mut runtime = export.specifiers.is_empty(); + for spec in &export.specifiers { + let name = match spec { + ast::ExportSpecifier::Named(n) if !n.is_type_only => { + Some(export_name(n.exported.as_ref().unwrap_or(&n.orig))) + } + ast::ExportSpecifier::Namespace(n) => Some(export_name(&n.name)), + ast::ExportSpecifier::Default(_) => { + result.unknown_exports = true; + None + } + _ => None, + }; + if let Some(name) = name { + result.names.insert(name); + runtime = true; + } + } + if runtime { + dependency = export.src.as_ref(); + } + } + ast::ModuleDecl::ExportDecl(export) => match &export.decl { + ast::Decl::Fn(f) => { + result.names.insert(f.ident.sym.to_string()); + } + ast::Decl::Class(c) => { + result.names.insert(c.ident.sym.to_string()); + } + ast::Decl::Var(v) => { + for decl in &v.decls { + pattern_names(&decl.name, &mut result.names); + } + } + ast::Decl::TsEnum(e) => { + result.names.insert(e.id.sym.to_string()); + } + ast::Decl::TsInterface(_) | ast::Decl::TsTypeAlias(_) => {} + _ => result.unknown_exports = true, + }, + ast::ModuleDecl::ExportDefaultDecl(_) + | ast::ModuleDecl::ExportDefaultExpr(_) => { + result.names.insert("default".into()); + } + ast::ModuleDecl::TsImportEquals(_) | ast::ModuleDecl::TsExportAssignment(_) => { + result.unknown_exports = true; + result.unknown_dependencies = true; + } + _ => {} + } + if let Some(source) = dependency { + let source = source.value.to_string_lossy(); + let source = if matches!(decl, ast::ModuleDecl::Import(_)) { + ctx.package_aliases + .get(source.as_ref()) + .cloned() + .unwrap_or_else(|| source.into_owned()) + } else { + source.into_owned() + }; + if let Some(resolved) = + cached_resolve_import_with_lexical_base(&source, path, &canonical, ctx) + { + if resolved.kind == perry_hir::ModuleKind::NativeRust { + result.unknown_dependencies = true; + result.unknown_exports |= star; + } else if !super::super::super::is_declaration_file( + &resolved.canonical_path, + ) { + result.dependencies.push(resolved.source_path.clone()); + if star { + result.stars.push(resolved.source_path); + } + } + } else { + result.unknown_dependencies = true; + result.unknown_exports |= star; + } + } + } + } else { + result.unknown_exports = true; + result.unknown_dependencies = true; + } + self.summaries.insert(canonical, result.clone()); + result + } + + pub(super) fn might_export( + &mut self, + path: &Path, + name: &str, + ctx: &mut CompilationContext, + ) -> bool { + self.exports_match(path, Some(name), ctx) + } + + pub(super) fn may_have_star_exports( + &mut self, + path: &Path, + ctx: &mut CompilationContext, + ) -> bool { + self.exports_match(path, None, ctx) + } + + fn exports_match( + &mut self, + path: &Path, + name: Option<&str>, + ctx: &mut CompilationContext, + ) -> bool { + let key = (path.to_owned(), name.map(str::to_owned)); + if let Some(result) = self.exports.get(&key) { + return *result; + } + let mut seen = HashSet::new(); + let mut work = vec![path.to_owned()]; + let mut found = false; + while let Some(path) = work.pop() { + let canonical = path.canonicalize().unwrap_or_else(|_| path.clone()); + if !seen.insert(canonical) { + continue; + } + let summary = self.summary(&path, ctx); + let matches = match name { + Some(name) => summary.names.contains(name), + None => summary.names.iter().any(|name| name != "default"), + }; + if summary.unknown_exports || matches { + found = true; + break; + } + work.extend(summary.stars); + } + self.exports.insert(key, found); + found + } + + pub(super) fn can_drop_tree(&mut self, path: &Path, ctx: &mut CompilationContext) -> bool { + let root = path.canonicalize().unwrap_or_else(|_| path.to_owned()); + if let Some(result) = self.droppable.get(&root) { + return *result; + } + let mut seen = HashSet::new(); + let mut visiting = HashSet::new(); + let mut work = vec![(path.to_owned(), false)]; + while let Some((path, finished)) = work.pop() { + let canonical = path.canonicalize().unwrap_or_else(|_| path.clone()); + if finished { + visiting.remove(&canonical); + seen.insert(canonical); + continue; + } + if seen.contains(&canonical) { + continue; + } + match self.droppable.get(&canonical) { + Some(true) => continue, + Some(false) => { + self.droppable.insert(root, false); + return false; + } + None => {} + } + if !visiting.insert(canonical.clone()) { + // Dropping a barrel edge can change the entry into a retained + // cycle and thus the values of exported `var` initializers. + // Package contracts permit omission, not cyclic reordering. + for ancestor in visiting { + self.droppable.insert(ancestor, false); + } + self.droppable.insert(root, false); + return false; + } + if !self.declared_pure(&canonical) { + self.droppable.insert(root, false); + return false; + } + let summary = self.summary(&path, ctx); + if summary.unknown_dependencies { + self.droppable.insert(root, false); + return false; + } + work.push((path, true)); + work.extend(summary.dependencies.into_iter().map(|path| (path, false))); + } + // Only cache success for the whole explored set after checking all + // branches. Caching a partially visited cycle could hide an effect. + for path in seen { + self.droppable.insert(path, true); + } + true + } + + pub(super) fn static_tree(&self, root: &Path, seen: &mut HashSet) { + let mut work = vec![root.to_owned()]; + while let Some(path) = work.pop() { + let canonical = path.canonicalize().unwrap_or(path); + if !seen.insert(canonical.clone()) { + continue; + } + if let Some(summary) = self.summaries.get(&canonical) { + work.extend(summary.dependencies.iter().cloned()); + } + } + } +} + +fn export_name(name: &ast::ModuleExportName) -> String { + match name { + ast::ModuleExportName::Ident(i) => i.sym.to_string(), + ast::ModuleExportName::Str(s) => s.value.to_string_lossy().into_owned(), + } +} + +fn pattern_names(pattern: &ast::Pat, names: &mut HashSet) { + match pattern { + ast::Pat::Ident(i) => { + names.insert(i.id.sym.to_string()); + } + ast::Pat::Array(a) => { + for p in a.elems.iter().flatten() { + pattern_names(p, names); + } + } + ast::Pat::Object(o) => { + for p in &o.props { + match p { + ast::ObjectPatProp::KeyValue(p) => pattern_names(&p.value, names), + ast::ObjectPatProp::Assign(p) => { + names.insert(p.key.id.sym.to_string()); + } + ast::ObjectPatProp::Rest(p) => pattern_names(&p.arg, names), + } + } + } + ast::Pat::Assign(p) => pattern_names(&p.left, names), + ast::Pat::Rest(p) => pattern_names(&p.arg, names), + _ => {} + } +} + +#[derive(Default)] +struct OpaqueLoads(bool); + +impl Visit for OpaqueLoads { + fn visit_ident(&mut self, ident: &ast::Ident) { + // Aliased require/eval and CommonJS mixed with ESM cannot supply a + // complete static effect graph. Over-approximating these names is safe. + if matches!(ident.sym.as_ref(), "require" | "eval" | "module") { + self.0 = true; + } + } + + fn visit_jsx_element(&mut self, _: &ast::JSXElement) { + self.0 = true; + } + fn visit_jsx_fragment(&mut self, _: &ast::JSXFragment) { + self.0 = true; + } +} diff --git a/crates/perry/src/commands/compile/collect_modules/reexport_prune/side_effects.rs b/crates/perry/src/commands/compile/collect_modules/reexport_prune/side_effects.rs new file mode 100644 index 0000000000..b7a1a463e0 --- /dev/null +++ b/crates/perry/src/commands/compile/collect_modules/reexport_prune/side_effects.rs @@ -0,0 +1,164 @@ +use std::collections::HashMap; +use std::path::{Path, PathBuf}; + +#[derive(Default)] +pub(super) struct Contracts { + files: HashMap, + manifests: HashMap>, +} + +impl Contracts { + pub(super) fn is_pure(&mut self, path: &Path) -> bool { + if let Some(pure) = self.files.get(path) { + return *pure; + } + let pure = self.lookup(path); + self.files.insert(path.to_owned(), pure); + pure + } + + fn lookup(&mut self, path: &Path) -> bool { + // Stop at the owning package, never inherit a parent's contract across + // nested node_modules. Type-only package.json files inside dist/ may + // omit sideEffects; the owning package's declaration still applies. + let mut package_root = None; + let mut prefix = PathBuf::new(); + let mut components = path.components(); + while let Some(component) = components.next() { + prefix.push(component); + if component.as_os_str() == "node_modules" { + let Some(name) = components.next() else { + return false; + }; + prefix.push(name); + if name.as_os_str().to_string_lossy().starts_with('@') { + let Some(name) = components.next() else { + return false; + }; + prefix.push(name); + } + package_root = Some(prefix.clone()); + } + } + let Some(root) = package_root else { + return false; + }; + for dir in path.parent().into_iter().flat_map(Path::ancestors) { + let manifest = self.manifests.entry(dir.to_owned()).or_insert_with(|| { + std::fs::read(dir.join("package.json")) + .ok() + .and_then(|bytes| serde_json::from_slice(&bytes).ok()) + }); + if manifest.is_none() && dir.join("package.json").exists() { + return false; + } + if let Some(value) = manifest.as_ref().and_then(|v| v.get("sideEffects")) { + return match value { + serde_json::Value::Bool(false) => true, + serde_json::Value::Array(patterns) => { + let Ok(relative) = path.strip_prefix(dir) else { + return false; + }; + let relative = relative.to_string_lossy().replace('\\', "/"); + patterns.iter().all(|pattern| { + pattern + .as_str() + .is_some_and(|pattern| !may_match(pattern, &relative)) + }) + } + _ => false, + }; + } + if dir == root { + break; + } + } + false + } +} + +/// Standard *, ** and ? globs. Unsupported syntax is treated as matching, +/// including negation/extglobs/braces/classes: uncertainty must retain files. +fn may_match(pattern: &str, path: &str) -> bool { + if !pattern.is_ascii() + || !path.is_ascii() + || pattern.starts_with('/') + || pattern.contains(['!', '[', ']', '{', '}', '(', ')', '\\']) + { + return true; + } + let rooted = pattern.starts_with("./"); + let pattern = pattern.strip_prefix("./").unwrap_or(pattern); + let pattern = if rooted || pattern.contains('/') { + pattern.to_owned() + } else { + format!("**/{pattern}") + }; + glob( + &pattern.split('/').collect::>(), + &path.split('/').collect::>(), + ) +} + +fn glob(pattern: &[&str], path: &[&str]) -> bool { + let mut row = vec![false; path.len() + 1]; + row[0] = true; + for part in pattern { + let mut next = vec![false; path.len() + 1]; + if *part == "**" { + next[0] = row[0]; + } + for i in 1..=path.len() { + next[i] = if *part == "**" { + row[i] || next[i - 1] + } else { + row[i - 1] && segment(part.as_bytes(), path[i - 1].as_bytes()) + }; + } + row = next; + } + row[path.len()] +} + +fn segment(pattern: &[u8], text: &[u8]) -> bool { + // Dynamic programming bounds adversarial sequences of stars to O(n*m). + let mut row = vec![false; text.len() + 1]; + row[0] = true; + for c in pattern { + let mut next = vec![false; text.len() + 1]; + if *c == b'*' { + next[0] = row[0]; + } + for i in 1..=text.len() { + next[i] = if *c == b'*' { + row[i] || next[i - 1] + } else { + row[i - 1] && (*c == b'?' || *c == text[i - 1]) + }; + } + row = next; + } + row[text.len()] +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn glob_contracts_fail_closed() { + for (pattern, path) in [ + ("*.css", "nested/a.css"), + ("./init.js", "init.js"), + ("**/init?.js", "deep/init1.js"), + ("**/*.js", "a.js"), + ("!*.js", "a.ts"), + ("[ab].js", "c.ts"), + ("?.js", "ä.js"), + ] { + assert!(may_match(pattern, path), "{pattern} {path}"); + } + assert!(!may_match("./init.js", "nested/init.js")); + assert!(!may_match("*.css", "a.js")); + } +} diff --git a/crates/perry/src/commands/compile/collect_modules/tests.rs b/crates/perry/src/commands/compile/collect_modules/tests.rs index 6d03afcd5c..cd9305ce38 100644 --- a/crates/perry/src/commands/compile/collect_modules/tests.rs +++ b/crates/perry/src/commands/compile/collect_modules/tests.rs @@ -3,7 +3,7 @@ use super::{ collect_js_module_imports, collect_modules, env_defines_for_lowering, - expand_dynamic_import_glob, package_has_unsupported_node_addon, + expand_dynamic_import_glob, file_loader_import_sources, package_has_unsupported_node_addon, refuse_compile_package_native_addon, }; use crate::commands::compile::{CompilationContext, DefineValue}; @@ -788,6 +788,16 @@ fn assert_dynamic_asset_import(source: &str, filename: &str, bytes: &[u8]) { assert!(ctx.native_modules.contains_key(&canonical_asset)); } +#[test] +fn invalid_wtf8_static_asset_specifier_is_not_collected_as_empty() { + let module = perry_parser::parse_typescript( + r#"import asset from "\uD800" with { type: "file" };"#, + "entry.ts", + ) + .expect("lone surrogate import source is valid JavaScript syntax"); + assert!(file_loader_import_sources(&module).is_empty()); +} + #[test] fn dynamic_wasm_import_attribute_returns_embedded_path() { assert_dynamic_asset_import( diff --git a/crates/perry/src/commands/compile/collect_modules/walk.rs b/crates/perry/src/commands/compile/collect_modules/walk.rs index e0ada64141..f41e52eb92 100644 --- a/crates/perry/src/commands/compile/collect_modules/walk.rs +++ b/crates/perry/src/commands/compile/collect_modules/walk.rs @@ -14,6 +14,7 @@ pub(crate) fn collect_modules( progress: &VerboseProgress, mut parse_cache: Option<&mut ParseCache>, ) -> Result<()> { + ctx.reexport_pruner.root(&entry_path.canonicalize()?); let mut states: HashMap = HashMap::new(); let mut stack = vec![WorkFrame::Enter(entry_path.clone())]; // Next.js wall 54 (part 2): a standalone `server.js` loads its page, route, @@ -47,61 +48,78 @@ pub(crate) fn collect_modules( } } } - while let Some(frame) = stack.pop() { - match frame { - WorkFrame::Enter(next_path) => { - let canonical = next_path.canonicalize().map_err(|e| { - anyhow!("Failed to canonicalize {}: {}", next_path.display(), e) - })?; + loop { + while let Some(frame) = stack.pop() { + match frame { + WorkFrame::Enter(next_path) => { + let canonical = next_path.canonicalize().map_err(|e| { + anyhow!("Failed to canonicalize {}: {}", next_path.display(), e) + })?; + // Worker/asset/standalone roots can enter outside ordinary + // import edges. Their full namespace must remain available. + ctx.reexport_pruner.implicit_root(&canonical); - if matches!( - states.get(&canonical), - Some(VisitState::InProgress | VisitState::Done) - ) { - continue; - } - if visited.contains(&canonical) { - states.insert(canonical, VisitState::Done); - continue; - } + if matches!( + states.get(&canonical), + Some(VisitState::InProgress | VisitState::Done) + ) { + continue; + } + if visited.contains(&canonical) { + states.insert(canonical, VisitState::Done); + continue; + } - states.insert(canonical.clone(), VisitState::InProgress); - visited.insert(canonical.clone()); - progress.record(ProgressSnapshot { - stage: "collect-module", - module_path: Some(&canonical), - visited: Some(visited.len()), - collected: Some(ctx.native_modules.len() + ctx.js_modules.len()), - ..Default::default() - }); + states.insert(canonical.clone(), VisitState::InProgress); + visited.insert(canonical.clone()); + progress.record(ProgressSnapshot { + stage: "collect-module", + module_path: Some(&canonical), + visited: Some(visited.len()), + collected: Some(ctx.native_modules.len() + ctx.js_modules.len()), + ..Default::default() + }); - let discovered = collect_module_one( - &next_path, - canonical.clone(), - ctx, - visited, - format, - target, - next_class_id, - progress, - parse_cache.as_deref_mut(), - )?; + let discovered = collect_module_one( + &next_path, + canonical.clone(), + ctx, + visited, + format, + target, + next_class_id, + progress, + parse_cache.as_deref_mut(), + )?; - if let Some(prepared) = discovered.finish { - stack.push(WorkFrame::Finish(prepared)); - } else { - states.insert(canonical, VisitState::Done); + if let Some(prepared) = discovered.finish { + stack.push(WorkFrame::Finish(prepared)); + } else { + states.insert(canonical, VisitState::Done); + } + for child in discovered.children.into_iter().rev() { + stack.push(WorkFrame::Enter(child)); + } } - for child in discovered.children.into_iter().rev() { - stack.push(WorkFrame::Enter(child)); + WorkFrame::Finish(prepared) => { + let canonical = prepared.canonical.clone(); + collect_module_finish( + prepared, + ctx, + visited, + target, + skip_transforms, + progress, + )?; + states.insert(canonical, VisitState::Done); } } - WorkFrame::Finish(prepared) => { - let canonical = prepared.canonical.clone(); - collect_module_finish(prepared, ctx, visited, target, skip_transforms, progress)?; - states.insert(canonical, VisitState::Done); - } } + let pending = reexport_prune::settle(ctx); + if pending.is_empty() { + break; + } + stack.extend(pending.into_iter().rev().map(WorkFrame::Enter)); } Ok(()) } diff --git a/crates/perry/src/commands/compile/collect_modules_helpers.rs b/crates/perry/src/commands/compile/collect_modules_helpers.rs index 494223c2df..ebc0fff0df 100644 --- a/crates/perry/src/commands/compile/collect_modules_helpers.rs +++ b/crates/perry/src/commands/compile/collect_modules_helpers.rs @@ -81,7 +81,8 @@ pub(super) fn file_loader_import_sources(module: &swc_ecma_ast::Module) -> HashS }; let attributes = import.with.as_deref()?; requests_asset_path(attributes) - .then(|| import.src.value.as_str().unwrap_or("").to_string()) + .then(|| import.src.value.as_str().map(str::to_owned)) + .flatten() }) .collect(); diff --git a/crates/perry/src/commands/compile/library_search.rs b/crates/perry/src/commands/compile/library_search.rs index be6192f455..e6007ff72a 100644 --- a/crates/perry/src/commands/compile/library_search.rs +++ b/crates/perry/src/commands/compile/library_search.rs @@ -1255,6 +1255,15 @@ pub(super) fn find_runtime_abort_library(target: Option<&str>) -> Option) -> Option { + if is_windows_target(target) { + return None; + } + find_library("libperry_runtime_core.a", target) +} + /// Find the stdlib library for linking (optional - only needed for native modules) pub(super) fn find_stdlib_library(target: Option<&str>) -> Option { let lib_name = if is_windows_target(target) { diff --git a/crates/perry/src/commands/compile/optimized_libs.rs b/crates/perry/src/commands/compile/optimized_libs.rs index 60b2942e1a..b7d2bffd1f 100644 --- a/crates/perry/src/commands/compile/optimized_libs.rs +++ b/crates/perry/src/commands/compile/optimized_libs.rs @@ -20,6 +20,7 @@ mod driver; mod freshness; mod no_auto; mod paths; +mod prebuilt_core; pub(crate) use driver::build_optimized_libs; pub(crate) use freshness::{ diff --git a/crates/perry/src/commands/compile/optimized_libs/driver.rs b/crates/perry/src/commands/compile/optimized_libs/driver.rs index baa04ec457..6f4d32d765 100644 --- a/crates/perry/src/commands/compile/optimized_libs/driver.rs +++ b/crates/perry/src/commands/compile/optimized_libs/driver.rs @@ -573,6 +573,22 @@ pub(crate) fn build_optimized_libs( let workspace_root = match find_perry_workspace_root() { Some(p) => p, None => { + if super::prebuilt_core::eligible(ctx, cli_features) { + if let Some(runtime) = + super::super::library_search::find_runtime_core_library(target) + { + if matches!(format, OutputFormat::Text) && verbose > 0 { + eprintln!( + " auto-optimize: using prebuilt core runtime: {}", + runtime.display() + ); + } + return OptimizedLibs { + runtime: Some(runtime), + ..OptimizedLibs::empty() + }; + } + } // Not verbose-gated: the fallback links the full-feature // prebuilt stdlib (sqlite/crypto/tokio/…), which typically // adds 5MB+ of code the linker cannot dead-strip (the diff --git a/crates/perry/src/commands/compile/optimized_libs/prebuilt_core.rs b/crates/perry/src/commands/compile/optimized_libs/prebuilt_core.rs new file mode 100644 index 0000000000..d59971d5d8 --- /dev/null +++ b/crates/perry/src/commands/compile/optimized_libs/prebuilt_core.rs @@ -0,0 +1,209 @@ +//! Source-free subset selection shares auto-mode's required-feature analysis. +//! A missing archive or any unsupported requirement retains the full fallback. + +use super::{auto_optimized_cross_features, CompilationContext}; +use std::collections::BTreeSet; + +// The existing conservative member-name analysis also sees `console.log` as +// a possible `Math.log` value read. Keep Math's namespace in the small profile +// rather than weakening that analysis and risking a missing extracted method. +const CORE_FEATURES: &[&str] = &["full", "alloc-mimalloc", "keepalive-anchors", "global-math"]; + +pub(super) fn eligible(ctx: &CompilationContext, cli_features: &[String]) -> bool { + // The first packaged subset supports runtime-only native programs. Keep + // native modules, dynamic/native callbacks and custom feature requests on + // their existing archive paths, even if today's feature list looks small. + if ctx.needs_stdlib + || ctx.needs_ui + || ctx.needs_thread + || ctx.needs_plugins + || ctx.needs_geisterhand + || ctx.needs_wasm_runtime + || !ctx.native_addons.is_empty() + || !ctx.native_module_imports.is_empty() + || !ctx.extra_stdlib_features.is_empty() + || !cli_features.is_empty() + || ctx.native_modules.values().any(needs_global_object_fallback) + // Runtime-owned native modules such as bun:ffi do not enter the + // stdlib feature/import set above. Retain their original HIR import + // provenance so they cannot accidentally select the first subset. + || ctx.native_modules.values().any(|module| { + module.imports.iter().any(|import| { + import.is_native && !import.type_only && !import.runtime_erased + }) + }) + { + return false; + } + auto_optimized_cross_features(ctx, &BTreeSet::new(), cli_features) + .iter() + .all(|feature| { + feature + .strip_prefix("perry-runtime/") + .is_some_and(|name| CORE_FEATURES.contains(&name)) + }) +} + +fn needs_global_object_fallback(module: &perry_hir::Module) -> bool { + if module.references_global_this { + return true; + } + // Runtime keys can select any optional constructor/namespace without its + // name appearing in the source: globalThis[process.argv[2]]. Include HIR + // spellings as well as the source flag to cover global/self aliases, + // escaped identifier spellings and folded `Function("return this")()`. + // As in the existing feature detector, over-inclusion only costs size. + let hir = format!("{module:?}"); + [ + "GlobalThisExpr", + "property: \"globalThis\"", + "property: \"global\"", + "property: \"self\"", + ] + .iter() + .any(|token| hir.contains(token)) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn context() -> CompilationContext { + CompilationContext::new(std::env::current_dir().unwrap()) + } + + #[test] + fn core_profile_uses_the_same_feature_contract_as_packaging() { + let root = super::super::super::find_perry_workspace_root().unwrap(); + let manifest: toml::Value = toml::from_str( + &std::fs::read_to_string(root.join("crates/perry-runtime/Cargo.toml")).unwrap(), + ) + .unwrap(); + let packaged: BTreeSet<_> = manifest["features"]["prebuilt-core"] + .as_array() + .unwrap() + .iter() + .map(|v| v.as_str().unwrap()) + .collect(); + assert_eq!(packaged, CORE_FEATURES.iter().copied().collect()); + assert!(eligible(&context(), &[])); + let mut console_or_math = context(); + console_or_math.uses_global_math = true; + assert!(eligible(&console_or_math, &[])); + } + + #[test] + fn optional_engines_and_dynamic_code_keep_the_full_fallback() { + for configure in [ + |c: &mut CompilationContext| c.uses_regex = true, + |c: &mut CompilationContext| c.uses_temporal = true, + |c: &mut CompilationContext| c.uses_url = true, + |c: &mut CompilationContext| c.uses_string_normalize = true, + |c: &mut CompilationContext| c.uses_intl_segmenter = true, + |c: &mut CompilationContext| c.uses_intl_namespace = true, + |c: &mut CompilationContext| c.uses_intl_locale = true, + |c: &mut CompilationContext| c.uses_intl_datetime = true, + |c: &mut CompilationContext| c.uses_data_url_dynamic_import = true, + |c: &mut CompilationContext| c.uses_diagnostics = true, + |c: &mut CompilationContext| c.uses_global_json = true, + |c: &mut CompilationContext| c.uses_global_reflect = true, + |c: &mut CompilationContext| c.uses_global_atomics = true, + |c: &mut CompilationContext| c.uses_global_url = true, + |c: &mut CompilationContext| c.uses_global_text = true, + |c: &mut CompilationContext| c.uses_global_websocket = true, + |c: &mut CompilationContext| c.uses_global_webcrypto = true, + |c: &mut CompilationContext| c.uses_global_webfetch = true, + |c: &mut CompilationContext| c.uses_proc_ipc = true, + |c: &mut CompilationContext| c.bun_platform = true, + ] { + let mut ctx = context(); + configure(&mut ctx); + assert!(!eligible(&ctx, &[])); + } + } + + #[test] + fn native_modules_callbacks_and_custom_features_keep_existing_archives() { + for configure in [ + |c: &mut CompilationContext| c.needs_stdlib = true, + |c: &mut CompilationContext| c.needs_ui = true, + |c: &mut CompilationContext| c.needs_thread = true, + |c: &mut CompilationContext| c.needs_plugins = true, + |c: &mut CompilationContext| c.needs_geisterhand = true, + |c: &mut CompilationContext| c.needs_wasm_runtime = true, + |c: &mut CompilationContext| { + c.native_module_imports.insert("vm".into()); + }, + |c: &mut CompilationContext| { + c.native_module_imports.insert("worker_threads".into()); + }, + |c: &mut CompilationContext| { + c.native_module_imports.insert("bun:ffi".into()); + }, + ] { + let mut ctx = context(); + configure(&mut ctx); + assert!(!eligible(&ctx, &[])); + } + assert!(!eligible(&context(), &["ios-game-loop".into()])); + } + + #[test] + fn runtime_owned_native_imports_keep_full_without_a_stdlib_marker() { + let mut ctx = context(); + let mut module = perry_hir::Module::new("ffi-consumer"); + module.imports.push(perry_hir::Import { + source: "bun:ffi".into(), + specifiers: Vec::new(), + is_native: true, + module_kind: perry_hir::ModuleKind::NativeRust, + resolved_path: None, + type_only: false, + runtime_erased: false, + is_dynamic: false, + is_dynamic_target: false, + is_deferred_require: false, + is_adopted_require: false, + }); + let key = std::path::PathBuf::from("ffi-consumer.ts"); + ctx.native_modules.insert(key.clone(), module); + assert!(ctx.native_module_imports.is_empty()); + assert!(!ctx.needs_stdlib); + assert!(!eligible(&ctx, &[])); + ctx.native_modules.get_mut(&key).unwrap().imports[0].type_only = true; + assert!(eligible(&ctx, &[])); + let import = &mut ctx.native_modules.get_mut(&key).unwrap().imports[0]; + import.type_only = false; + import.runtime_erased = true; + assert!(eligible(&ctx, &[])); + } + + #[test] + fn global_object_values_keep_optional_engines_available() { + let key = std::path::PathBuf::from("global-consumer.ts"); + for global in ["globalThis", "global", "self"] { + let mut ctx = context(); + let mut module = perry_hir::Module::new("global-consumer"); + module + .init + .push(perry_hir::Stmt::Expr(perry_hir::Expr::PropertyGet { + object: Box::new(perry_hir::Expr::GlobalGet(0)), + property: global.into(), + byte_offset: 0, + })); + ctx.native_modules.insert(key.clone(), module); + assert!(!eligible(&ctx, &[]), "{global} can expose optional engines"); + } + let mut ctx = context(); + let mut module = perry_hir::Module::new("global-consumer"); + module + .init + .push(perry_hir::Stmt::Expr(perry_hir::Expr::GlobalThisExpr)); + ctx.native_modules.insert(key.clone(), module); + assert!(!eligible(&ctx, &[])); + let module = ctx.native_modules.get_mut(&key).unwrap(); + module.init.clear(); + module.references_global_this = true; + assert!(!eligible(&ctx, &[])); + } +} diff --git a/crates/perry/src/commands/compile/run_pipeline.rs b/crates/perry/src/commands/compile/run_pipeline.rs index e5164e2534..a71acbbcd2 100644 --- a/crates/perry/src/commands/compile/run_pipeline.rs +++ b/crates/perry/src/commands/compile/run_pipeline.rs @@ -934,7 +934,24 @@ pub fn run_with_parse_cache( } } + collect_modules::reexport_prune::finish(&mut ctx); run_post_collect_preflight(&args, &mut ctx, format)?; + if std::env::var("PERRY_COLLECT_ONLY").ok().as_deref() == Some("1") { + collect_modules::reexport_prune::write_graph(&mut ctx, &args.input.canonicalize()?)?; + return Ok(CompileResult { + output_path: ctx.cache_dir.join("audit.json"), + target: "module-graph".into(), + bundle_id: None, + is_dylib: false, + codegen_cache_stats: None, + link_cache_stats: None, + build_cache_stats: None, + }); + } + + if let Some(result) = tiny_program::try_compile(&args, &ctx, format, verbose)? { + return Ok(result); + } // #2309: tree-shake the final module graph — prune unreachable // node_modules modules and re-raise any deferred refusal that survives. diff --git a/crates/perry/src/commands/compile/tiny_program/analysis.rs b/crates/perry/src/commands/compile/tiny_program/analysis.rs new file mode 100644 index 0000000000..9fd1240679 --- /dev/null +++ b/crates/perry/src/commands/compile/tiny_program/analysis.rs @@ -0,0 +1,297 @@ +//! Closed allowlist over the original AST, before folding or builtin lowering. +//! Adding an expression requires proving both its value and absence of effects. +use std::collections::HashMap; + +use swc_ecma_ast::{ + Callee, Decl, Expr, Lit, MemberProp, Module, ModuleItem, Pat, Stmt, VarDeclKind, +}; + +const MAX_BYTES: usize = 1024 * 1024; +const MAX_DEPTH: usize = 64; + +#[derive(Debug, PartialEq, Eq)] +pub(super) struct Output { + pub fd: u8, + pub bytes: Vec, +} + +/// Only this module can construct the proof consumed by minimal codegen. +#[derive(Debug)] +pub(super) struct ProvenProgram { + outputs: Vec, +} + +impl ProvenProgram { + pub(super) fn outputs(&self) -> &[Output] { + &self.outputs + } +} + +pub(super) fn analyze(source: &str, filename: &str) -> Result { + if source.len() > MAX_BYTES { + return Err("source exceeds tiny analysis limit"); + } + let mut cache = perry_diagnostics::SourceCache::new(); + let parsed = perry_parser::parse_typescript_with_cache(source, filename, &mut cache) + .map_err(|_| "source did not parse cleanly")?; + if !parsed.diagnostics.is_empty() { + return Err("parser recovery requires normal compilation"); + } + analyze_module(&parsed.module) +} + +fn analyze_module(module: &Module) -> Result { + let mut strings = HashMap::>::new(); + let mut outputs = Vec::new(); + let mut retained_bytes = 0usize; + for item in &module.body { + let ModuleItem::Stmt(stmt) = item else { + return Err("imports and exports require normal compilation"); + }; + match stmt { + Stmt::Empty(_) => {} + Stmt::Decl(Decl::Var(var)) if var.kind == VarDeclKind::Const && !var.declare => { + for decl in &var.decls { + let Pat::Ident(binding) = &decl.name else { + return Err("destructuring is outside the proven subset"); + }; + let name = binding.id.sym.to_string(); + // Reject even a later lexical declaration: the receiver + // would be in its TDZ at an earlier console call. + if name == "console" || strings.contains_key(&name) { + return Err("shadowed console or duplicate lexical binding"); + } + let expr = decl.init.as_deref().ok_or("uninitialized binding")?; + let value = constant_string(expr, &strings, 0)?; + charge(&mut retained_bytes, value.len())?; + strings.insert(name, value); + } + } + Stmt::Expr(stmt) => { + if matches!(stmt.expr.as_ref(), Expr::Lit(Lit::Str(_))) { + continue; // Directives / effect-free string expression. + } + let Expr::Call(call) = stmt.expr.as_ref() else { + return Err("expression is outside the proven subset"); + }; + let Callee::Expr(callee) = &call.callee else { + return Err("unknown callee"); + }; + let Expr::Member(member) = callee.as_ref() else { + return Err("unknown callee"); + }; + if !matches!(member.obj.as_ref(), Expr::Ident(id) if id.sym == *"console") { + return Err("console receiver is not the original builtin"); + } + let MemberProp::Ident(property) = &member.prop else { + return Err("computed console access is not proven"); + }; + let fd = match property.sym.as_ref() { + "log" => 1, + "error" => 2, + _ => return Err("console method is outside the proven subset"), + }; + if call.args.len() != 1 || call.args[0].spread.is_some() { + return Err("console requires one constant string argument"); + } + let value = constant_string(&call.args[0].expr, &strings, 0)?; + let mut bytes = String::from_utf16_lossy(&value).into_bytes(); + bytes.push(b'\n'); + charge(&mut retained_bytes, bytes.len())?; + outputs.push(Output { fd, bytes }); + } + _ => return Err("statement is outside the proven subset"), + } + } + Ok(ProvenProgram { outputs }) +} + +fn charge(total: &mut usize, bytes: usize) -> Result<(), &'static str> { + *total = total + .checked_add(bytes) + .ok_or("constant storage overflow")?; + if *total > MAX_BYTES { + return Err("constant storage exceeds tiny analysis limit"); + } + Ok(()) +} + +fn constant_string( + expr: &Expr, + strings: &HashMap>, + depth: usize, +) -> Result, &'static str> { + if depth >= MAX_DEPTH { + return Err("expression exceeds tiny analysis depth"); + } + let value = match expr { + Expr::Lit(Lit::Str(s)) => s.value.as_wtf8().to_ill_formed_utf16().collect(), + Expr::Ident(id) => strings + .get(id.sym.as_ref()) + .cloned() + .ok_or("string binding is not initialized and proven")?, + Expr::Tpl(template) => { + let mut text = Vec::new(); + let mut size = 0; + for (index, quasi) in template.quasis.iter().enumerate() { + let part: Vec = quasi + .cooked + .as_ref() + .ok_or("uncooked template")? + .as_wtf8() + .to_ill_formed_utf16() + .collect(); + charge(&mut size, part.len())?; + text.extend_from_slice(&part); + if let Some(expr) = template.exprs.get(index) { + let part = constant_string(expr, strings, depth + 1)?; + charge(&mut size, part.len())?; + text.extend_from_slice(&part); + } + } + text + } + Expr::Paren(p) => constant_string(&p.expr, strings, depth + 1)?, + Expr::TsAs(p) => constant_string(&p.expr, strings, depth + 1)?, + Expr::TsTypeAssertion(p) => constant_string(&p.expr, strings, depth + 1)?, + Expr::TsNonNull(p) => constant_string(&p.expr, strings, depth + 1)?, + Expr::TsSatisfies(p) => constant_string(&p.expr, strings, depth + 1)?, + _ => return Err("argument is not a proven effect-free string"), + }; + if value.len() > MAX_BYTES { + return Err("string exceeds tiny analysis limit"); + } + Ok(value) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn proves_empty_and_constant_template_output() { + assert!(analyze("// empty\n;", "app.ts") + .unwrap() + .outputs() + .is_empty()); + let plan = analyze( + "const who = 'world'; const greeting = `hello, ${who}`; console.log(greeting); console.error('done');", + "app.ts", + ).unwrap(); + assert_eq!( + plan.outputs(), + &[ + Output { + fd: 1, + bytes: b"hello, world\n".to_vec() + }, + Output { + fd: 2, + bytes: b"done\n".to_vec() + }, + ] + ); + } + + #[test] + fn preserves_bytes_and_erased_string_assertions() { + let plan = analyze(r#"console.log(('a\0%\n🦀\uD800' as string)!);"#, "app.ts").unwrap(); + assert_eq!(plan.outputs()[0].bytes, "a\0%\n🦀�\n".as_bytes()); + } + + #[test] + fn template_substitution_preserves_surrogate_pairs() { + let plan = analyze( + r#"const low = '\uDC00'; console.log(`\uD800${low}`);"#, + "app.ts", + ) + .unwrap(); + assert_eq!(plan.outputs()[0].bytes, "𐀀\n".as_bytes()); + } + + #[test] + fn every_unproven_construct_falls_back() { + let sources = [ + "let s = 'a'; console.log(s);", + "var s = 'a'; console.log(s);", + "const console = 'fake'; console.log('a');", + "console.log('a'); const console = 'fake';", + "console.log('a'); function console() {}", + "console.log('a'); var console;", + "const {log} = console; log('a');", + "const c = console; c.log('a');", + "console['log']('a');", + "console.log?.('a');", + "console?.log('a');", + "console.log = () => {}; console.log('a');", + "console.log((console.log = () => {}, 'a'));", + "console.log({ get x() { return 'a'; } });", + "console.log('a', 'b');", + "console.log(...['a']);", + "console.log(42);", + "console.log(`n=${42}`);", + "console.log('a' + 'b');", + "console.log(s); const s = 'a';", + "const s = s;", + "const s = 'a'; const s = 'b';", + "const s = 'a'; s = 'b';", + "console.log(String('a'));", + "globalThis.console.log('a');", + "import 'node:fs'; console.log('a');", + "import type {T} from './t';", + "export const s = 'a';", + "export {};", + "function unused() {}", + "if (false) { new Promise(() => {}); } console.log('a');", + "while (false) { const x = []; }", + "for (;;) { const x = {}; }", + "class C {}", + "const f = () => 'a';", + "const x = [];", + "Promise.resolve('a');", + "queueMicrotask(() => {});", + "setTimeout(() => {}, 0);", + "process.on('exit', () => {});", + "process.argv;", + "eval('');", + "new Function('return this')();", + "import('node:fs');", + "throw 'a';", + "try { console.log('a'); } catch {}", + "using x = null;", + "await Promise.resolve();", + "namespace N {}", + "interface T {}", + "declare const x: string;", + "debugger;", + ]; + for source in sources { + assert!( + analyze(source, "app.ts").is_err(), + "incorrectly admitted {source}" + ); + } + } + + #[test] + fn escaped_identifiers_do_not_hide_receiver_shadowing() { + assert!(analyze(r#"const \u0063onsole = 'x'; console.log('a');"#, "app.ts").is_err()); + assert!(analyze(r#"console.log('a'); const \u0063onsole = 'x';"#, "app.ts").is_err()); + assert_eq!( + analyze(r#"\u0063onsole.log('a');"#, "app.ts") + .unwrap() + .outputs()[0] + .bytes, + b"a\n" + ); + } + + #[test] + fn expansion_is_bounded() { + let mut source = String::from("const s0 = 'abcdefgh';"); + for i in 1..20 { + source.push_str(&format!("const s{i} = `${{s{}}}${{s{}}}`;", i - 1, i - 1)); + } + assert!(analyze(&source, "app.ts").is_err()); + } +} diff --git a/crates/perry/src/commands/compile/tiny_program/emit.rs b/crates/perry/src/commands/compile/tiny_program/emit.rs new file mode 100644 index 0000000000..c0595567a2 --- /dev/null +++ b/crates/perry/src/commands/compile/tiny_program/emit.rs @@ -0,0 +1,82 @@ +//! Minimal native entry. Only a proved program can produce its output table. +//! output.c consumes this table without allocation or managed runtime calls. +use std::fmt::Write; + +use super::analysis::ProvenProgram; + +pub(super) fn llvm_ir(program: &ProvenProgram) -> String { + let mut ir = String::from("; Perry proven constant-string program: no managed runtime\n"); + for (index, output) in program.outputs().iter().enumerate() { + let bytes: String = output.bytes.iter().map(|b| format!("\\{b:02X}")).collect(); + writeln!(ir, "@perry_tiny_text_{index} = private unnamed_addr constant [{} x i8] c\"{bytes}\", align 1", output.bytes.len()).unwrap(); + } + if !program.outputs().is_empty() { + ir.push_str("%perry.tiny.output = type { i32, ptr, i64 }\n"); + writeln!( + ir, + "@perry_tiny_outputs = private constant [{} x %perry.tiny.output] [", + program.outputs().len() + ) + .unwrap(); + for (index, output) in program.outputs().iter().enumerate() { + let separator = if index + 1 == program.outputs().len() { + "" + } else { + "," + }; + writeln!(ir, " %perry.tiny.output {{ i32 {}, ptr @perry_tiny_text_{index}, i64 {} }}{separator}", output.fd, output.bytes.len()).unwrap(); + } + ir.push_str("]\ndeclare void @perry_tiny_run_output(ptr, i64)\n"); + } + ir.push_str("define i32 @main(i32 %argc, ptr %argv) {\nentry:\n"); + if !program.outputs().is_empty() { + writeln!( + ir, + " call void @perry_tiny_run_output(ptr @perry_tiny_outputs, i64 {})", + program.outputs().len() + ) + .unwrap(); + } + ir.push_str(" ret i32 0\n}\n"); + ir +} + +#[cfg(test)] +mod tests { + use super::super::analysis::analyze; + use super::*; + + #[test] + fn empty_entry_has_no_external_dependencies() { + let ir = llvm_ir(&analyze("", "app.ts").unwrap()); + assert!(!ir.contains("declare ")); + assert!(!ir.contains("call ")); + assert!(ir.contains("ret i32 0")); + } + + #[test] + fn output_has_only_the_audited_native_helper_dependency() { + let ir = llvm_ir(&analyze("console.log('a\\0%'); console.error('b');", "app.ts").unwrap()); + let declarations: Vec<_> = ir + .lines() + .filter(|line| line.starts_with("declare ")) + .collect(); + assert_eq!( + declarations, + ["declare void @perry_tiny_run_output(ptr, i64)"] + ); + assert!(ir.contains(r#"c"\61\00\25\0A""#)); + for excluded in [ + "@js_", + "@perry_gc", + "mimalloc", + "@malloc", + "@calloc", + "promise", + "shadow", + "statepoint", + ] { + assert!(!ir.contains(excluded), "unexpected dependency {excluded}"); + } + } +} diff --git a/crates/perry/src/commands/compile/tiny_program/mod.rs b/crates/perry/src/commands/compile/tiny_program/mod.rs new file mode 100644 index 0000000000..b37e25bf5e --- /dev/null +++ b/crates/perry/src/commands/compile/tiny_program/mod.rs @@ -0,0 +1,397 @@ +//! Automatic specialization for a strictly proven whole-program subset. +//! This does not use the runtime-feature detector as an effects proof. +mod analysis; +mod emit; + +use std::fs; +use std::process::Command; + +use anyhow::{bail, Context, Result}; + +use super::{CompilationContext, CompileArgs, CompileResult, JavaScriptPlatform}; +use crate::OutputFormat; + +pub(super) fn try_compile( + args: &CompileArgs, + ctx: &CompilationContext, + format: OutputFormat, + verbose: u8, +) -> Result> { + if let Err(reason) = build_eligibility(args, ctx, |name| std::env::var_os(name).is_some()) { + explain_fallback(reason, format, verbose); + return Ok(None); + } + // Reparse the ORIGINAL source, never HIR or a folded/reachable-only body. + // Normal collection/preflight has already validated configuration and the + // graph. A full syntax allowlist prevents dead functions/imports/effects + // from disappearing before this proof can see them. + let source = fs::read_to_string(&args.input)?; + let proof = match analysis::analyze(&source, &args.input.to_string_lossy()) { + Ok(proof) => proof, + Err(reason) => { + explain_fallback(reason, format, verbose); + return Ok(None); + } + }; + let ir = emit::llvm_ir(&proof); + let triple = args + .target + .as_deref() + .and_then(perry_codegen::resolve_target_triple); + let object = perry_codegen::linker::compile_ll_to_object(&ir, triple.as_deref()) + .context("compiling proven tiny-program entry")?; + let staging = tempfile::tempdir().context("staging tiny-program object")?; + let object_path = staging.path().join("tiny.o"); + fs::write(&object_path, object)?; + let stem = args + .input + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or("output"); + let output = args.output.clone().unwrap_or_else(|| { + super::output_path::default_output_path(false, false, args.target.as_deref(), stem) + }); + // Use an absolute output argument so a user filename beginning with '-' + // cannot be interpreted as a linker option. + let absolute_output = if output.is_absolute() { + output.clone() + } else { + std::env::current_dir()?.join(&output) + }; + // The host C compiler supplies the platform's stat/poll/errno ABI. This + // source is embedded in the compiler, so an npm install needs no checkout. + let helper_path = staging.path().join("tiny-output.o"); + if !proof.outputs().is_empty() { + let helper_source = staging.path().join("tiny-output.c"); + fs::write(&helper_source, include_str!("output.c"))?; + let mut cc = Command::new("cc"); + if cfg!(target_os = "macos") { + cc.args([ + "-arch", + if cfg!(target_arch = "aarch64") { + "arm64" + } else { + "x86_64" + }, + ]); + } + cc.args(["-std=c11", "-O2", "-c"]) + .arg(&helper_source) + .arg("-o") + .arg(&helper_path); + if args.target.as_deref() == Some("macos") { + cc.arg("-mmacosx-version-min=15.0"); + } + let built = cc + .output() + .context("compiling tiny console output helper")?; + if !built.status.success() { + bail!( + "tiny output helper failed: {}", + String::from_utf8_lossy(&built.stderr) + ); + } + } + super::run_lock_verify_for_compile(ctx, args.target.as_deref())?; + let mut command = Command::new("cc"); + if cfg!(target_os = "macos") { + command.args([ + "-arch", + if cfg!(target_arch = "aarch64") { + "arm64" + } else { + "x86_64" + }, + ]); + } + if !proof.outputs().is_empty() { + command.arg(&helper_path); + } + if args.target.as_deref() == Some("macos") { + command.arg("-mmacosx-version-min=15.0"); + } + command.arg(&object_path).arg("-o").arg(&absolute_output); + if cfg!(target_os = "macos") { + command.arg("-Wl,-dead_strip"); + } else { + command.arg("-Wl,--gc-sections"); + } + if verbose > 0 && matches!(format, OutputFormat::Text) { + println!( + " using proven tiny program: {} constant output calls; no managed runtime", + proof.outputs().len() + ); + println!(" tiny link: {command:?}"); + } + let linked = command + .output() + .context("linking tiny program with the host C toolchain")?; + if !linked.status.success() { + bail!( + "tiny-program link failed:\n{}{}", + String::from_utf8_lossy(&linked.stdout), + String::from_utf8_lossy(&linked.stderr) + ); + } + if args.keep_intermediates { + fs::copy(&object_path, output.with_extension("tiny.o"))?; + fs::write(output.with_extension("tiny.ll"), &ir)?; + if !proof.outputs().is_empty() { + fs::copy(&helper_path, output.with_extension("tiny-output.o"))?; + fs::write( + output.with_extension("tiny-output.c"), + include_str!("output.c"), + )?; + } + } + super::post_link::strip_final_binary( + ctx, + &output, + args.target.as_deref(), + false, + false, + false, + false, + false, + false, + ); + if matches!(format, OutputFormat::Text) { + println!("Wrote executable: {}", output.display()); + } + super::post_link::print_binary_size(format, &output); + super::size_report::emit_size_report(format, &output, args.report_size); + if matches!(format, OutputFormat::Json) { + println!( + "{}", + serde_json::json!({ + "success": true, + "output": output, + "native_modules": ctx.native_modules.len(), + "js_modules": ctx.js_modules.len(), + "build_cache": {"hit": false, "miss_reason": "tiny program specialization"}, + "codegen_cache": null, + "link_cache": { + "linked": 1, "skipped": 0, "object_fingerprints_used": 0, + "object_files_hashed": 0, "external_inputs_hashed": 0, + }, + "runtimeProfile": "tiny", + "outputCalls": proof.outputs().len(), + }) + ); + } + Ok(Some(CompileResult { + output_path: output, + target: args.target.clone().unwrap_or_else(|| "native".to_string()), + bundle_id: None, + is_dylib: false, + codegen_cache_stats: None, + link_cache_stats: None, + build_cache_stats: None, + })) +} + +fn explain_fallback(reason: &str, format: OutputFormat, verbose: u8) { + if verbose > 0 && matches!(format, OutputFormat::Text) { + println!(" tiny program fallback: {reason}"); + } +} + +fn build_eligibility( + args: &CompileArgs, + ctx: &CompilationContext, + has_env: impl Fn(&str) -> bool, +) -> Result<(), &'static str> { + let native_target = args.target.as_deref().is_none_or(|target| { + target == "native" + || (cfg!(all(target_os = "macos", target_arch = "aarch64")) && target == "macos") + || (cfg!(all(target_os = "linux", target_arch = "x86_64")) && target == "linux") + || (cfg!(all(target_os = "linux", target_arch = "aarch64")) + && target == "linux-aarch64") + }); + if !cfg!(all( + target_pointer_width = "64", + any(target_arch = "x86_64", target_arch = "aarch64"), + any(target_os = "macos", target_os = "linux") + )) || !native_target + || args.libc.is_some() + || args.output_type != "executable" + || args.platform != JavaScriptPlatform::Node + { + return Err("requires a native Linux/macOS standalone Node-compatible executable"); + } + // Honor the existing general optimization opt-out; there is no new flag + // or environment switch to enable this specialization. + if args.no_auto_optimize || has_env("PERRY_NO_AUTO_OPTIMIZE") { + return Err("automatic optimization disabled"); + } + if args.no_link + || args.type_check + || args.print_hir + || args.trace.is_some() + || args.focus.is_some() + || args.debug_symbols + || args.opt_report.is_some() + || args.statepoint_report.is_some() + || args.explain_lowering + || args.verify_native_regions + || args.typed_feedback_profile.is_some() + || args.typed_feedback_sites.is_some() + { + return Err("requested build diagnostics require the normal pipeline"); + } + if args.enable_wasm_runtime + || args.bundle_extensions.is_some() + || args.app_bundle_id.is_some() + || !args.embed.is_empty() + || !args.asset_module.is_empty() + || args.bunfs_root.is_some() + || args.features.is_some() + || args.minimal_stdlib + || args.enable_geisterhand + || args.geisterhand_port.is_some() + || ctx.needs_ui + || ctx.needs_plugins + || ctx.needs_geisterhand + || ctx.needs_wasm_runtime + || ctx.precompile_capture + || !ctx.native_libraries.is_empty() + || !ctx.embedded_assets.is_empty() + || !ctx.define.is_empty() + || ctx.emit_attest + || ctx.emit_sandbox + || ctx.lockdown + { + return Err("host features or source transforms require the normal pipeline"); + } + if ctx.native_modules.len() != 1 + || !ctx.js_modules.is_empty() + || !ctx.native_module_imports.is_empty() + || !ctx.native_addons.is_empty() + || ctx.needs_thread + || ctx.uses_diagnostics + { + return Err("program is not a standalone effect-free source graph"); + } + // These existing compiler diagnostics require ordinary generated helpers. + // Merely retaining symbols (PERRY_KEEP_SYMBOLS) is safe and remains useful + // for checking that the specialized link truly omits the runtime. + for name in [ + "PERRY_DEBUG_SYMBOLS", + "PERRY_OPT_REPORT", + "PERRY_SAVE_LL", + "PERRY_LLVM_KEEP_IR", + "PERRY_NATIVEINST_DIAG", + "PERRY_SEGVIEW_DIAG", + "PERRY_OUTLINE_ENTRY_REPORT", + "PERRY_EXTRA_LINK_ARGS", + ] { + if has_env(name) { + return Err("compiler instrumentation requires the normal pipeline"); + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::Parser; + + #[derive(Parser)] + struct TestCli { + #[command(flatten)] + args: CompileArgs, + } + + fn defaults() -> (CompileArgs, CompilationContext) { + let args = TestCli::parse_from(["perry", "app.ts"]).args; + let mut ctx = CompilationContext::new(".".into()); + ctx.native_modules + .insert("app.ts".into(), perry_hir::Module::new("app")); + (args, ctx) + } + + #[test] + #[cfg(all( + any(target_os = "linux", target_os = "macos"), + any(target_arch = "x86_64", target_arch = "aarch64") + ))] + fn ordinary_native_build_needs_no_enabling_switch() { + let (mut args, ctx) = defaults(); + assert!(build_eligibility(&args, &ctx, |_| false).is_ok()); + args.keep_intermediates = true; + args.report_size = true; + assert!(build_eligibility(&args, &ctx, |name| name == "PERRY_KEEP_SYMBOLS").is_ok()); + } + + #[test] + fn unsupported_build_requests_keep_the_normal_pipeline() { + for change in [ + |a: &mut CompileArgs| a.target = Some("windows".into()), + |a: &mut CompileArgs| a.libc = Some("musl".into()), + |a: &mut CompileArgs| a.output_type = "dylib".into(), + |a: &mut CompileArgs| a.platform = JavaScriptPlatform::Bun, + |a: &mut CompileArgs| a.no_auto_optimize = true, + |a: &mut CompileArgs| a.no_link = true, + |a: &mut CompileArgs| a.type_check = true, + |a: &mut CompileArgs| a.print_hir = true, + |a: &mut CompileArgs| a.debug_symbols = true, + |a: &mut CompileArgs| a.trace = Some("llvm".into()), + |a: &mut CompileArgs| a.focus = Some("main".into()), + |a: &mut CompileArgs| a.explain_lowering = true, + |a: &mut CompileArgs| a.verify_native_regions = true, + |a: &mut CompileArgs| a.enable_wasm_runtime = true, + |a: &mut CompileArgs| a.bundle_extensions = Some("ext".into()), + |a: &mut CompileArgs| a.app_bundle_id = Some("app".into()), + |a: &mut CompileArgs| a.embed.push("asset".into()), + |a: &mut CompileArgs| a.asset_module.push("asset".into()), + |a: &mut CompileArgs| a.features = Some("full".into()), + |a: &mut CompileArgs| a.minimal_stdlib = true, + |a: &mut CompileArgs| a.enable_geisterhand = true, + ] { + let (mut args, ctx) = defaults(); + change(&mut args); + assert!(build_eligibility(&args, &ctx, |_| false).is_err()); + } + let (args, ctx) = defaults(); + for env in [ + "PERRY_NO_AUTO_OPTIMIZE", + "PERRY_DEBUG_SYMBOLS", + "PERRY_EXTRA_LINK_ARGS", + "PERRY_SAVE_LL", + "PERRY_OPT_REPORT", + ] { + assert!( + build_eligibility(&args, &ctx, |name| name == env).is_err(), + "{env}" + ); + } + } + + #[test] + fn host_callbacks_plugins_and_graph_extensions_cannot_bypass_the_proof() { + for change in [ + |c: &mut CompilationContext| c.needs_ui = true, + |c: &mut CompilationContext| c.needs_plugins = true, + |c: &mut CompilationContext| c.needs_geisterhand = true, + |c: &mut CompilationContext| c.needs_wasm_runtime = true, + |c: &mut CompilationContext| c.needs_thread = true, + |c: &mut CompilationContext| c.uses_diagnostics = true, + |c: &mut CompilationContext| c.precompile_capture = true, + |c: &mut CompilationContext| c.emit_attest = true, + |c: &mut CompilationContext| c.emit_sandbox = true, + |c: &mut CompilationContext| c.lockdown = true, + |c: &mut CompilationContext| { + c.native_module_imports.insert("bun:ffi".into()); + }, + |c: &mut CompilationContext| { + c.native_modules + .insert("extra.ts".into(), perry_hir::Module::new("extra")); + }, + ] { + let (args, mut ctx) = defaults(); + change(&mut ctx); + assert!(build_eligibility(&args, &ctx, |_| false).is_err()); + } + } +} diff --git a/crates/perry/src/commands/compile/tiny_program/output.c b/crates/perry/src/commands/compile/tiny_program/output.c new file mode 100644 index 0000000000..0f52eea4d0 --- /dev/null +++ b/crates/perry/src/commands/compile/tiny_program/output.c @@ -0,0 +1,127 @@ +/* The proven program's entire output is immutable and known at compile time. + * Two cursors drain it without a heap, JS callbacks, or Perry's event loop. + * POSIX pipe/socket console writes are asynchronous in Node: a blocked stdout + * must not prevent a subsequent stderr write (e.g. a reader's handshake). + * Files and terminals retain synchronous writes. Console ignores write errors. + */ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include + +struct perry_tiny_output { + int fd; + const unsigned char *bytes; + size_t length; +}; + +_Static_assert(sizeof(struct perry_tiny_output) == 24, "LLVM output record size"); +_Static_assert(offsetof(struct perry_tiny_output, bytes) == 8, "LLVM output pointer offset"); +_Static_assert(offsetof(struct perry_tiny_output, length) == 16, "LLVM output length offset"); + +struct stream_cursor { + size_t record; + size_t offset; + int initialized; + int closed; + int pending; + int asynchronous; +}; + +static void initialize_stream(int fd, struct stream_cursor *cursor) { + struct stat info; + cursor->initialized = 1; + if (fstat(fd, &info) < 0) { + cursor->closed = 1; + return; + } + if (S_ISFIFO(info.st_mode) || S_ISSOCK(info.st_mode)) { + cursor->asynchronous = 1; + int flags = fcntl(fd, F_GETFL); + if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) + cursor->closed = 1; + } else if (isatty(fd)) { + /* Node's POSIX terminal console is synchronous, even if the parent + * supplied a nonblocking descriptor. */ + int flags = fcntl(fd, F_GETFL); + if (flags < 0 || fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) + cursor->closed = 1; + } +} + +static void write_current(const struct perry_tiny_output *outputs, + struct stream_cursor *cursor) { + const struct perry_tiny_output *output = &outputs[cursor->record]; + while (cursor->offset < output->length) { + ssize_t written = write(output->fd, output->bytes + cursor->offset, + output->length - cursor->offset); + if (written > 0) { + cursor->offset += (size_t)written; + continue; + } + if (written < 0 && errno == EINTR) + continue; + if (written < 0 && cursor->asynchronous && + (errno == EAGAIN || errno == EWOULDBLOCK)) { + cursor->pending = 1; + return; + } + cursor->closed = 1; + break; + } + cursor->pending = 0; +} + +void perry_tiny_run_output(const struct perry_tiny_output *outputs, size_t count) { + struct stream_cursor streams[2] = {{0}}; + (void)signal(SIGPIPE, SIG_IGN); + /* Preserve source call order for each initial write. If a stream fills, + * its later calls stay queued in the immutable output table; the other + * stream continues independently. No allocation or byte copying occurs. */ + for (size_t i = 0; i < count; ++i) { + const int fd = outputs[i].fd; /* proof: exactly 1 or 2 */ + struct stream_cursor *cursor = &streams[fd - 1]; + if (!cursor->initialized) + initialize_stream(fd, cursor); + if (cursor->closed || cursor->pending) + continue; + cursor->record = i; + cursor->offset = 0; + write_current(outputs, cursor); + } + /* Only pending output can keep this finite program alive. poll() waits + * for its two standard streams; it cannot run JS or create new work. */ + while (streams[0].pending || streams[1].pending) { + struct pollfd fds[2] = { + {streams[0].pending ? 1 : -1, POLLOUT, 0}, + {streams[1].pending ? 2 : -1, POLLOUT, 0}, + }; + int ready = poll(fds, 2, -1); + if (ready < 0) { + if (errno == EINTR) + continue; + return; + } + for (int stream = 0; stream < 2; ++stream) { + struct stream_cursor *cursor = &streams[stream]; + if (!cursor->pending || !fds[stream].revents) + continue; + for (;;) { + write_current(outputs, cursor); + if (cursor->closed || cursor->pending) + break; + size_t next = cursor->record + 1; + while (next < count && outputs[next].fd != stream + 1) + ++next; + if (next == count) + break; + cursor->record = next; + cursor->offset = 0; + } + } + } +} diff --git a/crates/perry/src/commands/compile/types.rs b/crates/perry/src/commands/compile/types.rs index 2bba63262c..1316022d9c 100644 --- a/crates/perry/src/commands/compile/types.rs +++ b/crates/perry/src/commands/compile/types.rs @@ -624,6 +624,7 @@ pub struct GeneratedAssetModule { pub struct CompilationContext { /// Native TypeScript modules to compile pub native_modules: BTreeMap, + pub(crate) reexport_pruner: super::collect_modules::reexport_prune::ReexportPruner, /// JavaScript modules to interpret via V8 pub js_modules: BTreeMap, /// Declaration sidecars discovered for resolved implementation files. @@ -1217,6 +1218,7 @@ impl CompilationContext { pub fn new(project_root: PathBuf) -> Self { Self { native_modules: BTreeMap::new(), + reexport_pruner: Default::default(), js_modules: BTreeMap::new(), declaration_sidecars: BTreeMap::new(), import_map: BTreeMap::new(), diff --git a/crates/perry/tests/issue_5234_wasm_esm_import.rs b/crates/perry/tests/issue_5234_wasm_esm_import.rs index a8f620488c..e329e0693c 100644 --- a/crates/perry/tests/issue_5234_wasm_esm_import.rs +++ b/crates/perry/tests/issue_5234_wasm_esm_import.rs @@ -250,6 +250,9 @@ console.log("tableCycle=" + runTableCycle()); const fileModule = new WebAssembly.Module(readFileSync(addWasmPath)); const fileInstance = new WebAssembly.Instance(fileModule); console.log("fileInstance=" + fileInstance.exports.add(9, 12)); +WebAssembly.instantiate(fileModule).then((instance) => { + console.log("moduleThen=" + instance.exports.add(10, 11)); +}); const importedModule = new WebAssembly.Module(readFileSync(importedWasmPath)); const importedInstance = new WebAssembly.Instance(importedModule, { @@ -472,6 +475,7 @@ fn wasm_esm_import_instantiates_and_exposes_exports() { assert!(stdout.contains("table=2:5:true"), "stdout:\n{stdout}"); assert!(stdout.contains("tableCycle=4:true"), "stdout:\n{stdout}"); assert!(stdout.contains("fileInstance=21"), "stdout:\n{stdout}"); + assert!(stdout.contains("moduleThen=21"), "stdout:\n{stdout}"); assert!(stdout.contains("fileImported=21"), "stdout:\n{stdout}"); assert!(stdout.contains("instanceLength=1"), "stdout:\n{stdout}"); assert!(stdout.contains("sharedGlobal=7"), "stdout:\n{stdout}"); diff --git a/crates/perry/tests/source_graph_export_regressions.rs b/crates/perry/tests/source_graph_export_regressions.rs index c250983905..ea4df499cb 100644 --- a/crates/perry/tests/source_graph_export_regressions.rs +++ b/crates/perry/tests/source_graph_export_regressions.rs @@ -6,6 +6,9 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::Once; +#[path = "source_graph_export_regressions/issue_10153.rs"] +mod issue_10153; + const GC_ENV_OVERRIDES: &[&str] = &[ "PERRY_GEN_GC", "PERRY_GC_SCAVENGE", @@ -956,5 +959,7 @@ fn mixed_type_and_value_specifier_import_keeps_runtime_edge() { } #[path = "source_graph_export_regressions/issue_10160.rs"] mod issue_10160; +#[path = "source_graph_export_regressions/issue_10180.rs"] +mod issue_10180; #[path = "source_graph_export_regressions/issue_10197.rs"] mod issue_10197; diff --git a/crates/perry/tests/source_graph_export_regressions/issue_10153.rs b/crates/perry/tests/source_graph_export_regressions/issue_10153.rs new file mode 100644 index 0000000000..6f4e4b5ae6 --- /dev/null +++ b/crates/perry/tests/source_graph_export_regressions/issue_10153.rs @@ -0,0 +1,126 @@ +//! Babel's CommonJS accessor re-exports must remain live value bindings. + +use super::{compile_and_run, write}; + +fn fixtures(dir: &std::path::Path) { + write( + dir, + "index.cjs", + include_str!("../../../../test-files/cjs_getter_reexport/index.cjs"), + ); + write( + dir, + "transform.cjs", + include_str!("../../../../test-files/cjs_getter_reexport/transform.cjs"), + ); +} + +#[test] +fn named_cjs_getter_reexport_is_a_live_value() { + let dir = tempfile::tempdir().unwrap(); + fixtures(dir.path()); + write( + dir.path(), + "main.mjs", + include_str!("../../../../test-files/test_cjs_getter_reexport.ts") + .replace("./cjs_getter_reexport/index.cjs", "./index.cjs") + .as_str(), + ); + assert_eq!( + compile_and_run(dir.path(), "main.mjs"), + "0\nfunction\n2\n3\nundefined\n12\n13\n14\n3\n" + ); +} + +#[test] +fn namespace_cjs_getter_reexport_is_a_live_value() { + let dir = tempfile::tempdir().unwrap(); + fixtures(dir.path()); + write( + dir.path(), + "main.mjs", + "import * as ns from './index.cjs';\n\ + console.log(ns.reads());\n\ + console.log(typeof ns.transform);\n\ + console.log(ns.transform(1));\n\ + console.log(ns.value(2));\n\ + console.log(typeof ns.late);\n\ + ns.update();\n\ + console.log(ns.transform(1));\n\ + console.log(ns.value(2));\n\ + console.log(ns.late(3));\n\ + console.log(ns.reads());\n", + ); + assert_eq!( + compile_and_run(dir.path(), "main.mjs"), + "0\nfunction\n2\n3\nundefined\n12\n13\n14\n3\n" + ); +} + +#[test] +fn cjs_getter_reexport_survives_barrels_and_materialized_namespaces() { + let dir = tempfile::tempdir().unwrap(); + fixtures(dir.path()); + write( + dir.path(), + "barrel.mjs", + "export { transform as renamed, update, reads } from './index.cjs';\n", + ); + write( + dir.path(), + "main.mjs", + "import { renamed as transform, update, reads } from './barrel.mjs';\n\ + import * as ns from './barrel.mjs';\n\ + const materialized = ns;\n\ + console.log(reads());\n\ + console.log(transform(1));\n\ + console.log(materialized.renamed(2));\n\ + update();\n\ + console.log(transform(1));\n\ + console.log(materialized.renamed(2));\n\ + console.log(reads());\n", + ); + assert_eq!( + compile_and_run(dir.path(), "main.mjs"), + "0\n2\n3\n12\n13\n4\n" + ); +} + +#[test] +fn dynamic_cjs_namespace_keeps_getter_reexports_live() { + let dir = tempfile::tempdir().unwrap(); + fixtures(dir.path()); + write( + dir.path(), + "main.mjs", + "const ns = await import('./index.cjs');\n\ + const key = process.argv[2] || 'transform';\n\ + console.log(ns.reads());\n\ + console.log(Reflect.get(ns, key)(1));\n\ + ns.update();\n\ + console.log(Reflect.get(ns, key)(1));\n\ + console.log(ns.reads());\n", + ); + assert_eq!(compile_and_run(dir.path(), "main.mjs"), "0\n2\n12\n2\n"); +} + +#[test] +fn ordinary_esm_property_export_keeps_its_snapshot() { + let dir = tempfile::tempdir().unwrap(); + write( + dir.path(), + "value.mjs", + "const _cjs = { value: 1 };\n\ + export const value = _cjs.value;\n\ + export function update() { _cjs.value = 2; }\n", + ); + write( + dir.path(), + "main.mjs", + "import { value, update } from './value.mjs';\n\ + console.log(value);\n\ + update();\n\ + console.log(value);\n", + ); + assert_eq!(compile_and_run(dir.path(), "main.mjs"), "1\n1\n"); +} diff --git a/crates/perry/tests/source_graph_export_regressions/issue_10180.rs b/crates/perry/tests/source_graph_export_regressions/issue_10180.rs new file mode 100644 index 0000000000..45bd5a088f --- /dev/null +++ b/crates/perry/tests/source_graph_export_regressions/issue_10180.rs @@ -0,0 +1,527 @@ +//! Collection and runtime regressions for unused re-export pruning. Each +//! assertion reads the actual collection audit, so a successful executable +//! alone cannot turn the pruning checks into vacuous parity tests. + +use std::path::Path; +use std::process::Command; + +use super::{perry_bin, runtime_dir}; + +fn write(root: &Path, path: &str, text: &str) { + let path = root.join(path); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + std::fs::write(path, text).unwrap(); +} + +fn fixture(side_effects: Option) -> tempfile::TempDir { + let dir = tempfile::tempdir().unwrap(); + write( + dir.path(), + "package.json", + r#"{"perry":{"compilePackages":["fixture","external"],"allow":{"compilePackages":["fixture","external"]}}}"#, + ); + let mut package = serde_json::json!({"name":"fixture","type":"module","main":"index.js"}); + if let Some(value) = side_effects { + package["sideEffects"] = value; + } + write( + dir.path(), + "node_modules/fixture/package.json", + &package.to_string(), + ); + write( + dir.path(), + "node_modules/fixture/index.js", + "export { used } from './used.js'; export { unused } from './unused.js';", + ); + write( + dir.path(), + "node_modules/fixture/used.js", + "export const used = 42;", + ); + write( + dir.path(), + "node_modules/fixture/unused.js", + "export const unused = 99;", + ); + write( + dir.path(), + "main.ts", + "import { used } from 'fixture'; console.log(used);", + ); + dir +} + +fn compile(root: &Path, disabled: bool, collect_only: bool) -> (Vec, String) { + let cache = root.join(if disabled { "cache-off" } else { "cache-on" }); + let binary = root.join(if disabled { "main-off" } else { "main-on" }); + let mut command = Command::new(perry_bin()); + command + .current_dir(root) + .args(["compile", "main.ts", "--no-cache", "--cache-dir"]) + .arg(&cache) + .arg("-o") + .arg(&binary) + .env("PERRY_NO_AUTO_OPTIMIZE", "1") + .env("PERRY_NO_REEXPORT_PRUNE", if disabled { "1" } else { "0" }) + .env("PERRY_COLLECT_ONLY", if collect_only { "1" } else { "0" }); + if !collect_only { + command.env("PERRY_RUNTIME_DIR", runtime_dir()); + } + let result = command.output().unwrap(); + let output = format!( + "{}\n{}", + String::from_utf8_lossy(&result.stdout), + String::from_utf8_lossy(&result.stderr) + ); + assert!(result.status.success(), "{output}"); + assert!( + output.contains("pruned as unreferenced side-effect-free re-exports"), + "{output}" + ); + let audit: serde_json::Value = + serde_json::from_slice(&std::fs::read(cache.join("audit.json")).unwrap()).unwrap(); + let paths = audit["modules"] + .as_array() + .unwrap() + .iter() + .map(|m| m["source"].as_str().unwrap().to_owned()) + .collect(); + if collect_only { + assert!( + !binary.exists(), + "collect-only must stop before code generation" + ); + return (paths, output); + } + let result = Command::new(binary).current_dir(root).output().unwrap(); + assert!( + result.status.success(), + "{}", + String::from_utf8_lossy(&result.stderr) + ); + (paths, String::from_utf8(result.stdout).unwrap()) +} + +fn contains(paths: &[String], file: &str) -> bool { + paths.iter().any(|p| p.replace('\\', "/").ends_with(file)) +} + +#[test] +fn unused_sibling_is_never_collected_and_disable_switch_restores_it() { + let dir = fixture(Some(false.into())); + let (on, stdout) = compile(dir.path(), false, false); + assert_eq!(stdout, "42\n"); + assert!(contains(&on, "/fixture/used.js")); + assert!(!contains(&on, "/fixture/unused.js")); + let (off, stdout_off) = compile(dir.path(), true, false); + assert_eq!(stdout, stdout_off); + assert_eq!(off.len(), on.len() + 1); + assert!(contains(&off, "/fixture/unused.js")); +} + +#[test] +fn missing_or_effectful_contract_keeps_side_effect_order() { + for contract in [None, Some(true.into())] { + let dir = fixture(contract); + write( + dir.path(), + "node_modules/fixture/used.js", + "console.log('used'); export const used = 42;", + ); + write( + dir.path(), + "node_modules/fixture/unused.js", + "console.log('unused'); export const unused = 99;", + ); + write(dir.path(), "node_modules/fixture/index.js", "export { used } from './used.js'; export { unused } from './unused.js'; console.log('barrel');"); + let (paths, output) = compile(dir.path(), false, false); + assert!(contains(&paths, "/fixture/unused.js")); + assert_eq!(output, "used\nunused\nbarrel\n42\n"); + } +} + +#[test] +fn export_star_chain_and_renamed_binding_prune_transitive_siblings() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "node_modules/fixture/index.js", + "export * from './middle.js'; export * from './unused.js';", + ); + write( + dir.path(), + "node_modules/fixture/middle.js", + "export { used as answer } from './used.js'; export * from './extra.js';", + ); + write( + dir.path(), + "node_modules/fixture/extra.js", + "export const extra = 7;", + ); + write( + dir.path(), + "main.ts", + "import { answer } from 'fixture'; console.log(answer);", + ); + let (paths, output) = compile(dir.path(), false, false); + assert_eq!(output, "42\n"); + assert!(contains(&paths, "/fixture/middle.js")); + assert!(!contains(&paths, "/fixture/unused.js")); + assert!(!contains(&paths, "/fixture/extra.js")); +} + +#[test] +fn later_direct_import_restores_a_previously_unused_module() { + let dir = fixture(Some(false.into())); + write(dir.path(), "main.ts", "import { used } from 'fixture'; import { unused } from 'fixture/unused.js'; console.log(used, unused);"); + let (paths, output) = compile(dir.path(), false, false); + assert!(contains(&paths, "/fixture/unused.js")); + assert_eq!(output, "42 99\n"); +} + +#[test] +fn later_importer_adds_demand_to_already_visited_barrel() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "later.ts", + "import { unused } from 'fixture'; export const later = unused;", + ); + write(dir.path(), "main.ts", "import { used } from 'fixture'; import { later } from './later'; console.log(used, later);"); + let (paths, output) = compile(dir.path(), false, false); + assert!(contains(&paths, "/fixture/unused.js")); + assert_eq!(output, "42 99\n"); +} + +#[test] +fn dynamic_import_retains_namespace_and_defers_initialization() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "node_modules/fixture/unused.js", + "console.log('dynamic'); export const unused = 99;", + ); + write(dir.path(), "later.ts", "export async function load() { const ns = await import('fixture/unused.js'); console.log(ns.unused); }"); + write(dir.path(), "main.ts", "import { used } from 'fixture'; import { load } from './later'; console.log(used); load();"); + let (paths, output) = compile(dir.path(), false, false); + assert!(contains(&paths, "/fixture/unused.js")); + assert_eq!(output, "42\ndynamic\n99\n"); +} + +#[test] +fn dynamic_import_of_a_static_barrel_restores_all_exports() { + let dir = fixture(Some(false.into())); + write(dir.path(), "main.ts", "import { used } from 'fixture'; console.log(used); async function load() { const ns = await import('fixture'); console.log(ns.unused); } load();"); + let (paths, output) = compile(dir.path(), false, false); + assert!(contains(&paths, "/fixture/unused.js")); + assert_eq!(output, "42\n99\n"); +} + +#[test] +fn live_binding_is_read_after_mutation_through_reexport() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "node_modules/fixture/used.js", + "export let counter = 0; export function increment() { counter++; }", + ); + write( + dir.path(), + "node_modules/fixture/index.js", + "export { counter, increment } from './used.js'; export * from './unused.js';", + ); + write(dir.path(), "main.ts", "import { counter, increment } from 'fixture'; console.log(counter); increment(); console.log(counter);"); + let (paths, output) = compile(dir.path(), false, false); + assert!(!contains(&paths, "/fixture/unused.js")); + assert_eq!(output, "0\n1\n"); + assert_eq!(output, compile(dir.path(), true, false).1); +} + +#[test] +fn side_effect_globs_preserve_matches_and_external_effect_dependencies() { + let dir = fixture(Some(serde_json::json!(["**/effect*.js"]))); + write(dir.path(), "node_modules/fixture/index.js", "export { used } from './used.js'; export * from './unused.js'; export * from './effect.js';"); + write( + dir.path(), + "node_modules/fixture/effect.js", + "console.log('effect'); export const effect = 1;", + ); + let (paths, output) = compile(dir.path(), false, false); + assert_eq!(output, "effect\n42\n"); + assert!(contains(&paths, "/fixture/effect.js")); + assert!(!contains(&paths, "/fixture/unused.js")); + + write( + dir.path(), + "node_modules/external/package.json", + r#"{"name":"external","main":"index.js"}"#, + ); + write( + dir.path(), + "node_modules/external/index.js", + "console.log('external');", + ); + write( + dir.path(), + "node_modules/fixture/unused.js", + "import 'external'; export const unused = 99;", + ); + let (paths, output) = compile(dir.path(), false, false); + assert!(contains(&paths, "/external/index.js")); + assert!(contains(&paths, "/fixture/unused.js")); + assert_eq!(output, "external\neffect\n42\n"); +} + +#[test] +fn collect_only_handles_star_cycles_and_unknown_globs_conservatively() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "node_modules/fixture/index.js", + "export * from './cycle.js'; export * from './used.js'; export * from './unused.js';", + ); + write( + dir.path(), + "node_modules/fixture/cycle.js", + "export * from './index.js';", + ); + let (paths, _) = compile(dir.path(), false, true); + assert!(contains(&paths, "/fixture/unused.js")); + write( + dir.path(), + "node_modules/fixture/package.json", + r#"{"name":"fixture","main":"index.js","sideEffects":["[ab].js"]}"#, + ); + let (paths, _) = compile(dir.path(), false, true); + assert!(contains(&paths, "/fixture/unused.js")); +} + +#[test] +fn cached_build_rechecks_omitted_sources_and_new_package_contracts() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "node_modules/fixture/index.js", + "export { used } from './used.js'; export * from './dead/index.js';", + ); + write( + dir.path(), + "node_modules/fixture/dead/index.js", + "console.log('restored'); export const unused = 99;", + ); + let binary = dir.path().join("cached-bin"); + let run = || { + let compile = Command::new(perry_bin()) + .current_dir(dir.path()) + .args(["compile", "main.ts", "--cache-dir", "cache", "-o"]) + .arg(&binary) + .env("PERRY_NO_AUTO_OPTIMIZE", "1") + .env("PERRY_RUNTIME_DIR", runtime_dir()) + .env("PERRY_NO_REEXPORT_PRUNE", "0") + .env_remove("PERRY_NO_CACHE") + .env_remove("PERRY_COLLECT_ONLY") + .output() + .unwrap(); + assert!( + compile.status.success(), + "{}", + String::from_utf8_lossy(&compile.stderr) + ); + let output = Command::new(&binary).output().unwrap(); + assert!(output.status.success()); + String::from_utf8(output.stdout).unwrap() + }; + assert_eq!(run(), "42\n"); + assert_eq!(run(), "42\n"); + // No previously fingerprinted file changed: the new manifest must itself + // invalidate the cached proof that dead/index.js has no side effects. + write( + dir.path(), + "node_modules/fixture/dead/package.json", + r#"{"sideEffects":true}"#, + ); + assert_eq!(run(), "restored\n42\n"); + std::fs::remove_file(dir.path().join("node_modules/fixture/dead/package.json")).unwrap(); + assert_eq!(run(), "42\n"); + // Changing only an omitted source introduces an effectful dependency. + write( + dir.path(), + "node_modules/external/package.json", + r#"{"name":"external","main":"index.js"}"#, + ); + write( + dir.path(), + "node_modules/external/index.js", + "console.log('source changed');", + ); + write( + dir.path(), + "node_modules/fixture/dead/index.js", + "import 'external'; export const unused = 99;", + ); + assert_eq!(run(), "source changed\n42\n"); +} + +#[test] +fn namespace_keeps_values_but_star_does_not_forward_types_or_default() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "node_modules/fixture/index.js", + "export * from './used.js'; export * from './types.ts'; export * from './default.js';", + ); + write( + dir.path(), + "node_modules/fixture/types.ts", + "export interface Shape { x: number }; export type Alias = string;", + ); + write( + dir.path(), + "node_modules/fixture/default.js", + "export default 99;", + ); + write( + dir.path(), + "main.ts", + "import * as ns from 'fixture'; console.log(ns.used, Object.keys(ns).join(','));", + ); + let (paths, output) = compile(dir.path(), false, false); + assert!(!contains(&paths, "/fixture/types.ts")); + assert!(!contains(&paths, "/fixture/default.js")); + assert_eq!(output, "42 used\n"); + assert_eq!(output, compile(dir.path(), true, false).1); +} + +#[test] +fn imported_bindings_used_only_by_export_lists_are_reexports() { + let dir = fixture(Some(false.into())); + write(dir.path(), "node_modules/fixture/index.js", "import { used as value } from './used.js'; import { unused as other } from './unused.js'; export { value as used, other as unused };"); + let (paths, output) = compile(dir.path(), false, false); + assert_eq!(output, "42\n"); + assert!(!contains(&paths, "/fixture/unused.js")); + assert_eq!(output, compile(dir.path(), true, false).1); +} + +#[test] +fn forwarding_normalization_preserves_live_aliases() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "node_modules/fixture/used.js", + "export let counter = 0; export function increment() { counter++; }", + ); + write(dir.path(), "node_modules/fixture/index.js", "import { counter as localCounter, increment } from './used.js'; import { unused } from './unused.js'; export { localCounter as counter, increment, unused };"); + write(dir.path(), "main.ts", "import { counter, increment } from 'fixture'; console.log(counter); increment(); console.log(counter);"); + let (paths, output) = compile(dir.path(), false, false); + assert_eq!(output, "0\n1\n"); + assert!(!contains(&paths, "/fixture/unused.js")); + assert_eq!(output, compile(dir.path(), true, false).1); +} + +#[test] +fn imports_used_by_module_code_are_not_normalized() { + let dir = fixture(Some(false.into())); + write(dir.path(), "node_modules/fixture/index.js", "import { used } from './used.js'; import { unused } from './unused.js'; console.log(unused); export { used, unused };"); + let (paths, output) = compile(dir.path(), false, false); + assert_eq!(output, "99\n42\n"); + assert!(contains(&paths, "/fixture/unused.js")); +} + +#[test] +fn import_attributes_keep_forwarding_imports_and_their_loader() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "node_modules/fixture/payload.js", + "export default 99;", + ); + write(dir.path(), "node_modules/fixture/index.js", "import { default as asset } from './payload.js' with { type: 'file' }; import { used } from './used.js'; import { unused } from './unused.js'; export { asset, used, unused };"); + write(dir.path(), "main.ts", "import { asset, used } from 'fixture'; console.log(used, typeof asset, asset.endsWith('payload.js'));"); + let (paths, output) = compile(dir.path(), false, false); + assert_eq!(output, "42 string true\n"); + assert!(contains(&paths, "/fixture/unused.js")); + assert_eq!(output, compile(dir.path(), true, false).1); +} + +#[test] +fn forwarding_barrels_keep_effectful_dependencies_and_bare_imports() { + let dir = fixture(Some(false.into())); + write(dir.path(), "node_modules/fixture/index.js", "import { used } from './used.js'; import { unused } from './unused.js'; import './bare.js'; export { used, unused };"); + write( + dir.path(), + "node_modules/fixture/bare.js", + "export const bare = 1;", + ); + let (paths, output) = compile(dir.path(), false, false); + assert_eq!(output, "42\n"); + assert!(contains(&paths, "/fixture/unused.js")); + assert!(contains(&paths, "/fixture/bare.js")); + + write( + dir.path(), + "node_modules/external/package.json", + r#"{"name":"external","main":"index.js","sideEffects":true}"#, + ); + write( + dir.path(), + "node_modules/external/index.js", + "console.log('effect');", + ); + write( + dir.path(), + "node_modules/fixture/unused.js", + "import 'external'; export const unused = 99;", + ); + let (paths, output) = compile(dir.path(), false, false); + assert_eq!(output, "effect\n42\n"); + assert!(contains(&paths, "/fixture/unused.js")); + assert!(contains(&paths, "/external/index.js")); + assert_eq!(output, compile(dir.path(), true, false).1); +} + +#[test] +fn cyclic_initialization_order_survives_forwarding_and_pruning() { + let dir = fixture(Some(false.into())); + write( + dir.path(), + "node_modules/fixture/index.js", + "import { a } from './a.js'; import * as bns from './b.js'; export { a, bns };", + ); + write( + dir.path(), + "node_modules/fixture/a.js", + "import { b } from './b.js'; export var a = (b ?? 0) + 1;", + ); + write( + dir.path(), + "node_modules/fixture/b.js", + "import { a } from './a.js'; export var b = (a ?? 0) + 1;", + ); + write( + dir.path(), + "main.ts", + "import { a, bns } from 'fixture'; console.log(a, bns.b);", + ); + let (paths, output) = compile(dir.path(), false, false); + assert!(contains(&paths, "/fixture/a.js")); + assert!(contains(&paths, "/fixture/b.js")); + assert_eq!(output, "2 1\n"); + assert_eq!(output, compile(dir.path(), true, false).1); + + write( + dir.path(), + "node_modules/fixture/index.js", + "export { a } from './a.js'; export { b } from './b.js';", + ); + write( + dir.path(), + "main.ts", + "import { b } from 'fixture'; console.log(b);", + ); + let (paths, output) = compile(dir.path(), false, false); + assert!(contains(&paths, "/fixture/a.js")); + assert!(contains(&paths, "/fixture/b.js")); + assert_eq!(output, "1\n"); + assert_eq!(output, compile(dir.path(), true, false).1); +} diff --git a/docs/examples/ui/overview/frame-persistence.ts b/docs/examples/ui/overview/frame-persistence.ts new file mode 100644 index 0000000000..cd99806c2c --- /dev/null +++ b/docs/examples/ui/overview/frame-persistence.ts @@ -0,0 +1,14 @@ +// demonstrates: opt-in desktop window frame persistence across launches +// docs: docs/src/ui/overview.md +// platforms: macos, linux, windows + +import { App, Text } from "perry/ui" + +App({ + title: "Remember my window", + width: 800, + height: 600, + windowState: "normal", + frameAutosaveName: "main", + body: Text("Move or resize this window, close it, and launch again."), +}) diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 8400e3d587..10e4d064dd 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -10,6 +10,8 @@ - [Hello World](getting-started/hello-world.md) - [First Native App](getting-started/first-app.md) - [Project Configuration](getting-started/project-config.md) +- [Small-process memory profile](runtime/small-process-memory.md) +- [Automatic tiny programs](runtime/tiny-programs.md) # Language @@ -185,6 +187,7 @@ # Internals - [Memory Model](internals/memory-model.md) +- [RegExp program ownership and caching](internals/regexp-program-cache.md) - [Garbage Collector](internals/garbage-collector.md) - [Explicit Memory Control](internals/explicit-memory.md) - [The GC rooting invariant (codegen)](internals/gc-rooting-invariant.md) diff --git a/docs/src/cli/flags.md b/docs/src/cli/flags.md index 4c4d226773..8e8b915f98 100644 --- a/docs/src/cli/flags.md +++ b/docs/src/cli/flags.md @@ -618,3 +618,36 @@ coercion. Replay does not relax ownership, alias, lifetime or method-identity checks. Native-region verification requires a consumed fresh replay fact, a matching runtime guard and an explicit fallback/materialization record for every claimed profile selection. + +## Unused re-export collection + +Collection prunes unused `export { name } from`, `export * from`, and namespace +re-export edges by default when the exporting package declares the file free +of side effects and every static dependency of the omitted target has the same +guarantee. Perry honors `sideEffects: false` and arrays of `*`, `**`, and `?` +globs. Unsupported patterns, missing contracts, CommonJS, unresolved dependencies, +and cyclic static dependency trees conservatively retain the edge. Keeping cycles +preserves initialization order even when exported variables read each other. +This pass does not remove direct +imports used by module code or individual declarations. An `import { x }; export { x }` +pair is first normalized to a re-export only in barrels whose runtime imports +are all named bindings forwarded through local export lists, and whose entire +static dependency tree has side-effect-free contracts. Moving the complete +import group preserves dependency order in cycles. Mixed barrels retain their imports. +Imports with attributes, nonstandard phases, or package aliases retain their original resolution. +Namespace and dynamic imports retain the +complete exported surface, and existing dynamic initialization stays deferred. + +Set `PERRY_NO_REEXPORT_PRUNE=1` to disable this collection pass for an A/B build. +The compile summary reports unique omitted modules in the proven static +dependency trees, excluding modules retained by another importer. + +Set `PERRY_COLLECT_ONLY=1` to stop after collection and write `/audit.json` +and `module-graph.json` (paths, eager/deferred initialization, and pruned count) +without generating objects or linking an executable. This mode bypasses the +finished-build cache and runs normal collection/preflight checks. For example: + +```sh +PERRY_COLLECT_ONLY=1 perry compile src/index.ts --cache-dir /tmp/graph-after +PERRY_COLLECT_ONLY=1 PERRY_NO_REEXPORT_PRUNE=1 perry compile src/index.ts --cache-dir /tmp/graph-before +``` diff --git a/docs/src/internals/regexp-program-cache.md b/docs/src/internals/regexp-program-cache.md new file mode 100644 index 0000000000..b99e591dfd --- /dev/null +++ b/docs/src/internals/regexp-program-cache.md @@ -0,0 +1,57 @@ +# RegExp program ownership and caching + +JavaScript RegExp construction uses Perex directly. There is one engine; +lookbehind, backreferences, and other accepted patterns share the same +construction path. `perex_construct.rs` retains eager syntax checking, so +invalid patterns still throw at construction and the first search does not +inherit deferred compilation work. + +`regex/perex_cache.rs` retains immutable programs by source plus canonical +flags. The first lookup uses the source `StringHeader` identity and reads no +pattern bytes. An identity miss hashes the original bytes, then verifies exact +equality within the hash bucket. Independently allocated equal strings share +a program. Flags remain part of both keys; canonical ordering makes `ig` and +`gi` equivalent. Every new RegExp still owns its original source/flags and +independent `lastIndex`. Retained source strings are marked shared to prevent +unique-string append from modifying the cached pattern or OriginalSource. + +The cache holds at most 512 entries and 32 MiB of source/program payload. +Least-recently-used entries are evicted individually. Programs above the byte +limit remain usable through their RegExp owner without cache retention. +The cache registers its scanner before publishing its first root, so programs +that never construct a RegExp add no cache scanner work. Source and program slots are mutable GC roots; +evacuation rewrites them and +rebuilds the source-identity index. Content hashes are address independent. + +Perex's `ProgramWitness` lives beside the immutable words in each program GC +cell (upstream #10166/#10183). The first binding validates the words; subsequent +bindings use that witness in constant work, including bindings from another +RegExp sharing the cached program. Each operation still roots its own program, +so eviction, collection, and reentrant receiver recompilation cannot invalidate +an active search. No borrowed program slice survives a safepoint. Scratch +buffers remain operation-owned and charged to the existing memory budget. + +Compound split/replace/match operations retain program and subject bindings in +`perex_api::Reuse`, checking the current receiver and program cell before reuse. +Within a compound operation, the previous position is reused only with the same +subject binding. Across JavaScript calls, upstream's four-entry scalar position +table can reuse a non-ASCII string's cursor when its concealed address, lengths +and heap generation still agree. Heap changes or in-place length changes +invalidate that identity. The table does not retain a string or program. +Validated heap strings use the existing counted-subject path. + +The fast builtin dispatch guard contains only immutable ShapeIds, field +indices, and epochs, with no untraced GC address. It verifies the current +builtin `exec` value, the receiver's metadata, expando table, and prototype. +Empty-string replacement additionally verifies builtin flags getters and +`Symbol.replace`, and requires primitive strings and numeric `lastIndex`. +Observable overrides take the ordinary dispatch path. Admitted replacements +use full-subject UTF-16 spans and preserve global/sticky index updates and +Unicode empty-match advancement, without allocating exec result arrays. + +`PERRY_REGEX_DIAG=1` reports compile and validation counts, identity/content +hits, bytes hashed, canonical dispatches, searches, and scratch allocation and +growth counts. Diagnostics deliberately inspect source bytes for attribution; +measure timings with diagnostics disabled. Cache payload lives in traced GC +cells; the census separately reports native cache metadata as +`regex.program_cache`. diff --git a/docs/src/runtime/small-process-memory.md b/docs/src/runtime/small-process-memory.md new file mode 100644 index 0000000000..905e601429 --- /dev/null +++ b/docs/src/runtime/small-process-memory.md @@ -0,0 +1,31 @@ +# Small-process memory profile + +On Linux x64 and ARM64 builds using mimalloc, launch a native Perry executable +with `PERRY_MEMORY_PROFILE=small` to prefer lower resident memory for short +programs. This defaults mimalloc's `allow_thp` option to zero before startup +allocations. The normal profile retains mimalloc's existing policy. + +```sh +PERRY_MEMORY_PROFILE=small ./app +``` + +This profile applies to programs that retain the managed runtime. Automatically +specialized [tiny programs](tiny-programs.md) already omit Perry's allocator and +GC, so they do not need this setting. + +An explicit `MIMALLOC_ALLOW_THP=1` overrides the profile; `=0` also works without +the profile. Mimalloc still parses these settings itself. Set the environment +before launching the executable. Changing it from JavaScript is too late to +configure the initial heap. Other profile names and unsupported platforms keep +the existing allocator policy. + +The policy is a tradeoff. Earlier Linux experiments with the equivalent +allocator setting roughly halved tiny-program RSS, but startup gains varied +between CPUs and allocation-heavy workloads sometimes slowed down. Measure +your workload; the profile does not promise a startup or throughput improvement. + +On Linux, mimalloc uses `PR_SET_THP_DISABLE` for this option. That affects the +entire process, including memory managed by other allocators. Embedders that +share mimalloc with a host must configure it before the host's first allocation; +loading Perry later cannot retroactively change the initial heap policy. Setting +`MIMALLOC_ALLOW_THP=0` at host process launch makes that intent explicit. diff --git a/docs/src/runtime/tiny-programs.md b/docs/src/runtime/tiny-programs.md new file mode 100644 index 0000000000..d185109cd1 --- /dev/null +++ b/docs/src/runtime/tiny-programs.md @@ -0,0 +1,59 @@ +# Automatic tiny programs + +Perry automatically specializes a narrow class of standalone programs on native +64-bit Linux and macOS. No enabling flag is needed: + +```typescript +const who: string = "world"; +console.log(`hello, ${who}`); +``` + +A proof over the original, unfolded AST admits only empty statements, literal +string expressions, immutable constant strings/templates, and direct calls to +the unshadowed builtin `console.log` or `console.error` with one constant string. +The evaluator keeps UTF-16 until final output encoding, including surrogate pairs +formed across template substitutions. It bounds analysis depth and stored +constant data; exceeding either bound uses normal compilation. + +Every unproven construct uses the normal compiler/runtime. This includes imports, +exports, unknown calls, console aliases or mutation, computed access, functions, +classes, loops, objects, promises, timers, exceptions, and lifecycle APIs. Dead +code is checked too. A synchronous allocating loop still needs the normal GC. +Host/plugin/library modes, cross targets, source defines, custom runtime features, +and diagnostics requiring normal HIR/codegen also keep the normal pipeline. +The existing general `--no-auto-optimize` option remains respected. + +The specialized executable links its constant data and a small native output +helper against the host C library. It contains no Perry runtime archive, managed +heap, GC initialization or root registration, mimalloc, class tables, promises, +or JS event-loop machinery. Empty programs need no output helper at all. +The ordinary host C compiler/linker is required, as for native linking generally. + +The helper preserves byte output, final newlines, per-stream order, partial writes, +EINTR, broken pipes and ignored console write errors. Files and terminals use +synchronous writes. Pipes/sockets can fill: two stack cursors retain positions +in the immutable output table and wait for writability without blocking progress +on the other stream. This bounded I/O drain cannot execute JS callbacks or create +new work. It is necessary to preserve Node's independent stdout/stderr behavior; +removing the JS event loop does not mean ignoring backpressure. +See [Node's process I/O contract](https://nodejs.org/api/process.html#a-note-on-process-io). + +Use ordinary `-v` compiler output to see the selected path or fallback reason. +`--keep-intermediates` retains `.tiny.ll`, `.tiny.o`, and the exact output helper +source/object for inspection. `PERRY_KEEP_SYMBOLS=1` retains linked symbols without +changing selection. Runtime GC diagnostics have no managed heap to observe in a +tiny executable; use normal compilation when investigating runtime behavior. + +Local verification: + +```sh +RUST_TEST_THREADS=1 cargo test --profile perry-dev -p perry --bin perry -- tiny_program --test-threads=1 +python3 scripts/verify_tiny_program.py /absolute/path/to/perry /absolute/path/to/results +``` + +The verifier requires the pinned Node 26.5.1 oracle, records compiler/source/binary +hashes, executes positive and fallback programs, inspects linked symbols, and +checks independent-stream handshakes under backpressure. Run it against the actual +installed npm compiler outside a source checkout for package acceptance. Its +`--tiny-only` switch is a verifier development scope, never an enabling compiler +flag; that scope does not satisfy full fallback acceptance. diff --git a/docs/src/ui/overview.md b/docs/src/ui/overview.md index 65a0c1982b..6d22ef639c 100644 --- a/docs/src/ui/overview.md +++ b/docs/src/ui/overview.md @@ -52,6 +52,7 @@ Every Perry UI app starts with `App()`: | `body` | widget | Root widget | | `icon` | string | App icon file path (optional) | | `windowState` | string | Initial state: `"normal"`, `"maximized"`, `"fullscreen"` (optional) | +| `frameAutosaveName` | string | Remember the desktop window frame under a stable name (optional; empty disables) | | `frameless` | boolean | Remove title bar (optional) | | `level` | string | Window z-order: `"floating"`, `"statusBar"`, `"modal"` (optional) | | `transparent` | boolean | Transparent background (optional) | @@ -62,6 +63,29 @@ See [Multi-Window](multi-window.md#app-window-properties) for full documentation on window properties, including the native primitive each field maps to per platform. +### Remembering the window frame + +Set `frameAutosaveName` to a stable name to reopen an app where the user left it: + +```typescript +{{#include ../../examples/ui/overview/frame-persistence.ts}} +``` + +The saved frame takes precedence over `width`, `height`, and `windowState`. +Missing or unreadable saved settings use those launch defaults. Omit the option +or pass an empty string to disable persistence. Names are scoped to the executable +path, so unrelated apps do not share frames; moving the executable starts fresh. +Use a different name for each independent app window. Changing the title does not +change the saved frame. + +macOS uses AppKit's native frame autosave for position and size; this option does +not promise restoration of a fullscreen Space. Windows, including WinUI, saves +normal placement and maximized/fullscreen state, and reopens minimized windows +in their previous non-minimized state. GTK4 saves normal size and +maximized/fullscreen state; the compositor controls window position because +[GTK4 has no global positioning API](https://docs.gtk.org/gtk4/migrating-3to4.html). +The option is ignored on mobile, TV, watch, and visionOS backends. + ### Lifecycle Hooks ```typescript diff --git a/npm/perry/README.md b/npm/perry/README.md index a683b56d53..46281e7ea1 100644 --- a/npm/perry/README.md +++ b/npm/perry/README.md @@ -60,3 +60,17 @@ perry --help # full CLI reference - Repository: https://github.com/PerryTS/perry - Issues: https://github.com/PerryTS/perry/issues - Changelog: https://github.com/PerryTS/perry/blob/main/CHANGELOG.md + +## Prebuilt runtime profiles + +Native Unix packages include a core runtime alongside the full runtime. When +the source checkout is unavailable, the compiler can select the core archive +for runtime-only programs whose existing feature analysis needs no optional +engines. Programs using regex, Intl/Temporal, dynamic evaluation, native modules, +workers, or FFI retain the existing fallback. A missing core archive also falls +back, so older and partial installations remain usable. + +The core profile includes the Math namespace and preserves the allocator and +unwind policy. It reduces the code +linked into eligible programs; smaller binaries do not by themselves establish +lower startup time or RSS. `--no-auto-optimize` keeps the full-archive path. diff --git a/scripts/build_core_runtime.sh b/scripts/build_core_runtime.sh new file mode 100644 index 0000000000..d3022cf409 --- /dev/null +++ b/scripts/build_core_runtime.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Build the feature-trimmed unwind runtime without changing the ordinary archive. +# Run in the same toolchain/sysroot as the full runtime. Optional second argument +# lets the verification campaign use release instead of the packaging profile. +set -euo pipefail +target="${1:?usage: build_core_runtime.sh TARGET [PROFILE]}" +profile="${2:-dist}" +case "$target" in + *-apple-darwin|*-unknown-linux-gnu|*-unknown-linux-musl) ;; + *) echo "unsupported core runtime target: $target" >&2; exit 2 ;; +esac +ordinary_target_dir="${CARGO_TARGET_DIR:-target}" +core_target_dir="${PERRY_CORE_TARGET_DIR:-${ordinary_target_dir}-core}" +CARGO_TARGET_DIR="$core_target_dir" cargo build --locked --profile "$profile" --target "$target" \ + -p perry-runtime-static --no-default-features --features perry-runtime/prebuilt-core +mkdir -p "$ordinary_target_dir/$target/$profile" +cp "$core_target_dir/$target/$profile/libperry_runtime.a" \ + "$ordinary_target_dir/$target/$profile/libperry_runtime_core.a" diff --git a/scripts/build_linux_glibc_2_31.sh b/scripts/build_linux_glibc_2_31.sh index 9f5334a042..548eaf44f1 100755 --- a/scripts/build_linux_glibc_2_31.sh +++ b/scripts/build_linux_glibc_2_31.sh @@ -68,6 +68,9 @@ CARGO_TARGET_DIR="$abort_target_dir" CARGO_PROFILE_DIST_PANIC=abort \ cp "$abort_target_dir/$target/dist/libperry_runtime.a" \ "$target_dir/$target/dist/libperry_runtime_abort.a" +# Build the optional feature subset inside the same glibc 2.31 sysroot. +bash scripts/build_core_runtime.sh "$target" + # Match the ordinary release leg's best-effort extension-library build. #5716: # enumerate the explicit governance inventory rather than every matching # directory. Keep perry and both wrappers in each invocation so Cargo resolves diff --git a/scripts/build_linux_musl.sh b/scripts/build_linux_musl.sh index c34d1948f4..f17020d113 100755 --- a/scripts/build_linux_musl.sh +++ b/scripts/build_linux_musl.sh @@ -108,6 +108,8 @@ CARGO_TARGET_DIR="$abort_target_dir" CARGO_PROFILE_DIST_PANIC=abort \ cp "$abort_target_dir/$target/dist/libperry_runtime.a" \ "$target_dir/$target/dist/libperry_runtime_abort.a" +bash scripts/build_core_runtime.sh "$target" + # A dynamically linked binary can run on the glibc build host and still fail # immediately for users on Alpine. Gate the artifact itself, not just the # Cargo exit status. diff --git a/scripts/gc_runtime_root_holders.json b/scripts/gc_runtime_root_holders.json index 533ae24984..40b42fb1fa 100644 --- a/scripts/gc_runtime_root_holders.json +++ b/scripts/gc_runtime_root_holders.json @@ -307,7 +307,7 @@ "file": "crates/perry-runtime/src/gc/census.rs", "name": "PASS1_MARKED", "verdict": "non_moving_snapshot", - "why": "Real GC header addresses, deliberately untraced so the diagnostic does not keep its observed objects alive. Populated only at the end of mark propagation of a synchronous full cycle; consumed at sweep entry in the same run_to_completion invocation. The intervening full-cycle phases do not relocate or run JS callbacks. The Vec is used for membership comparisons and dropped with the census before sweep. Budgeted and minor cycles skip both boundaries. Pin re-audited 2026-09-05 after #9760 touched `gc/mod.rs`: that change is `mod heap_stats;` plus a `pub(crate) use` re-export and alters no mark/sweep control flow. `heap_stats()` is reached only from `js_bun_jsc_heap_stats` (the JS-facing `bun:jsc.heapStats()`), i.e. from mutator code, never inside a cycle, and its own module contract forbids allocation or collection during its walk. The mark-complete → sweep-entry window is unchanged. Re-audited 2026-09-05 (train125) after #9769 and #9771 touched pinned files. #9769 adds one `reg_scanner!` registration to `gc/mod.rs`; #9771 adds a feature-gated `alloc_census_init()` there and a feature-gated Rust-heap dump inside `take_census`. `alloc-census` is not in the default feature set, and decisively: `census_take_if_armed_at_full_sweep_start` does `PASS1_MARKED.with(|p| p.borrow_mut().take())` BEFORE calling `take_census`, so the snapshot has already left the thread-local by the time #9771's code runs — it cannot affect the window. Neither change alters mark/sweep control flow. Re-audited 2026-09-06 after #9831 touched `gc/policy.rs`. Its hunks are (a) the tiny-parse pressure guard's pricing (`tiny_parse_pressure_headroom_bytes`, `tiny_parse_pressure_due*`, a `Cell` byte-count base) consulted from JSON.parse's mutator-side boundaries (`gc_bump_malloc_trigger`, `gc_collect_pending_suppressed_parse`, `gc_schedule_parse_boundary_collection_if_pressure`), none of which is reachable from inside a cycle, and (b) one extra `Cell` store in `note_collection_finished_arena_occupancy`, which runs from `publish_reclaim_outcome` in the Publish subphase — after `step_sweep` has already consumed the snapshot. Mark/sweep control flow between `census_pass1_if_armed` and `census_take_if_armed_at_full_sweep_start` is untouched. Re-audited 2026-09-05 (train126) after #9755 restructured `gc/cycle.rs`. Its hunks are all root-scan machinery (`RootScanSubphase`, `RootScanCycleState`, the mutable-scanner iteration state), which runs BEFORE mark propagation completes; `gc/mod.rs` gains only a `mod young_log;` declaration. The bracketing is unchanged — `census_pass1_if_armed` is still inside `step_mark_propagation` and `census_take_if_armed_at_full_sweep_start` inside `step_sweep` — and a synchronous full mark-sweep still moves nothing between them. Re-pinned 2026-09-05 for the #9740 hot-TLS conversion of this file: the sole change is `thread_local!` → `crate::perry_thread_local!`, a macro-name swap with identical declaration syntax and `.with()` call sites. No control flow, no phase boundary, and no storage semantics change. Re-audited 2026-09-06 (train128) after #9794's GC diagnostics touched `gc/mod.rs` and `gc/policy.rs`: both gain diagnostic module declarations and counters only — no mark/sweep control flow, and the census bracketing in `step_mark_propagation` / `step_sweep` is unchanged. Re-audited for #9794's GC diagnostics: `gc/mod.rs` gains `mod diag_sites;` / `mod survival_diag;`, a re-export, a `diag_sites::full_started(...)` call at TRIGGER time (before mark propagation begins), and exit-time reporting. Nothing executes between mark-complete and sweep-entry, so the window is unchanged. Re-audited 2026-09-06 for the retained array-growth verifier fix: the cycle.rs change passes the existing non-copying evacuation verifier an explicit all-forwarded policy. That call remains in minor finalization, outside the synchronous full-cycle census window; its root and heap reads do not allocate GC objects, move objects, or invoke JS callbacks. The mark-complete and sweep-entry boundaries are unchanged. Re-audited 2026-09-05 after #9830 touched `gc/policy.rs`. That change is (a) six `thread_local! {` blocks rewritten as `crate::perry_thread_local! {` and (b) one `#[cfg(test)]` accessor listing the trigger path's hot-slot indices. The macro keeps the same storage, the same `.with()` at every read and write, and the same destructor registration (the teardown guard exists exactly when `needs_drop` holds, which is what `std::thread_local!` already decided); no value, predicate or branch in the file changes, so no mark or sweep control flow does. The one new behaviour is on a declaration's FIRST read: `HotKey::resolve_and_cache` takes a mutex and allocates a key through the GLOBAL allocator. Even if a first read landed inside this window it would be sound — the window's contract is that nothing relocates and no JS callback runs, and a mimalloc allocation does neither. `census_pass1_if_armed` is still inside `step_mark_propagation` and `census_take_if_armed_at_full_sweep_start` inside `step_sweep`; the bracketing is untouched. Re-audited 2026-09-06 (train132) after #9860 and #9845 touched `gc/mod.rs`. Both hunks are re-export lists and nothing else: #9860 adds `idle_reclaim_elapsed_starts` / `IDLE_RECLAIM_REARM_MS`, and #9845 adds `owner_is_dead_copied_minor_from_space_of_type`. No mark or sweep control flow changes. #9845's substantive work sits in `gc/oldgen.rs` and `gc/copying.rs`, neither pinned: the copying-minor arm (`finalize_dead_copied_minor_from_space_regexps`) runs on a MINOR, which skips both census boundaries; the full-cycle arm (`collect_dead_registered_regexps_post_trace`, from `with_dead_collection_finalize`) walks the RegExp registry building a Vec of addresses — no GC allocation, no JS callback, so it cannot relocate the snapshot's subjects — and it is reached from the sweep body, i.e. AFTER `census_take_if_armed_at_full_sweep_start` has already `take()`n the snapshot out of the thread-local. The mark-complete -> sweep-entry window is unchanged. Re-audited 2026-09-07 for #9965 after 1ec9e0e8a touched `gc/cycle.rs` and `gc/mod.rs`: `gc/mod.rs:216-217` only declares and imports the failure-attribution module, while `gc/cycle.rs:1414-1417` reads the trigger and diagnostic counters immediately before evacuation verification inside `atomic_finalize_minor_prelude`. Full cycles bypass `MinorPrelude` at `gc/cycle.rs:1192-1196`; evacuation remains guarded by the minor-only context at `gc/cycle.rs:1330-1372`. The snapshot store remains at `gc/cycle.rs:963-964` after synchronous full marking, and its take remains at `gc/cycle.rs:1454-1457` before sweep. No new write, relocation, collection, or JS callback was added to that full-cycle interval, so the PASS1_MARKED window is unaffected. Re-audited 2026-09-07 for the regex census rows: all new work is in `take_census` after `census_take_if_armed_at_full_sweep_start` has taken PASS1_MARKED out of TLS; neither boundary nor the intervening cycle control flow changed. Re-audited 2026-09-08 (train144) after #9976 and #9977 touched pinned files. `gc/mod.rs` gains exactly three lines: `mod copying_phase;` and `mod regex_census;` (declarations) and one `reg_scanner!(regex::site_test::scan_roots_mut)` registration. A scanner registration adds a root SOURCE for the mutable-root walks; it does not move either census boundary and runs nowhere between them. `gc/census.rs` widens `side_tables()` to `pub(super)`, extends it with regex rows and adds a test module — all census REPORTING, which runs from the diagnostic dump, not inside a cycle. Mark/sweep control flow between `census_pass1_if_armed` and `census_take_if_armed_at_full_sweep_start` is untouched. Re-audited 2026-09-08 for #9849 JSON construction deferral. `gc/mod.rs` adds the `json_defer` module/re-export and a trusted-header layout helper used only by already-validated JSON emitters; neither changes or runs in collector phase control flow. `gc/policy.rs` adds JSON completion scheduling, construction-grace checks, and safepoint deferral predicates. These are called from mutator-side JSON allocation/output boundaries and ordinary safepoint entry; they do not alter `step_mark_propagation`, `step_sweep`, or invoke callbacks or relocation between the census boundaries. The mark-complete to sweep-entry window is unchanged. The follow-up adds a cfg(test)-only one-shot boolean for deterministic explicit-pressure fixtures; it is absent from production builds and cannot affect the census window. The first predicate read consumes it, so post-parse accounting exercises normal pricing. Re-audited 2026-09-09 for bounded tiny-JSON completion polling. The policy.rs changes split the mutator-side pending-parse check into an inlined empty fast path plus an outlined debt-service path, and amortize the mutator-side arena-pressure read across 64 bounded parse completions. Neither function is reachable from step_mark_propagation or step_sweep; neither census boundary nor the synchronous full-cycle interval between them changes. Re-audited 2026-09-09 for lazy JSON record batches: policy.rs only widens gc_budgeted_cycle_active visibility from pub(super) to pub(crate). Its body remains a read-only Cell query. The new caller is lazy_get materialization in the mutator; run_to_completion, step_mark_propagation, census snapshot consumption at step_sweep, and the synchronous non-moving window are unchanged. Re-audited 2026-09-09 for completed JSON-output debt: the added gc_service_json_output_sweep function calls the existing trigger check from a rooted mutator boundary and reports whether its malloc-count request remains due. It is not called from any census or collector phase; the synchronous mark-complete to sweep-entry window is unchanged. Re-audited 2026-09-09 for the JSON byte-debt carry: the same mutator-only service helper now distinguishes requests satisfied before its call from those satisfied by its trigger check. The added enum contains no payload, both count reads are scalar, and no census boundary or collector phase changed. Re-audited 2026-09-11 for #10055: gc/mod.rs only registers the weak UTF-16 index scanner during gc_init. It neither marks strings nor allocates GC objects or runs JS; offset vectors use the Rust allocator. The mark-complete to sweep-entry census window and cycle control flow are unchanged. Re-audited 2026-09-11 for #10054: gc/mod.rs adds only the trim-cache mutable-root scanner registration in gc_init. Its scanner visits two existing string slots without allocating or invoking JS. Root scanning still precedes mark completion, and neither census boundary nor the synchronous mark-complete to sweep-entry window changes. Re-audited 2026-09-11 for #10060: the census array classifier now reads the logical element start and bounds its scan by the remaining capacity. The helper only reads the existing GC/header words and performs pointer arithmetic; it cannot allocate, collect, or call JS. This classifier runs in take_census after PASS1_MARKED has been taken out of TLS. Neither census boundary nor the mark-complete to sweep-entry control flow changed. Re-audited for #8512: gc/mod.rs only enables the existing PTY mutable-root scanner on Windows; it changes no mark/sweep phase or census boundary. The scanner visits NaN-boxed slots without running JS callbacks. Re-audited 2026-09-12 for the single regular-expression engine: `gc/mod.rs` changes `mod prefetch;` to `pub(crate) mod prefetch;` so the RegExp owner-table walks can prefetch headers, a visibility change with no new call in collector control flow; `gc/census.rs` changes only its `#[cfg(test)]` `regex_census_tests` module, dropping assertions for the previous engine's cache rows. Neither boundary (`census_pass1_if_armed` in `step_mark_propagation`, `census_take_if_armed_at_full_sweep_start` in `step_sweep`) nor the synchronous mark-complete to sweep-entry interval changes. Re-audited 2026-09-13 after the #10169 fix touched `gc/mod.rs` and `gc/policy.rs`. `gc/mod.rs` gains only `pub(crate) use` re-exports (`policy::note_young_leaf_born_old`, `policy::young_generation_holds_a_nursery`, `promote_in_place::{young_generation_measured_dying, young_generation_measured_retained}`, and cfg(test) survival seeders). `gc/policy.rs` gains a `Cell` thread-local (`GC_YOUNG_LEAF_BORN_OLD`, no pointer), its setter, a pure predicate over `copying_from_space_in_use_bytes` vs the base nursery cap, and a consumed-once branch at the top of `gc_budgeted_due_trigger` that may answer `YoungScavengeCap` ahead of `OldReclaim`. That branch decides WHICH collection a safepoint starts (a minor instead of a full); it runs before any cycle begins and never inside one, so the mark-complete → sweep-entry window of a synchronous full — where PASS1_MARKED is populated and consumed within one `run_to_completion` — is unchanged, and neither hunk adds an allocation, a JS callback, or a relocation to it. Re-audited 2026-09-13 for the heap generation (#10164 cross-call search positions): `gc/mod.rs` only declares `pub(crate) mod heap_generation;`. `gc/cycle.rs` wraps the `Sweep` and `Reclaim` arms of `GcCycleState::step` in a `HeapChange` scope and opens one inside `atomic_finalize_minor_prelude`'s evacuation branch (with a nested one around old-page defrag). Opening and closing a scope only increments two thread-local integer cells (`HEAP_GENERATION`, `OPEN_HEAP_CHANGES`); a first thread-local read may allocate a key through the global allocator, which neither relocates nor runs JS. The `Sweep` scope opens immediately before `step_sweep`, i.e. before `census_take_if_armed_at_full_sweep_start` takes PASS1_MARKED out of TLS, and adds no relocation, collection or JS callback to the synchronous mark-complete to sweep-entry window; the minor-prelude scope is unreachable from a full cycle, which bypasses `MinorPrelude`. Neither boundary nor the intervening control flow changed. Re-audited 2026-09-13 for #10182 block-granular reclamation, which touched `gc/cycle.rs`. Two hunks: (a) in the `RememberedSetRebuild` subphase of AtomicFinalize — INSIDE the window — the require-marked old-to-young rebuild is now constructed with `OldToYoungRememberedRebuildState::new_skipping`, whose cursor never enters blocks the census recorded as holding no reached, pinned or pre-marked object (`BlockCensus::unmarked_blocks`); computing that list reads `arena_block_snapshots()` and allocates one `Vec` through the global allocator. It visits a subset of the same objects the rebuild already walked (every skipped object would have been rejected as unmarked), and it neither allocates a GC object, relocates anything, nor runs a JS callback. (b) In `step_sweep`, `IncrementalSweepState::with_block_skip` runs after `census_take_if_armed_at_full_sweep_start` has already taken PASS1_MARKED out of TLS. Neither boundary moved and the synchronous mark-complete to sweep-entry interval gains no relocation, collection or callback.", + "why": "Real GC header addresses, deliberately untraced so the diagnostic does not keep its observed objects alive. Populated only at the end of mark propagation of a synchronous full cycle; consumed at sweep entry in the same run_to_completion invocation. The intervening full-cycle phases do not relocate or run JS callbacks. The Vec is used for membership comparisons and dropped with the census before sweep. Budgeted and minor cycles skip both boundaries. Pin re-audited 2026-09-05 after #9760 touched `gc/mod.rs`: that change is `mod heap_stats;` plus a `pub(crate) use` re-export and alters no mark/sweep control flow. `heap_stats()` is reached only from `js_bun_jsc_heap_stats` (the JS-facing `bun:jsc.heapStats()`), i.e. from mutator code, never inside a cycle, and its own module contract forbids allocation or collection during its walk. The mark-complete → sweep-entry window is unchanged. Re-audited 2026-09-05 (train125) after #9769 and #9771 touched pinned files. #9769 adds one `reg_scanner!` registration to `gc/mod.rs`; #9771 adds a feature-gated `alloc_census_init()` there and a feature-gated Rust-heap dump inside `take_census`. `alloc-census` is not in the default feature set, and decisively: `census_take_if_armed_at_full_sweep_start` does `PASS1_MARKED.with(|p| p.borrow_mut().take())` BEFORE calling `take_census`, so the snapshot has already left the thread-local by the time #9771's code runs — it cannot affect the window. Neither change alters mark/sweep control flow. Re-audited 2026-09-06 after #9831 touched `gc/policy.rs`. Its hunks are (a) the tiny-parse pressure guard's pricing (`tiny_parse_pressure_headroom_bytes`, `tiny_parse_pressure_due*`, a `Cell` byte-count base) consulted from JSON.parse's mutator-side boundaries (`gc_bump_malloc_trigger`, `gc_collect_pending_suppressed_parse`, `gc_schedule_parse_boundary_collection_if_pressure`), none of which is reachable from inside a cycle, and (b) one extra `Cell` store in `note_collection_finished_arena_occupancy`, which runs from `publish_reclaim_outcome` in the Publish subphase — after `step_sweep` has already consumed the snapshot. Mark/sweep control flow between `census_pass1_if_armed` and `census_take_if_armed_at_full_sweep_start` is untouched. Re-audited 2026-09-05 (train126) after #9755 restructured `gc/cycle.rs`. Its hunks are all root-scan machinery (`RootScanSubphase`, `RootScanCycleState`, the mutable-scanner iteration state), which runs BEFORE mark propagation completes; `gc/mod.rs` gains only a `mod young_log;` declaration. The bracketing is unchanged — `census_pass1_if_armed` is still inside `step_mark_propagation` and `census_take_if_armed_at_full_sweep_start` inside `step_sweep` — and a synchronous full mark-sweep still moves nothing between them. Re-pinned 2026-09-05 for the #9740 hot-TLS conversion of this file: the sole change is `thread_local!` → `crate::perry_thread_local!`, a macro-name swap with identical declaration syntax and `.with()` call sites. No control flow, no phase boundary, and no storage semantics change. Re-audited 2026-09-06 (train128) after #9794's GC diagnostics touched `gc/mod.rs` and `gc/policy.rs`: both gain diagnostic module declarations and counters only — no mark/sweep control flow, and the census bracketing in `step_mark_propagation` / `step_sweep` is unchanged. Re-audited for #9794's GC diagnostics: `gc/mod.rs` gains `mod diag_sites;` / `mod survival_diag;`, a re-export, a `diag_sites::full_started(...)` call at TRIGGER time (before mark propagation begins), and exit-time reporting. Nothing executes between mark-complete and sweep-entry, so the window is unchanged. Re-audited 2026-09-06 for the retained array-growth verifier fix: the cycle.rs change passes the existing non-copying evacuation verifier an explicit all-forwarded policy. That call remains in minor finalization, outside the synchronous full-cycle census window; its root and heap reads do not allocate GC objects, move objects, or invoke JS callbacks. The mark-complete and sweep-entry boundaries are unchanged. Re-audited 2026-09-05 after #9830 touched `gc/policy.rs`. That change is (a) six `thread_local! {` blocks rewritten as `crate::perry_thread_local! {` and (b) one `#[cfg(test)]` accessor listing the trigger path's hot-slot indices. The macro keeps the same storage, the same `.with()` at every read and write, and the same destructor registration (the teardown guard exists exactly when `needs_drop` holds, which is what `std::thread_local!` already decided); no value, predicate or branch in the file changes, so no mark or sweep control flow does. The one new behaviour is on a declaration's FIRST read: `HotKey::resolve_and_cache` takes a mutex and allocates a key through the GLOBAL allocator. Even if a first read landed inside this window it would be sound — the window's contract is that nothing relocates and no JS callback runs, and a mimalloc allocation does neither. `census_pass1_if_armed` is still inside `step_mark_propagation` and `census_take_if_armed_at_full_sweep_start` inside `step_sweep`; the bracketing is untouched. Re-audited 2026-09-06 (train132) after #9860 and #9845 touched `gc/mod.rs`. Both hunks are re-export lists and nothing else: #9860 adds `idle_reclaim_elapsed_starts` / `IDLE_RECLAIM_REARM_MS`, and #9845 adds `owner_is_dead_copied_minor_from_space_of_type`. No mark or sweep control flow changes. #9845's substantive work sits in `gc/oldgen.rs` and `gc/copying.rs`, neither pinned: the copying-minor arm (`finalize_dead_copied_minor_from_space_regexps`) runs on a MINOR, which skips both census boundaries; the full-cycle arm (`collect_dead_registered_regexps_post_trace`, from `with_dead_collection_finalize`) walks the RegExp registry building a Vec of addresses — no GC allocation, no JS callback, so it cannot relocate the snapshot's subjects — and it is reached from the sweep body, i.e. AFTER `census_take_if_armed_at_full_sweep_start` has already `take()`n the snapshot out of the thread-local. The mark-complete -> sweep-entry window is unchanged. Re-audited 2026-09-07 for #9965 after 1ec9e0e8a touched `gc/cycle.rs` and `gc/mod.rs`: `gc/mod.rs:216-217` only declares and imports the failure-attribution module, while `gc/cycle.rs:1414-1417` reads the trigger and diagnostic counters immediately before evacuation verification inside `atomic_finalize_minor_prelude`. Full cycles bypass `MinorPrelude` at `gc/cycle.rs:1192-1196`; evacuation remains guarded by the minor-only context at `gc/cycle.rs:1330-1372`. The snapshot store remains at `gc/cycle.rs:963-964` after synchronous full marking, and its take remains at `gc/cycle.rs:1454-1457` before sweep. No new write, relocation, collection, or JS callback was added to that full-cycle interval, so the PASS1_MARKED window is unaffected. Re-audited 2026-09-07 for the regex census rows: all new work is in `take_census` after `census_take_if_armed_at_full_sweep_start` has taken PASS1_MARKED out of TLS; neither boundary nor the intervening cycle control flow changed. Re-audited 2026-09-08 (train144) after #9976 and #9977 touched pinned files. `gc/mod.rs` gains exactly three lines: `mod copying_phase;` and `mod regex_census;` (declarations) and one `reg_scanner!(regex::site_test::scan_roots_mut)` registration. A scanner registration adds a root SOURCE for the mutable-root walks; it does not move either census boundary and runs nowhere between them. `gc/census.rs` widens `side_tables()` to `pub(super)`, extends it with regex rows and adds a test module — all census REPORTING, which runs from the diagnostic dump, not inside a cycle. Mark/sweep control flow between `census_pass1_if_armed` and `census_take_if_armed_at_full_sweep_start` is untouched. Re-audited 2026-09-08 for #9849 JSON construction deferral. `gc/mod.rs` adds the `json_defer` module/re-export and a trusted-header layout helper used only by already-validated JSON emitters; neither changes or runs in collector phase control flow. `gc/policy.rs` adds JSON completion scheduling, construction-grace checks, and safepoint deferral predicates. These are called from mutator-side JSON allocation/output boundaries and ordinary safepoint entry; they do not alter `step_mark_propagation`, `step_sweep`, or invoke callbacks or relocation between the census boundaries. The mark-complete to sweep-entry window is unchanged. The follow-up adds a cfg(test)-only one-shot boolean for deterministic explicit-pressure fixtures; it is absent from production builds and cannot affect the census window. The first predicate read consumes it, so post-parse accounting exercises normal pricing. Re-audited 2026-09-09 for bounded tiny-JSON completion polling. The policy.rs changes split the mutator-side pending-parse check into an inlined empty fast path plus an outlined debt-service path, and amortize the mutator-side arena-pressure read across 64 bounded parse completions. Neither function is reachable from step_mark_propagation or step_sweep; neither census boundary nor the synchronous full-cycle interval between them changes. Re-audited 2026-09-09 for lazy JSON record batches: policy.rs only widens gc_budgeted_cycle_active visibility from pub(super) to pub(crate). Its body remains a read-only Cell query. The new caller is lazy_get materialization in the mutator; run_to_completion, step_mark_propagation, census snapshot consumption at step_sweep, and the synchronous non-moving window are unchanged. Re-audited 2026-09-09 for completed JSON-output debt: the added gc_service_json_output_sweep function calls the existing trigger check from a rooted mutator boundary and reports whether its malloc-count request remains due. It is not called from any census or collector phase; the synchronous mark-complete to sweep-entry window is unchanged. Re-audited 2026-09-09 for the JSON byte-debt carry: the same mutator-only service helper now distinguishes requests satisfied before its call from those satisfied by its trigger check. The added enum contains no payload, both count reads are scalar, and no census boundary or collector phase changed. Re-audited 2026-09-11 for #10055: gc/mod.rs only registers the weak UTF-16 index scanner during gc_init. It neither marks strings nor allocates GC objects or runs JS; offset vectors use the Rust allocator. The mark-complete to sweep-entry census window and cycle control flow are unchanged. Re-audited 2026-09-11 for #10054: gc/mod.rs adds only the trim-cache mutable-root scanner registration in gc_init. Its scanner visits two existing string slots without allocating or invoking JS. Root scanning still precedes mark completion, and neither census boundary nor the synchronous mark-complete to sweep-entry window changes. Re-audited 2026-09-11 for #10060: the census array classifier now reads the logical element start and bounds its scan by the remaining capacity. The helper only reads the existing GC/header words and performs pointer arithmetic; it cannot allocate, collect, or call JS. This classifier runs in take_census after PASS1_MARKED has been taken out of TLS. Neither census boundary nor the mark-complete to sweep-entry control flow changed. Re-audited for #8512: gc/mod.rs only enables the existing PTY mutable-root scanner on Windows; it changes no mark/sweep phase or census boundary. The scanner visits NaN-boxed slots without running JS callbacks. Re-audited 2026-09-12 for the single regular-expression engine: `gc/mod.rs` changes `mod prefetch;` to `pub(crate) mod prefetch;` so the RegExp owner-table walks can prefetch headers, a visibility change with no new call in collector control flow; `gc/census.rs` changes only its `#[cfg(test)]` `regex_census_tests` module, dropping assertions for the previous engine's cache rows. Neither boundary (`census_pass1_if_armed` in `step_mark_propagation`, `census_take_if_armed_at_full_sweep_start` in `step_sweep`) nor the synchronous mark-complete to sweep-entry interval changes. Re-audited 2026-09-13 after the #10169 fix touched `gc/mod.rs` and `gc/policy.rs`. `gc/mod.rs` gains only `pub(crate) use` re-exports (`policy::note_young_leaf_born_old`, `policy::young_generation_holds_a_nursery`, `promote_in_place::{young_generation_measured_dying, young_generation_measured_retained}`, and cfg(test) survival seeders). `gc/policy.rs` gains a `Cell` thread-local (`GC_YOUNG_LEAF_BORN_OLD`, no pointer), its setter, a pure predicate over `copying_from_space_in_use_bytes` vs the base nursery cap, and a consumed-once branch at the top of `gc_budgeted_due_trigger` that may answer `YoungScavengeCap` ahead of `OldReclaim`. That branch decides WHICH collection a safepoint starts (a minor instead of a full); it runs before any cycle begins and never inside one, so the mark-complete → sweep-entry window of a synchronous full — where PASS1_MARKED is populated and consumed within one `run_to_completion` — is unchanged, and neither hunk adds an allocation, a JS callback, or a relocation to it. Re-audited 2026-09-13 for the heap generation (#10164 cross-call search positions): `gc/mod.rs` only declares `pub(crate) mod heap_generation;`. `gc/cycle.rs` wraps the `Sweep` and `Reclaim` arms of `GcCycleState::step` in a `HeapChange` scope and opens one inside `atomic_finalize_minor_prelude`'s evacuation branch (with a nested one around old-page defrag). Opening and closing a scope only increments two thread-local integer cells (`HEAP_GENERATION`, `OPEN_HEAP_CHANGES`); a first thread-local read may allocate a key through the global allocator, which neither relocates nor runs JS. The `Sweep` scope opens immediately before `step_sweep`, i.e. before `census_take_if_armed_at_full_sweep_start` takes PASS1_MARKED out of TLS, and adds no relocation, collection or JS callback to the synchronous mark-complete to sweep-entry window; the minor-prelude scope is unreachable from a full cycle, which bypasses `MinorPrelude`. Neither boundary nor the intervening control flow changed. Re-audited 2026-09-13 for #10182 block-granular reclamation, which touched `gc/cycle.rs`. Two hunks: (a) in the `RememberedSetRebuild` subphase of AtomicFinalize — INSIDE the window — the require-marked old-to-young rebuild is now constructed with `OldToYoungRememberedRebuildState::new_skipping`, whose cursor never enters blocks the census recorded as holding no reached, pinned or pre-marked object (`BlockCensus::unmarked_blocks`); computing that list reads `arena_block_snapshots()` and allocates one `Vec` through the global allocator. It visits a subset of the same objects the rebuild already walked (every skipped object would have been rejected as unmarked), and it neither allocates a GC object, relocates anything, nor runs a JS callback. (b) In `step_sweep`, `IncrementalSweepState::with_block_skip` runs after `census_take_if_armed_at_full_sweep_start` has already taken PASS1_MARKED out of TLS. Neither boundary moved and the synchronous mark-complete to sweep-entry interval gains no relocation, collection or callback. Re-audited 2026-09-11 for the startup memory profile: gc/mod.rs only retains the pre-main allocator-policy constructor in js_gc_init. The constructor applies process allocation options, without invoking GC or JS. No census boundary, collector phase, or mark-complete to sweep-entry control flow changed. Re-audited 2026-09-13 for #10179: census.rs only adds a native regex cache metadata row and its unit assertion; snapshot consumption and the full-cycle window are unchanged.", "window": { "start": { "file": "crates/perry-runtime/src/gc/census.rs", @@ -322,9 +322,9 @@ "function": "run_to_completion" }, "sources": { - "crates/perry-runtime/src/gc/census.rs": "3f9e6be47b4454022b70ff2357bbdf3a80ef6a84c4986e58763acbdcce9142c1", + "crates/perry-runtime/src/gc/census.rs": "5c151725460ffb92a55a6bee781123ef5159263b4ce5958d16570f78216e0d67", "crates/perry-runtime/src/gc/cycle.rs": "fdef083301463ad8cec8142ca86cc4354e289c67e97bbc6caea2e8d16d4bf089", - "crates/perry-runtime/src/gc/mod.rs": "90339683735e4d662628cd98279a1972d523c3f2ebc358130ed3bdb0c6fc2d3f", + "crates/perry-runtime/src/gc/mod.rs": "18607409fa1304f578faa223645f69063714e953b81fecbdcb986f64104ee735", "crates/perry-runtime/src/gc/policy.rs": "aea89274a017156efea3516b4f49124f48a66527a08195b662cfbf61672ac042", "crates/perry-runtime/src/gc/progress.rs": "a5ad3971bbe4047229ca57325234780daa85921dbc778e1c08dff4ad07ccfb96" } @@ -480,12 +480,6 @@ "verdict": "not_a_gc_pointer", "why": "Inline-cache miss diagnostics (`PERRY_IC_DIAG`). `IcDiag` is two `Instant`s, three counters, and `sites: HashMap` whose KEY is a PIC cache-slot address — malloc'd arena storage from `field_get_set/ic_slot.rs`, never GC heap — and whose value is a `String` plus counters. Nothing here is a managed pointer." }, - { - "file": "crates/perry-runtime/src/hot_diag.rs", - "name": "REGEX_DIAG", - "verdict": "not_a_gc_pointer", - "why": "RegExp construction/exec diagnostics (`PERRY_REGEX_DIAG`), off unless armed. Every field is a counter or an `Instant` except `per_pattern: HashMap`, whose VALUE is Rust-owned (an owned prefix `String`, a flags `String`, counters). The KEY is a pattern `StringHeader` address, so it is a heap address — but it is used ONLY as an opaque grouping id and is never dereferenced: `PatStat::prefix` and `byte_len` are filled from the `&[u8]` argument at first insert, never by reading the key. Nothing here is traced, rooted or rewritten. The one consequence of the address being reused after a pattern dies is that two patterns' diagnostic counters merge into one row — an inaccuracy in an off-by-default diagnostic, with no collector implication. Distinct from `PASS1_MARKED`, whose addresses ARE walked and which therefore carries `non_moving_snapshot` with a pinned window." - }, { "file": "crates/perry-runtime/src/intl/segments_view.rs", "name": "DECLINE_EMPTY", @@ -2397,6 +2391,18 @@ "name": "WINDOW_ROOTS", "verdict": "not_a_gc_pointer", "why": "Window-root registry maps numeric window handles to numeric root-widget handles; neither value is a JavaScript heap pointer." + }, + { + "file": "crates/perry-runtime/src/promise/microtasks/empty.rs", + "name": "CALLED", + "verdict": "test_only", + "why": "Three cfg(test) callback counters increment integer invocation counts only. They never store managed values or heap addresses." + }, + { + "file": "crates/perry-runtime/src/weakref/test_support.rs", + "name": "DELIVERED", + "verdict": "test_only", + "why": "The finalization-boundary test stores the bits of its fixed numeric held value 41.0 and asserts that value after delivery. It cannot retain a managed pointer and is absent from production." } ], "_FRONTIER_README": "Identity-pinned debt ratchet over new perry-ui* candidates and otherwise-unclassified core raw/Perry TLS declarations (see the census docstring, “The identity-pinned frontier”). A new uncovered holder fails until it is scanned, receives a researched holders verdict, or is deliberately pinned as debt. Moving a researched false positive to holders graduates it from this list. A fixed or classified holder makes its old frontier pin stale, so the receipt must be deleted.", diff --git a/scripts/stage-npm.sh b/scripts/stage-npm.sh index 9ecdedc3da..fc4d032d38 100755 --- a/scripts/stage-npm.sh +++ b/scripts/stage-npm.sh @@ -74,7 +74,7 @@ PLATFORMS=( # Unix libs shared across platforms (runtime + stdlib). The UI lib is # handled per-platform above. Windows equivalents are baked into the case # block further down. -UNIX_CORE_LIBS=(libperry_runtime.a libperry_runtime_abort.a libperry_stdlib.a) +UNIX_CORE_LIBS=(libperry_runtime.a libperry_runtime_abort.a libperry_runtime_core.a libperry_stdlib.a) WIN_CORE_LIBS=(perry_runtime.lib perry_stdlib.lib) # ----------------------------------------------------------------------------- diff --git a/scripts/verify_tiny_output.py b/scripts/verify_tiny_output.py new file mode 100644 index 0000000000..c208ec9219 --- /dev/null +++ b/scripts/verify_tiny_output.py @@ -0,0 +1,95 @@ +"""Exercise the shipped tiny output helper with deterministic syscall faults.""" +import hashlib +import json +import os +from pathlib import Path +import subprocess +import sys +import tempfile + +root = Path(__file__).resolve().parents[1] +helper = root / 'crates/perry/src/commands/compile/tiny_program/output.c' +out = Path(sys.argv[1]).resolve() +out.mkdir(parents=True, exist_ok=True) +work = Path(tempfile.mkdtemp(prefix='perry-tiny-output-faults-')) +main = work / 'main.c' +main.write_text(r''' +#include +struct perry_tiny_output { int fd; const unsigned char *bytes; size_t length; }; +void perry_tiny_run_output(const struct perry_tiny_output *, size_t); +#ifdef INJECT +int perry_test_verdict(void); +#endif +int main(void) { + const struct perry_tiny_output outputs[] = { + {1, (const unsigned char *)"alpha\n", 6}, + {2, (const unsigned char *)"beta\n", 5}, + {1, (const unsigned char *)"gamma\n", 6}, + }; + perry_tiny_run_output(outputs, 3); +#ifdef INJECT + return perry_test_verdict(); +#else + return 0; +#endif +} +''') +faults = work / 'faults.c' +faults.write_text(r''' +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +static int mask, calls, polls; +ssize_t perry_test_write(int fd, const void *bytes, size_t length) { + if (fd == 1 && calls++ == 0) { mask |= 1; errno = EINTR; return -1; } + if (fd == 1 && calls == 2) { mask |= 2; errno = EAGAIN; return -1; } + if (length > 3) { mask |= 4; length = 3; } + return write(fd, bytes, length); +} +int perry_test_poll(struct pollfd *fds, nfds_t count, int timeout) { + if (polls++ == 0) { mask |= 8; errno = EINTR; return -1; } + return poll(fds, count, timeout); +} +int perry_test_verdict(void) { return mask == 15 ? 0 : 91; } +''') +# Include system headers before renaming the calls. glibc's fortified poll +# declaration can otherwise retain an asm("poll") alias and bypass injection. +injected_helper = work / 'injected.c' +injected_helper.write_text('''#define _POSIX_C_SOURCE 200809L +#include +#include +ssize_t perry_test_write(int, const void *, size_t); +int perry_test_poll(struct pollfd *, nfds_t, int); +#define write perry_test_write +#define poll perry_test_poll +#include ''' + json.dumps(str(helper)) + '\n') +receipt = {'source': str(helper), 'sourceSha256': hashlib.sha256(helper.read_bytes()).hexdigest(), + 'verifierSha256': hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), + 'work': str(work), 'commands': [], 'cases': {}, 'complete': False, 'passed': False} + +def run(argv): + result = subprocess.run([str(x) for x in argv], capture_output=True, timeout=30) + receipt['commands'].append({'argv': [str(x) for x in argv], 'exitCode': result.returncode, + 'stdout': result.stdout.decode(), 'stderr': result.stderr.decode()}) + assert result.returncode == 0, receipt['commands'][-1] + return result + +try: + for label, injected in [('normal', False), ('partial-eintr-eagain-poll-eintr', True)]: + obj = work / (label + '.o') + argv = ['cc', '-std=c11', '-O2', '-Wall', '-Wextra', '-Werror', '-c', + injected_helper if injected else helper, '-o', obj] + run(argv) + binary = work / label + argv = ['cc', '-std=c11', '-O2', main, obj, '-o', binary] + if injected: + argv += ['-DINJECT=1', faults] + run(argv) + observed = run([binary]) + assert observed.stdout == b'alpha\ngamma\n' and observed.stderr == b'beta\n' + receipt['cases'][label] = {'passed': True, 'stdout': observed.stdout.decode(), 'stderr': observed.stderr.decode()} + receipt['complete'] = True + receipt['passed'] = True +finally: + (out / 'verification.json').write_text(json.dumps(receipt, indent=2) + '\n') diff --git a/scripts/verify_tiny_program.py b/scripts/verify_tiny_program.py new file mode 100644 index 0000000000..2e829ff37b --- /dev/null +++ b/scripts/verify_tiny_program.py @@ -0,0 +1,237 @@ +"""Verify automatic tiny selection, Node output, symbols, and pipe behavior. + +Run outside a source checkout when testing the installed npm compiler. Optional +--build-env supplies explicit archive paths for a source-built compiler; no flag +or environment variable enables the tiny optimization. Full fallback fixtures +execute by default; --tiny-only is for an early compiler-only smoke test. +""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import re +import selectors +import subprocess +import tempfile +import threading +import time + +parser = argparse.ArgumentParser() +parser.add_argument('compiler') +parser.add_argument('output') +parser.add_argument('--build-env', default='{}') +parser.add_argument('--tiny-only', action='store_true') +args = parser.parse_args() +compiler = str(Path(args.compiler).resolve()) +out = Path(args.output).resolve() +out.mkdir(parents=True, exist_ok=True) +work = Path(tempfile.mkdtemp(prefix='perry-tiny-oracles-')) +(work / 'package.json').write_text('{"type":"module"}\n') +env = {k: v for k, v in os.environ.items() if not k.upper().startswith(('PERRY_', 'MIMALLOC_', 'DYLD_', 'LD_PRELOAD'))} +build_env = {**env, **json.loads(args.build_env), 'PERRY_KEEP_SYMBOLS': '1'} + +def file_record(p): + p = Path(p) + return {'file': str(p), 'bytes': p.stat().st_size, 'sha256': hashlib.sha256(p.read_bytes()).hexdigest()} + +def captured(data): + return {'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest(), + **({'text': data.decode('utf-8', errors='backslashreplace')} if len(data) < 4096 else {})} + +result = {'compiler': file_record(compiler), 'verifier': file_record(__file__), 'work': str(work), + 'buildEnv': json.loads(args.build_env), 'node': subprocess.check_output(['node', '-v'], text=True).strip(), + 'complete': False, 'passed': False, 'scope': 'tiny-only' if args.tiny_only else 'tiny-and-fallback', 'fixtures': {}} +assert result['node'] == 'v26.5.1', 'Use the pinned Node 26.5.1 oracle' + +def save(): + (out / 'verification.json').write_text(json.dumps(result, indent=2) + '\n') + +def run(command, **kwargs): + return subprocess.run(command, env=env, capture_output=True, timeout=45, **kwargs) + +positive = { + 'empty': '// empty\n;', + 'literal': 'console.log("hello");', + 'hello': 'const who: string = "world"; console.log(`hello, ${who}`); console.log("RESULT 1");', + 'stderr': 'console.error("error");', + 'ordering': 'console.log("one"); console.error("two"); console.log("three"); console.error("four");', + 'nul-unicode': r'console.log("a\0b\n🦀\uD800"); console.error("%s %d %% é");', + 'surrogate-join': r'const low = "\uDC00"; console.log(`\uD800${low}`);', + 'empty-string': 'console.log(""); console.error("");', + 'constant-chain': 'const a="a", b=`${a}b`; const c=`${b}${a}`; console.log(c);', + 'assertions': 'const s = "value"; console.log((s as string)!);', + 'escaped-builtin': r'\u0063onsole.\u006cog("escaped");', + 'pipe-survival': 'console.log("first"); console.log("second"); console.error("survived");', + 'stdout-handshake': 'const s="' + 'x' * 131072 + '"; console.log(s); console.error("ready");', + 'stderr-handshake': 'const s="' + 'x' * 131072 + '"; console.error(s); console.log("ready");', + 'backpressure': 'const s = "' + 'x' * 512 + '";\n' + 'console.log(s);\n' * 512, +} +fallback = { + 'sync-allocation-loop': 'let a: any[] = []; for(let i=0;i<100;i++) a.push({i}); console.log(a.length);', + 'numeric': 'console.log(42);', + 'arguments': 'console.log("a", "b");', + 'promise': 'Promise.resolve().then(() => console.log("later")); console.log("first");', + 'timer': 'setTimeout(() => console.log("timer"), 0);', + 'lifecycle': 'process.on("exit", () => console.log("exit")); console.log("body");', + 'computed-console': 'console["log"]("computed");', + 'global-alias': 'globalThis.console.log("global");', + 'dead-code': 'if (false) { new Promise(() => {}); } console.log("kept");', + 'unused-function': 'function unused() {} console.log("kept");', + 'exceptions': 'try { throw new Error("caught"); } catch(e) { console.log(e.message); }', + 'import': 'import { basename } from "node:path"; console.log(basename("/a/b"));', + 'getters': 'const x={get value(){console.log("get");return "value";}}; console.log(x.value);', +} +# Receiver replacement cases are covered by eligibility unit tests; the known +# baseline console-replacement defect is not disguised as a new Node pass. +try: + for name, source in {**positive, **({} if args.tiny_only else fallback)}.items(): + src = work / (name + '.ts') + src.write_text(source + '\n') + binary = work / (name + '.bin') + row = result['fixtures'][name] = {'source': file_record(src), 'expectedProfile': 'tiny' if name in positive else 'normal'} + argv = [compiler, 'compile', str(src), '--no-cache', '--keep-intermediates', '-v', '-o', str(binary)] + build = subprocess.run(argv, cwd=work, env=build_env, capture_output=True, timeout=240) + log = out / (name + '-build.log') + log.write_bytes(build.stdout + build.stderr) + row['build'] = {'argv': argv, 'exitCode': build.returncode, 'log': file_record(log)} + assert build.returncode == 0, f'{name}: compile failed; see {log}' + selected = b'using proven tiny program:' in build.stdout + build.stderr + assert selected == (name in positive), f'{name}: incorrect automatic selection' + row['tinySelected'] = selected + row['binary'] = file_record(binary) + oracle = run(['node', str(src)]) + actual = run([str(binary)]) + row['node'] = {'exitCode': oracle.returncode, 'stdout': captured(oracle.stdout), 'stderr': captured(oracle.stderr)} + row['native'] = {'exitCode': actual.returncode, 'stdout': captured(actual.stdout), 'stderr': captured(actual.stderr)} + assert oracle.returncode == 0, f'{name}: Node oracle did not execute' + assert (actual.returncode, actual.stdout, actual.stderr) == (oracle.returncode, oracle.stdout, oracle.stderr), f'{name}: output mismatch' + if selected: + ir = binary.with_suffix('.tiny.ll') + row['ir'] = file_record(ir) + symbols = run(['nm', str(binary)]) + assert symbols.returncode == 0, f'{name}: nm failed' + symbol_log = out / (name + '-symbols.log') + symbol_log.write_bytes(symbols.stdout + symbols.stderr) + row['symbols'] = file_record(symbol_log) + forbidden = rb'js_gc_|js_promise_|js_array_|js_string_|mimalloc|mi_malloc|perry_runtime|shadow_frame' + assert not re.search(forbidden, symbols.stdout), f'{name}: managed runtime symbols present' + assert b'libperry_' not in build.stdout + build.stderr, f'{name}: runtime archive linked' + row['passed'] = True + save() + print(name, 'passed', flush=True) + + # Machine-readable output keeps the ordinary compiler's success envelope. + # Compare against a real normal build when full archives are available. + result['jsonCli'] = {} + for label in ['tiny'] + ([] if args.tiny_only else ['normal']): + binary = work / ('json-' + label + '.bin') + argv = [compiler, '--format', 'json', 'compile', str(work / 'literal.ts'), + '--no-cache', '-o', str(binary)] + if label == 'normal': + argv.append('--no-auto-optimize') + built = subprocess.run(argv, cwd=work, env=build_env, capture_output=True, timeout=240) + assert built.returncode == 0, f'JSON {label} compile failed: {built.stderr!r}' + response = json.loads(built.stdout) + assert response['success'] is True and response['output'] == str(binary) + assert response['native_modules'] == 1 and response['js_modules'] == 0 + assert {'build_cache', 'codegen_cache', 'link_cache'} <= response.keys() + actual = run([str(binary)]) + assert (actual.returncode, actual.stdout, actual.stderr) == (0, b'hello\n', b'') + result['jsonCli'][label] = {'argv': argv, 'response': response, + 'binary': file_record(binary), 'stderr': captured(built.stderr)} + assert result['jsonCli']['tiny']['response']['runtimeProfile'] == 'tiny' + if not args.tiny_only: + normal = result['jsonCli']['normal']['response'] + tiny = result['jsonCli']['tiny']['response'] + assert normal.keys() <= tiny.keys(), 'Tiny dropped ordinary JSON result fields' + assert normal['link_cache'].keys() == tiny['link_cache'].keys() + save() + + pipe_source = work / 'pipe-survival.ts' + pipe_binary = work / 'pipe-survival.bin' + result['pipeCases'] = {} + # Both descriptors point at the same pipe: verify cross-stream call order. + def merged(command): + return subprocess.run(command, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=30) + before = merged(['node', str(work / 'ordering.ts')]) + after = merged([str(work / 'ordering.bin')]) + assert (after.returncode, after.stdout) == (before.returncode, before.stdout) + result['pipeCases']['merged-order'] = captured(after.stdout) + + def broken(command): + readfd, writefd = os.pipe() + os.close(readfd) + try: + child = subprocess.Popen(command, env=env, stdout=writefd, stderr=subprocess.PIPE) + finally: + os.close(writefd) + _, stderr = child.communicate(timeout=30) + return child.returncode, stderr + before = broken(['node', str(pipe_source)]) + after = broken([str(pipe_binary)]) + assert before == after == (0, b'survived\n'), f'broken pipe: {before!r} / {after!r}' + result['pipeCases']['broken-stdout'] = {'exitCode': after[0], 'stderr': captured(after[1])} + + def full_device(command): + with open('/dev/full', 'wb', buffering=0) as sink: + child = subprocess.run(command, env=env, stdout=sink, stderr=subprocess.PIPE, timeout=30) + return child.returncode, child.stderr + if Path('/dev/full').exists(): + before, after = full_device(['node', str(pipe_source)]), full_device([str(pipe_binary)]) + assert before == after == (0, b'survived\n') + result['pipeCases']['full-device'] = {'exitCode': after[0], 'stderr': captured(after[1])} + + def backpressure(command): + readfd, writefd = os.pipe() + os.set_blocking(writefd, False) + child = subprocess.Popen(command, env=env, stdout=writefd, stderr=subprocess.PIPE) + os.close(writefd) + chunks = [] + def consume(): + time.sleep(.15) # Ensure the inherited nonblocking pipe fills first. + with os.fdopen(readfd, 'rb', buffering=0) as stream: + while chunk := stream.read(4096): + chunks.append(chunk) + time.sleep(.001) + reader = threading.Thread(target=consume, daemon=True) + reader.start() + _, stderr = child.communicate(timeout=30) + reader.join(timeout=30) + assert not reader.is_alive() + return child.returncode, b''.join(chunks), stderr + before = backpressure(['node', str(work / 'backpressure.ts')]) + after = backpressure([str(work / 'backpressure.bin')]) + assert after == before and after[0] == 0 and len(after[1]) == 513 * 512, 'nonblocking output lost bytes' + result['pipeCases']['nonblocking-backpressure'] = {'exitCode': after[0], 'stdout': captured(after[1]), 'stderr': captured(after[2])} + # A consumer waits for the other stream before draining the full one. + # Blocking writes would deadlock this valid interaction; Node sends ready. + for name, waiting_stream in [('stdout-handshake', 'stderr'), ('stderr-handshake', 'stdout')]: + observed = {} + for label, command in [('node', ['node', str(work / (name + '.ts'))]), ('tiny', [str(work / (name + '.bin'))])]: + child = subprocess.Popen(command, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + with selectors.DefaultSelector() as selector: + selector.register(getattr(child, waiting_stream), selectors.EVENT_READ) + ready = bool(selector.select(timeout=3)) + stdout, stderr = child.communicate(timeout=30) + observed[label] = {'readyBeforeDrain': ready, 'exitCode': child.returncode, + 'stdout': captured(stdout), 'stderr': captured(stderr)} + assert observed['node'] == observed['tiny'] and observed['node']['readyBeforeDrain'], f'{name}: independent streams stalled' + result['pipeCases'][name] = observed + + def closed_stdout(command): + child = subprocess.Popen(command, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + preexec_fn=lambda: os.close(1)) + stdout, stderr = child.communicate(timeout=30) + return child.returncode, stdout, stderr + before = closed_stdout(['node', str(pipe_source)]) + after = closed_stdout([str(pipe_binary)]) + assert before == after == (0, b'', b'survived\n'), f'closed stdout: {before!r} / {after!r}' + result['pipeCases']['closed-stdout'] = {'exitCode': after[0], 'stderr': captured(after[2])} + result['complete'] = True + result['passed'] = True +except Exception as error: + result['error'] = str(error) + raise +finally: + save() diff --git a/test-files/cjs_getter_reexport/index.cjs b/test-files/cjs_getter_reexport/index.cjs new file mode 100644 index 0000000000..a20a1d5ee8 --- /dev/null +++ b/test-files/cjs_getter_reexport/index.cjs @@ -0,0 +1,19 @@ +Object.defineProperty(exports, "__esModule", { value: true }); +var _transform = require("./transform.cjs"); +Object.defineProperty(exports, "transform", { + enumerable: true, + get: function () { + return _transform.transform; + } +}); +Object.defineProperty(module.exports, "value", { + enumerable: true, + writable: true, + value: function (value) { return value + 1; } +}); +exports.reads = function () { return _transform.reads(); }; +exports.update = function () { + _transform.update(); + exports.value = function (value) { return value + 11; }; + exports.late = function (value) { return value + 11; }; +}; diff --git a/test-files/cjs_getter_reexport/transform.cjs b/test-files/cjs_getter_reexport/transform.cjs new file mode 100644 index 0000000000..8a19078fc7 --- /dev/null +++ b/test-files/cjs_getter_reexport/transform.cjs @@ -0,0 +1,14 @@ +Object.defineProperty(exports, "__esModule", { value: true }); +var transform = function (value) { return value + 1; }; +var readCount = 0; +Object.defineProperty(exports, "transform", { + enumerable: true, + get: function () { + readCount++; + return transform; + } +}); +exports.reads = function () { return readCount; }; +exports.update = function () { + transform = function (value) { return value + 11; }; +}; diff --git a/test-files/test_cjs_getter_reexport.ts b/test-files/test_cjs_getter_reexport.ts new file mode 100644 index 0000000000..351a6f4d66 --- /dev/null +++ b/test-files/test_cjs_getter_reexport.ts @@ -0,0 +1,14 @@ +// #10153 requires live CJS value reads. The parity runner uses the companion +// test-parity/expected file because Node snapshots CJS named imports. +import { transform, value, late, update, reads } from "./cjs_getter_reexport/index.cjs"; + +console.log(reads()); +console.log(typeof transform); +console.log(transform(1)); +console.log(value(2)); +console.log(typeof late); +update(); +console.log(transform(1)); +console.log(value(2)); +console.log(late(3)); +console.log(reads()); diff --git a/test-files/test_gap_10179_regexp_cache.ts b/test-files/test_gap_10179_regexp_cache.ts new file mode 100644 index 0000000000..faedf9039a --- /dev/null +++ b/test-files/test_gap_10179_regexp_cache.ts @@ -0,0 +1,80 @@ +// Node oracle: node --experimental-strip-types test-files/test_gap_10179_regexp_cache.ts +// Shared immutable programs must not share any observable RegExp state. +const source = "(?a+)(?=b)"; +const first = new RegExp(source, "gd"); +const second = new RegExp(["(?", "a+)(?=b)"].join(""), "dg"); +console.log("display", first.source, first.flags, second.source, second.flags); +console.log("identity", first === second, RegExp(first) === first, new RegExp(first) === first); +console.log("test", first.test("xaab"), first.lastIndex, second.lastIndex); +console.log("exec", JSON.stringify(second.exec("xaab")), first.lastIndex, second.lastIndex); +console.log("reset", first.test("xaab"), first.lastIndex, second.lastIndex); +for (const flags of ["", "i", "g", "y", "gy", "u", "s", "m"]) { + const re = new RegExp("a", flags); + re.lastIndex = 1; + console.log("flags", flags, re.test("bAab"), re.lastIndex, re.test("a"), re.lastIndex); +} +let hot = new RegExp("hot", "g"); +for (let i = 0; i < 1100; i++) { + const cold = new RegExp("cold" + i, "i"); + if (!cold.test("COLD" + i)) throw new Error("cold pattern changed"); + hot = new RegExp("hot", "g"); + if (hot.lastIndex !== 0 || !hot.test("hot")) throw new Error("shared lastIndex"); +} +console.log("churn", hot.source, hot.flags, hot.lastIndex); + +const compiled = new RegExp("before", "g"); +const twin = new RegExp("before", "g"); +compiled.lastIndex = 9; +console.log("compile", compiled.compile("after", "ig") === compiled, compiled.source, compiled.flags, compiled.lastIndex); +console.log("twin", twin.source, twin.flags, twin.lastIndex, twin.test("before")); +try { compiled.compile("(", ""); } catch (error) { console.log("invalid", error instanceof SyntaxError); } +console.log("unchanged", compiled.source, compiled.flags); +compiled.compile(twin); +console.log("copy", compiled.source, compiled.flags, compiled.lastIndex); +Object.defineProperty(compiled, "lastIndex", { writable: false }); +try { compiled.compile("published", "y"); } catch (error) { console.log("readonly", error instanceof TypeError); } +console.log("published", compiled.source, compiled.flags, compiled.lastIndex); + +const stateful = /a/g; +stateful.lastIndex = { valueOf() { console.log("coerce lastIndex"); return 1; } } as any; +console.log("coercion", stateful.test("ba"), stateful.lastIndex); +const overridden = /a/; +Object.defineProperty(overridden, "exec", { get() { console.log("get exec"); return () => null; } }); +console.log("override", overridden.test("a")); +const originalExec = RegExp.prototype.exec; +RegExp.prototype.exec = function (value: string) { + console.log("prototype exec", value); + return originalExec.call(this, value); +}; +console.log("patched", /a/.test("a"), "a".replace(/a/g, "x")); +RegExp.prototype.exec = originalExec; + +const ansi = /\u001b\[[0-9;]*m/g; +console.log("replace", "plain".replace(ansi, ""), ansi.lastIndex); +console.log("replace-match", "\u001b[31mred\u001b[0m".replace(ansi, ""), ansi.lastIndex); +console.log("sticky", "baaa".replace(/a/gy, "x"), "aaab".replace(/a/gy, "x")); +console.log("empty", "🚀a".replace(/(?:)/gu, "-")); +console.log("captures", "ab ab".replace(/(a)(b)/g, "$2$1")); +console.log("remove-empty", "🚀a".replace(/(?:)/gu, "")); +const stickyRemove = /a/y; +stickyRemove.lastIndex = 1; +console.log("remove-sticky", "baa".replace(stickyRemove, ""), stickyRemove.lastIndex); +const originalFlags = Object.getOwnPropertyDescriptor(RegExp.prototype, "flags")!; +Object.defineProperty(RegExp.prototype, "flags", { configurable: true, get() { console.log("get flags"); return "g"; } }); +console.log("flags-hook", "aa".replace(/a/g, "")); +Object.defineProperty(RegExp.prototype, "flags", originalFlags); +const originalReplace = RegExp.prototype[Symbol.replace]; +Object.defineProperty(RegExp.prototype, Symbol.replace, { configurable: true, get() { console.log("get replace"); return originalReplace; } }); +console.log("replace-hook", "aa".replace(/a/g, "")); +Object.defineProperty(RegExp.prototype, Symbol.replace, { configurable: true, writable: true, value: originalReplace }); +const ownReplace = /a/g; +ownReplace[Symbol.replace] = function () { return "own replacement"; }; +console.log("own-replace", "aa".replace(ownReplace, "")); +const changedProto = /a/g; +Object.setPrototypeOf(changedProto, { exec() { console.log("custom proto"); return null; } }); +console.log("custom-proto", RegExp.prototype.test.call(changedProto, "a")); +for (const pattern of ["", "a/b", "\n", "\ud800", "🚀"]) { + const a = new RegExp(pattern); + const b = new RegExp(pattern); + console.log("source", JSON.stringify(a.source), a.flags, a.test(pattern), b.test(pattern)); +} diff --git a/test-files/test_gap_startup_before_exit_microtasks_only.ts b/test-files/test_gap_startup_before_exit_microtasks_only.ts new file mode 100644 index 0000000000..b9eda2dd9d --- /dev/null +++ b/test-files/test_gap_startup_before_exit_microtasks_only.ts @@ -0,0 +1,7 @@ +// Promise/nextTick work alone must not cause beforeExit to repeat forever. +process.on("beforeExit", () => { + console.log("beforeExit"); + process.nextTick(() => console.log("tick")); + Promise.resolve().then(() => console.log("promise")); +}); +process.on("exit", () => console.log("exit")); diff --git a/test-files/test_gap_startup_before_exit_unref.ts b/test-files/test_gap_startup_before_exit_unref.ts new file mode 100644 index 0000000000..be869d310c --- /dev/null +++ b/test-files/test_gap_startup_before_exit_unref.ts @@ -0,0 +1,6 @@ +process.on("beforeExit", () => { + console.log("beforeExit"); + setTimeout(() => console.log("unexpected timeout"), 0).unref(); + setImmediate(() => console.log("unexpected immediate")).unref(); +}); +process.on("exit", () => console.log("exit")); diff --git a/test-files/test_gap_startup_empty_checkpoint_before_exit.ts b/test-files/test_gap_startup_empty_checkpoint_before_exit.ts new file mode 100644 index 0000000000..8e79a52353 --- /dev/null +++ b/test-files/test_gap_startup_empty_checkpoint_before_exit.ts @@ -0,0 +1,14 @@ +// The first checkpoint is empty. beforeExit then queues work for a later one. +let passes = 0; +console.log("sync"); +process.on("beforeExit", () => { + console.log("beforeExit", passes); + if (passes++ === 0) { + process.nextTick(() => console.log("tick")); + Promise.resolve().then(() => { + console.log("promise"); + setImmediate(() => console.log("immediate")); + }); + } +}); +process.on("exit", code => console.log("exit", code)); diff --git a/test-files/test_gap_startup_empty_checkpoint_timing.ts b/test-files/test_gap_startup_empty_checkpoint_timing.ts new file mode 100644 index 0000000000..dc5d571633 --- /dev/null +++ b/test-files/test_gap_startup_empty_checkpoint_timing.ts @@ -0,0 +1,6 @@ +// The runtime unit test covers the first empty checkpoint directly. This +// fixture checks the public perf_hooks surface after the loop has drained. +import { performance } from "node:perf_hooks"; +process.on("beforeExit", () => { + console.log("loop started", performance.nodeTiming.loopStart >= 0); +}); diff --git a/test-files/test_gap_startup_exit_job_boundary.ts b/test-files/test_gap_startup_exit_job_boundary.ts new file mode 100644 index 0000000000..b0167b8981 --- /dev/null +++ b/test-files/test_gap_startup_exit_job_boundary.ts @@ -0,0 +1,6 @@ +process.on("exit", () => { + console.log("exit"); + process.nextTick(() => console.log("unexpected tick")); + setImmediate(() => console.log("unexpected immediate")); + Promise.resolve().then(() => console.log("exit promise")); +}); diff --git a/test-parity/expected/test_cjs_getter_reexport.txt b/test-parity/expected/test_cjs_getter_reexport.txt new file mode 100644 index 0000000000..24e5ae9123 --- /dev/null +++ b/test-parity/expected/test_cjs_getter_reexport.txt @@ -0,0 +1,9 @@ +0 +function +2 +3 +undefined +12 +13 +14 +3 diff --git a/third_party/windows-winui/VENDORED.md b/third_party/windows-winui/VENDORED.md index 1d1414f5da..28f1c8a692 100644 --- a/third_party/windows-winui/VENDORED.md +++ b/third_party/windows-winui/VENDORED.md @@ -13,6 +13,8 @@ bootstrap DLL, its import library, and the XAML resource PRI in Perry's Cargo target directory. Perry also adds an application-exit callback hook so its existing lifecycle ABI can run termination handlers when the WinUI window closes. +Perry also adds a window-created hook and native handle accessor so desktop +frame persistence can restore placement before the window is activated. The upstream MIT and Apache-2.0 license files are preserved in every vendored crate directory. diff --git a/third_party/windows-winui/windows-reactor/src/app.rs b/third_party/windows-winui/windows-reactor/src/app.rs index f6ec23447f..6aa1b1ed4c 100644 --- a/third_party/windows-winui/windows-reactor/src/app.rs +++ b/third_party/windows-winui/windows-reactor/src/app.rs @@ -36,6 +36,7 @@ pub struct App { presenter: PresenterKind, backdrop: Option, on_exit: Option>, + on_window_created: Option>, } impl Default for App { @@ -54,6 +55,7 @@ impl App { presenter: PresenterKind::Default, backdrop: None, on_exit: None, + on_window_created: None, } } @@ -104,6 +106,15 @@ impl App { self } + /// Configure native placement after layout setup and before activation. + pub fn on_window_created( + mut self, + callback: impl FnOnce(&ReactorHost) + Send + 'static, + ) -> Self { + self.on_window_created = Some(Box::new(callback)); + self + } + /// Run with custom WinUI setup; the caller manages windows and content. pub fn run_custom(self, setup: F) -> Result<()> where @@ -156,6 +167,7 @@ impl App { let presenter = self.presenter; let backdrop = self.backdrop; let on_exit = Arc::new(Mutex::new(self.on_exit)); + let on_window_created = Mutex::new(self.on_window_created); let factory = Mutex::new(Some(root_factory)); let result_slot: Arc>> = Arc::new(Mutex::new(Ok(()))); let result_slot_cb = Arc::clone(&result_slot); @@ -164,6 +176,7 @@ impl App { let on_exit = Arc::clone(&on_exit); let inner = || -> Result<()> { let factory = factory.lock().unwrap().take().unwrap(); + let on_window_created = on_window_created.lock().unwrap().take(); let title = title.clone(); let on_launched: Box Result<()>> = Box::new(move || { @@ -185,6 +198,9 @@ impl App { if let Some(bd) = backdrop { host.set_backdrop(bd); } + if let Some(callback) = on_window_created { + callback(&host); + } host.activate()?; // Exit the process on window close. Application.Exit() // fail-fasts due to live COM refs, so terminate directly. diff --git a/third_party/windows-winui/windows-reactor/src/winui/host.rs b/third_party/windows-winui/windows-reactor/src/winui/host.rs index 332540fd5e..a7344b7056 100644 --- a/third_party/windows-winui/windows-reactor/src/winui/host.rs +++ b/third_party/windows-winui/windows-reactor/src/winui/host.rs @@ -339,6 +339,16 @@ impl ReactorHost { &self.window } + /// Native handle for platform services such as window frame persistence. + pub fn window_handle(&self) -> windows_core::Result { + let native = self.window.cast::()?; + let mut hwnd = HWND::default(); + unsafe { + native.get_WindowHandle(&mut hwnd)?; + } + Ok(hwnd as isize) + } + pub fn stats(&self) -> RenderStats { self.render_host.stats() } diff --git a/types/perry/ui/index.d.ts b/types/perry/ui/index.d.ts index dbd30584d6..76d53639a3 100644 --- a/types/perry/ui/index.d.ts +++ b/types/perry/ui/index.d.ts @@ -239,6 +239,16 @@ export function App(config: { * Issue #1280. */ windowState?: "normal" | "maximized" | "fullscreen"; + /** + * Remember the desktop window frame under this stable name. Omit or use + * an empty string to disable. Names are scoped to the executable path; + * use different names for independent windows. Saved geometry takes + * precedence over width/height/windowState; those remain first-run defaults. + * macOS restores position/size via AppKit. Windows (including WinUI) also + * restores maximized/fullscreen state. GTK4 restores size and state; the + * compositor controls position. Ignored on mobile, TV, watch, and visionOS. + */ + frameAutosaveName?: string; /** * Remove the window title bar and frame (borderless window, movable * by background). v0.4.11 launcher-style option.