measurements/randomx-arm-opt-20260910/README.md
On this page

ARM64 RandomX JIT optimization, 2026-09-10

Status: the selected optimization is applied locally. ASan/UBSan full/light native regression and Rust integration tests passed; production is unchanged.

The existing algorithm remains RandomX v2.0.1. This experiment changes the ARM64 JIT implementation, using the existing ARMv8-A + crypto build target. It retains the secure JIT and all previously added destruction wiping. The screening harness hashes a strictly dependent 68-byte chain, one worker, with full memory. Its dataset key is test key 000.

Candidate implementation

The combined.patch records the initial experimental candidate against the archived baseline. selected.patch records the final source, including comments and the sanitizer dispatch workaround described below. All earlier native hardening and transparent huge page changes are part of this baseline, so the measured gain is additional to the earlier huge page improvement.

Paired confirmation

The final selected Graviton result is 567.75 → 676.95 hashes/s, +19.23%. This is one strictly dependent chain on one pinned parent-host core, including secure JIT permission transitions. Two 30,000-hash runs per implementation were ordered baseline, selected, selected, baseline:

Implementation First run, H/s Second run, H/s
Baseline 567.772 567.723
Selected 677.148 676.752

All four final digests match. graviton-selected-long.jsonl contains these final-source measurements. An earlier 30,000-hash paired confirmation of the initial combined patch gave 570.47 → 677.51 H/s (+18.76%). A subsequent short selected-source check recorded 654 then 676 H/s against a 569 H/s baseline; that slower sample is retained in graviton-selected-confirm.jsonl. The longer final check was added because this shorter check had unexplained variation.

The gain is additional to the earlier huge-page optimization. These rates were measured with the enclave running, but outside it; a future enclave build requires its own calibration. A practical summary is about a 19% gain, not a guarantee of an exact percentage under every workload or deployment.

The M4 four-pair confirmation was unstable: baseline ranged from 472 to 746 H/s and optimized from 615 to 772 H/s. pmset recorded battery power during this phase. Normal user workloads and power/scheduler behavior were not controlled, and the late slowdown affected both implementations. No Mac percentage gain is claimed; the earlier short runs were near 770 H/s for both. All Mac result digests still match. Both hosts' complete results are retained in *-confirm.jsonl; none of the slow samples was dropped.

Screening

*-screen.jsonl: 5,000 dependent hashes per variant, two repetitions, forward then reverse variant order. On Graviton, averages were 567.8 H/s baseline, 587.7 lane swap, 655.7 dynamic FP loads, 669.7 all FP loads, 574.2 AES-only, and 677.1 combined. Every variant produced the same chain digest.

*-extra.jsonl: 10,000 hashes per variant, two repetitions. This isolates the combined change without AES folding and tries three alternative dataset prefetch hints. Graviton retained about 570 baseline versus 677 combined. L1 keep/stream and L2 keep did not improve on the existing L2 streaming hint. The prefetch variants are recorded but are not applied to repository source.

*-pairs.jsonl: compares the combined candidate with paired SIMD FP loads. On Graviton both alternatives changed speed by less than 0.2%, so neither was applied. These remain recorded experiments, not claimed improvements.

Mac screening has substantial time variation from active laptop workloads. It is not pinned to a physical core and P-core residency is not measured. Small differences in its screening results are not reliable evidence of a win. Graviton tests run on the native parent host, pinned to CPU 13 at nice 10, while the existing 12-core production enclave keeps running. These results are not an inside-enclave measurement or an upper bound on an attacker.

Native regression and sanitizer investigation

The final selected code passes the existing ASan and UBSan native fixture on both ARM hosts in full and light modes. This checks 16 secure-JIT hashes against the interpreter per mode and checks explicit VM register/program/hash erasure. On Linux it additionally checks scratchpad and complete JIT wiping immediately before release, and five injected permission failures must abort before returning a hash. Final evidence is in m4-selected-native/ and graviton-selected-native/, including every build command and exit code. Mac Rust release tests also pass: the light upstream vector, entropy failure and restart/tampering tests (3 tests), and the explicitly enabled full upstream vector (1 test). m4-selected-rust-*.stdout/.stderr retain the results.

The initial Graviton GCC combined-sanitizer run failed while dispatching a member-function pointer during light-mode compilation, before executing the new generated code. An identical build of the unchanged baseline failed the same way. This matches GCC's documented member-pointer array UBSan miscompilation, PR c++/116449. Materializing engine[instr.opcode] in a local member pointer before invoking it fixes both full and light dispatch sites. No sanitizer checks are disabled. Both failing investigative runs are preserved alongside the successful final regression. The release Mac object files are byte-identical with and without the workaround; GCC's release object differs, so final Graviton measurements were repeated against selected as well.

Correctness gates and reproduction

Before timing, bench.cpp checks each variant against upstream v1 and v2 full-memory vectors with both hardware and software AES. It also compares 16 v2 inputs to the unchanged interpreter, then requires every timed dependent chain to have the same final digest. Dataset initialization is outside timing. The dataset is shared across sequentially tested VMs, not rebuilt per variant.

experiment-source.tar.gz contains the complete dirty baseline vendored source snapshot and initial variant sources. extra-source.tar.gz and pairs-source.tar.gz contain additional variants. selected-source.tar.gz contains the final selected native files and build-selected.py. In a fresh experiment directory, extract the first archive, copy bench.cpp, build.py and variants.json beside source, and run python3 build.py. Extract each additional archive there and run its build-extra.py, build-pairs.py, or build-selected.py. The build needs CMake and an ARM64 C/C++ compiler; on Linux the crypto target must be executable by the host. Run ./bench . 10000 2 baseline,selected. Linux measurements additionally use nice -n 10 taskset -c 13.

The result is an implementation optimization, not a cryptographic redesign. Both generators and solvers can use it. Existing work counts are unchanged, but previous wall-clock calibration must not be attributed to this build. No enclave, service, Mullvad device, kernel setting or production deployment was restarted or changed during this work.

Final job audit: no experiment benchmark processes remain on either host. Experiment Docker containers are stopped and preserved. enclave-after.json confirms the original production enclave ID and PCR0 are still running. All evidence files are covered by this directory's SHA256SUMS.