Skip to content

perf(object): share ordinary property access and slot transactions - #19

Open
Eric-Song-Nop wants to merge 14 commits into
perf/published-vm-executionfrom
perf/ordinary-property-kernel
Open

perf(object): share ordinary property access and slot transactions#19
Eric-Song-Nop wants to merge 14 commits into
perf/published-vm-executionfrom
perf/ordinary-property-kernel

Conversation

@Eric-Song-Nop

@Eric-Song-Nop Eric-Song-Nop commented Sep 11, 2026

Copy link
Copy Markdown

Ordinary own-data assignment previously scanned the prototype chain, materialized complete descriptors and round-tripped through Define. It now locates once, checks writable and updates the existing slot. The same storage owner supports selective Get, value-only Define, presence/enumerability and on-demand prototype traversal. Stacked on #18 (perf/published-vm-execution, fec7519); implements the ordinary-property part of #16.

Storage, ordinary algorithms and observable dispatch have separate responsibilities. Slot positions remain private to a short state borrow; only the needed value/getter/setter is rooted. Set preserves distinct receivers, Proxy completions and rejection identity, Array length conversion order, Arguments, VarRef/AutoInit and cross-runtime checks. Slot replacement retains new references before publication and rolls them back only on pre-publication failure, also fixing Atom ownership after old-edge cleanup failure.

Array and TypedArray regressions are addressed in this version. Genuine Array own slots and directly encoded dense elements share selective value/getter reads; Array misses and non-immediate dense indices retain the existing fallback. Array writes keep their special semantics. TypedArray bounds checking and byte access reuse one owned buffer token, obtained after value conversion for writes; no token is reused across callbacks. The pure bounds calculation is shared with existing state queries.

Performance uses matched Rust 1.94.1 ordinary release builds on a Ryzen 7 7840HS, CPU 2, with identical flags/features. #18's engine is built at documentation-only commit c1dfce3; the final engine is 1cc51bb. Times are fixed-work whole-process wall time, including startup, compilation, setup and teardown. No builds or tests overlap admitted timing.

Ten rotating-order rounds compare #18, the previous #19 engine (9c91663), and the updated engine:

Case #18 ms Previous #19 ms Updated #19 ms Updated vs #18
prop_read 748.258 648.698 647.334 -13.5%
prop_write 843.843 291.777 291.697 -65.4%
prop_update 870.245 400.179 393.974 -54.7%
array_read 535.363 556.830 438.037 -18.2%
array_length_read 820.210 846.763 583.749 -28.8%
typed_array_read 348.580 372.710 303.577 -12.9%
typed_array_write 311.674 320.219 252.771 -18.9%

The complete 50 microbench + 8 real-program matrix and 38 object diagnostics each use five alternating rounds. The complete tables use the seven ten-round comparisons above for those sensitive cases. No additional over-3% rechecks were needed for the final matrix. Microbench geometric mean time change: -8.6%; real-program change: -12.7%. 8/8 real programs improved. No final matrix or diagnostic case was over 3% slower.

Five alternating rounds of hardware counters support the timing results:

Case Instructions vs #18 User cycles vs #18
array_length_read -25.6% -28.1%
array_read -8.4% -17.7%
typed_array_read -8.8% -12.6%
typed_array_write -7.5% -19.6%
prop_read -7.7% -14.4%
prop_write -61.3% -65.9%
prop_update -50.7% -54.7%

Final-source validation (1cc51bb, Rust 1.88.0):

  • Workspace/all-targets with test262-host and the pinned QuickJS oracle: 3,074 passed, 0 failed, 1 existing ignored. Includes the 14 ordinary-property regressions and new dense/slow Array, hole/prototype/receiver, named/Symbol/large-index, conversion resize/detach and shared-buffer coverage.
  • QuickJS fixture suites 13/13 and C oracles 19/19; Clippy for default, test262-host and profiling configurations passed. Node/WASM playground passed. Formatting, source layout, architecture scan and all 16 architecture unit tests passed.
  • Full Test262: all 102,037 outcomes unchanged (79,982 passes / 80,032 runnable, the same 50 runnable failures). Full TSV and JSONL hashes match frozen receipts after normalizing only the source fingerprint. The strict gate reports fingerprint-related checksum drift; frozen receipts and conformance expectations were not changed. Tested 1cc51bb fingerprint: e4f461c60faf9116313fcbcc6fc062d691f308ad4042e7f98d9a9154d21deea3.
  • Documentation head c52d4dc changes only Markdown; Rust sources are byte-identical to tested 1cc51bb. The source-tree fingerprint includes src/engine/object/README.md, so the documentation head has fingerprint 52ff05edb47af54c0a948544156fd7bb7f5509cb84d01096f9c42edbeac53a87; the executable receipts above intentionally identify the immutable tested engine commit.
  • The earlier full-plan validation and the independently reproduced perf(vm): reuse publication guarantees in execution #18 Rust 1.94.1 debug native-stack issue are recorded in docs/ordinary-property-plan.md; final correctness checks use the repository's Rust 1.88.0 toolchain.

Reproduce object diagnostics with python3 scripts/benchmark/ordinary_workloads.py --output target/ordinary-diagnostics. Use that manifest or docs/reports/data-structure-fixed-final.json with scripts/benchmark/fixed.py, the plain before/after binaries and --cpu 2 --repeat 5 (10 for rechecks). Build with scripts/benchmark/build.py to record compiler, flags, commit and ELF hashes.

Final receipts, raw stdout/stderr, distributions, counters and verification logs are retained locally under target/property-regfix3-*; these are not public downloads. Before ELF SHA-256: 66b89405a2a283816c5840fef3152a443bd96be2f0723a663198a15f32b0a765. After ELF SHA-256: 4221d5aed8ab9df7f5c8a6992acc6562d6760b2dde9e3376d5f23cf0b4f594a3. The inline-only experiment was reverted. Intermediate dense-read versions exposed a named-property regression and were superseded; their timings are not used in these final tables.

Complete 50 + 8 matrix: median [min–max], ms
Case #18 ms This PR ms Change Rounds
empty_loop 502.406 [500.764–504.312] 506.895 [501.313–519.338] +0.9% 5
empty_down_loop 419.087 [415.815–419.857] 421.081 [417.759–422.347] +0.5% 5
prop_read 748.258 [741.550–778.652] 647.334 [640.247–671.020] -13.5% 10
prop_write 843.843 [835.460–860.296] 291.697 [288.678–304.183] -65.4% 10
prop_update 870.245 [863.301–903.509] 393.974 [391.114–397.349] -54.7% 10
prop_create 408.962 [407.461–411.989] 377.962 [370.945–385.903] -7.6% 5
prop_clone 340.157 [337.522–341.834] 322.395 [320.503–324.255] -5.2% 5
prop_delete 407.744 [405.521–408.924] 390.336 [387.917–393.422] -4.3% 5
array_read 535.363 [526.160–648.324] 438.037 [435.427–484.526] -18.2% 10
array_write 489.560 [488.540–497.046] 454.425 [454.024–488.040] -7.2% 5
array_update 499.559 [496.372–508.118] 451.312 [447.844–451.819] -9.7% 5
array_prop_create 611.453 [606.182–617.473] 579.615 [574.382–595.633] -5.2% 5
array_slice 407.663 [403.657–417.738] 375.594 [370.895–390.123] -7.9% 5
array_length_read 820.210 [814.272–855.172] 583.749 [576.050–652.914] -28.8% 10
array_length_decr 598.094 [595.501–622.540] 558.162 [543.190–568.918] -6.7% 5
array_push 675.498 [670.628–701.489] 628.085 [615.887–636.804] -7.0% 5
array_pop 638.804 [632.450–639.975] 567.445 [564.239–578.878] -11.2% 5
typed_array_read 348.580 [341.478–401.785] 303.577 [298.781–309.167] -12.9% 10
typed_array_write 311.674 [305.945–393.110] 252.771 [251.125–269.446] -18.9% 10
arguments_read 356.730 [353.113–365.391] 354.719 [350.656–376.153] -0.6% 5
arguments_strict_read 333.262 [330.532–338.132] 335.186 [327.758–337.504] +0.6% 5
global_read 352.271 [349.643–353.321] 351.719 [350.959–354.401] -0.2% 5
global_write 375.583 [370.340–375.934] 369.073 [366.129–377.750] -1.7% 5
local_destruct 152.735 [150.857–158.109] 135.726 [134.745–138.372] -11.1% 5
global_func_call 709.794 [701.918–729.608] 694.555 [690.559–721.977] -2.1% 5
func_call 672.875 [667.101–682.666] 664.429 [662.273–665.137] -1.3% 5
func_closure_call 775.522 [770.221–779.774] 779.107 [765.093–804.749] +0.5% 5
int_arith 570.941 [567.987–577.142] 573.259 [569.963–584.742] +0.4% 5
float_arith 602.843 [596.944–606.157] 603.462 [594.720–606.697] +0.1% 5
bigint64_arith 460.271 [454.327–470.170] 470.916 [458.815–493.474] +2.3% 5
map_set_string 38.699 [37.831–40.006] 36.855 [36.730–37.591] -4.8% 5
map_set_int 50.960 [50.632–51.935] 49.252 [48.650–49.638] -3.4% 5
map_delete 60.183 [59.715–60.972] 57.882 [57.493–60.089] -3.8% 5
weak_map_set 360.713 [354.055–372.849] 334.050 [330.305–420.837] -7.4% 5
array_for 457.622 [450.861–471.152] 401.152 [394.642–435.158] -12.3% 5
array_for_in 843.188 [826.314–866.890] 790.905 [787.203–801.680] -6.2% 5
array_for_of 654.967 [650.707–665.062] 547.880 [544.744–559.739] -16.3% 5
math_min 790.743 [786.076–816.525] 748.583 [744.555–766.092] -5.3% 5
regexp_ascii 420.005 [417.600–429.590] 411.431 [410.357–422.147] -2.0% 5
regexp_utf16 428.251 [422.706–431.097] 421.987 [416.174–426.184] -1.5% 5
regexp_replace 801.649 [788.485–862.689] 789.754 [777.905–813.060] -1.5% 5
string_length 594.695 [591.361–603.856] 605.354 [596.827–627.788] +1.8% 5
string_build1 295.764 [287.950–341.877] 290.007 [286.942–293.717] -1.9% 5
string_build3 298.293 [296.623–306.697] 298.619 [295.937–308.984] +0.1% 5
string_build_large1 315.879 [313.288–330.326] 313.945 [311.479–319.679] -0.6% 5
string_build_large2 317.897 [313.194–325.466] 315.924 [313.683–362.060] -0.6% 5
int_to_string 281.037 [277.610–289.134] 282.269 [280.617–283.262] +0.4% 5
float_to_string 1537.538 [1535.385–1556.252] 1564.000 [1561.732–1581.290] +1.7% 5
string_to_int 162.293 [158.519–163.350] 160.058 [155.575–161.067] -1.4% 5
string_to_float 139.732 [136.064–145.904] 136.180 [134.180–137.562] -2.5% 5
v8-richards 111.232 [110.159–113.185] 75.598 [75.164–76.554] -32.0% 5
v8-deltablue 168.644 [166.223–170.333] 134.763 [133.355–138.549] -20.1% 5
v8-crypto 2298.372 [2273.651–2341.691] 2042.633 [2029.883–2062.143] -11.1% 5
v8-raytrace 1012.814 [1002.123–1013.397] 928.652 [922.374–936.758] -8.3% 5
v8-earley-boyer 2659.756 [2646.421–2674.981] 2551.708 [2543.191–2565.736] -4.1% 5
v8-regexp 5628.493 [5587.969–5648.373] 5484.431 [5458.627–5512.847] -2.6% 5
v8-splay 3406.722 [3368.120–3441.305] 3136.662 [3121.869–3150.973] -7.9% 5
v8-navier-stokes 1908.416 [1893.673–2488.604] 1691.208 [1678.671–1743.124] -11.4% 5
Complete 38 object diagnostics: median [min–max], ms
Case #18 ms This PR ms Change Rounds
own-write-depth-0 149.808 [148.661–153.591] 65.600 [65.074–67.650] -56.2% 5
own-setup-depth-0 5.449 [5.227–5.656] 5.181 [4.639–5.328] -4.9% 5
own-write-depth-1 155.796 [154.201–160.092] 66.275 [65.784–67.537] -57.5% 5
own-setup-depth-1 5.569 [4.922–5.704] 5.362 [4.988–5.738] -3.7% 5
own-write-depth-8 185.814 [182.730–193.822] 66.851 [65.133–68.530] -64.0% 5
own-setup-depth-8 4.623 [4.535–5.453] 4.674 [4.539–5.241] +1.1% 5
own-write-depth-64 429.666 [427.662–431.198] 65.454 [65.143–67.628] -84.8% 5
own-setup-depth-64 5.520 [5.138–5.913] 5.507 [5.462–5.781] -0.2% 5
own-write-depth-256 1220.011 [1217.383–1303.761] 66.902 [65.828–68.246] -94.5% 5
own-setup-depth-256 5.754 [5.558–5.823] 5.707 [5.617–5.809] -0.8% 5
write-width-4-shape 82.197 [79.299–83.791] 35.124 [35.046–36.749] -57.3% 5
write-width-4-dictionary 80.728 [79.631–81.110] 35.463 [35.053–35.711] -56.1% 5
write-width-32-shape 80.532 [79.498–82.669] 35.921 [35.190–37.597] -55.4% 5
write-width-32-dictionary 81.432 [80.070–82.981] 35.669 [34.880–36.197] -56.2% 5
write-width-256-shape 80.606 [79.490–85.470] 36.524 [35.809–39.700] -54.7% 5
write-width-256-dictionary 80.078 [79.901–83.130] 35.571 [35.448–38.525] -55.6% 5
write-width-2048-shape 84.020 [82.341–86.864] 38.364 [37.217–38.920] -54.3% 5
write-width-2048-dictionary 84.199 [82.534–89.814] 38.385 [37.956–40.687] -54.4% 5
write-value-int 107.140 [105.531–109.002] 52.914 [52.186–53.754] -50.6% 5
write-value-float 107.857 [106.285–109.624] 55.174 [52.288–55.758] -48.8% 5
write-value-string 105.560 [103.553–106.403] 54.240 [52.605–55.798] -48.6% 5
write-value-symbol 123.178 [121.304–123.677] 58.867 [58.203–60.965] -52.2% 5
write-value-object 119.666 [118.064–121.858] 56.759 [56.094–57.680] -52.6% 5
get-data 45.457 [44.602–47.775] 42.727 [41.478–43.590] -6.0% 5
get-inherited 51.874 [51.179–53.584] 43.294 [42.522–45.402] -16.5% 5
get-accessor 77.480 [76.982–80.234] 73.245 [73.039–73.440] -5.5% 5
set-accessor 89.439 [87.619–90.478] 79.640 [78.802–82.770] -11.0% 5
set-receiver 166.828 [162.737–172.400] 116.598 [114.506–119.489] -30.1% 5
define-value 414.284 [412.407–415.518] 306.300 [302.843–332.546] -26.1% 5
has-own 107.882 [105.826–112.852] 104.012 [102.345–105.587] -3.6% 5
enumerable 113.499 [112.866–113.675] 95.247 [94.209–97.456] -16.1% 5
proxy-set 42.181 [41.406–44.338] 33.059 [32.499–33.633] -21.6% 5
missing-get 59.986 [59.021–61.671] 48.347 [46.718–58.165] -19.4% 5
write-same-value-int 82.576 [81.929–85.610] 37.879 [36.856–38.190] -54.1% 5
write-same-value-float 82.496 [81.971–91.411] 37.695 [36.235–38.690] -54.3% 5
write-same-value-string 81.917 [80.424–82.424] 37.581 [36.365–38.291] -54.1% 5
write-same-value-symbol 99.811 [97.632–103.024] 43.623 [42.295–44.139] -56.3% 5
write-same-value-object 96.848 [94.583–98.192] 42.214 [42.057–44.598] -56.4% 5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant