measurements/sha3-chain-20260910/README.md
On this page

Sequential SHA3-256 optimization experiment

Measured 2026-09-10 UTC. Graviton5 reaches 6.813 million sequential hashes/s, versus 4.056 million on Hetzner and 8.072 million on the M4. These are the best variant medians, each from three 10-million-hash samples. Graviton is 1.68x the measured Genoa VM, while the M4 is 1.18x Graviton.

Implementation M4, million H/s Graviton5, million H/s Hetzner, million H/s
Scalar assembly + C wrapper 7.791 5.925 3.594
ARM SHA3 / x86 AVX512 + C wrapper 7.507 6.365 3.096
x86 AVX2 + C wrapper 3.304
x86 AVX512VL + C wrapper 3.632
Fused chain in assembly 7.875 6.808 4.056
Fused, all 24 rounds unrolled 8.072 6.813 4.049

The Graviton SHA3 instructions improve the equivalent wrapped scalar code by about 7%; keeping the chain in registers brings the total improvement to 15%. The x86 fused wrapper improves its best wrapped vector variant by about 12%. Full unrolling has effectively no benefit on Graviton or x86 at this precision. The M4 unrolled variant is the most consistent of its variants (8.065–8.088M); other Mac measurements vary more, so small differences should not be overread. This is a useful Graviton advantage over the measured x86 VM, but not an order-of-magnitude gap and not an advantage over the M4.

All 42 full runs have the same 10-million-step final digest. summary.json retains sample values, medians, means, ranges, and the common digest. Binaries, disassemblies, compiler versions, sources, and raw measurements are retained. Disassembly confirms the ARM crypto instructions and x86 ternary-logic/rotate instructions are present in the intended implementations.

This experiment measures x = SHA3-256(x) starting from 32 zero bytes. Every iteration consumes the preceding iteration's complete 32-byte digest. Each hash contains all 24 Keccak-f[1600] rounds and standard SHA3 domain separation and padding. No independently progressing messages are counted as one chain.

Implementations

All machines use assembly generated from the same OpenSSL 3.5.0 source tag, retained in upstream/ with its Apache 2.0 license. The code calls the primitive directly instead of repeatedly constructing an EVP context. This is a benchmark of internal assembly routines, not an application recommendation to depend on OpenSSL's private ABI.

generate.py retains the upstream round bodies and mechanically produces the fused and unrolled variants. On x86, OpenSSL uses a rearranged internal lane layout; the custom wrapper places the final padding bit in that layout. Only the initial four lanes are returned as the SHA3-256 digest.

Reproduction

Run LC_ALL=C CC=cc python3 generate.py from this directory (or pass its path). Generators are retained so an additional source download is unnecessary.

M4:

clang -O3 -mcpu=apple-m4 -I upstream bench.c keccak-m4.S -o bench-m4
python3 run.py m4 none bench-m4

Hetzner:

gcc -O3 -march=znver4 -Wl,-z,noexecstack bench.c keccak-x86-scalar.S keccak-x86-simd.S keccak-x86-avx2.S keccak-x86-avx512vl.S -o bench-gcc
python3 run.py hetzner 1 bench-gcc

Graviton5: compile in the existing attested-relay-arm-toolchain:20260909 container, with this directory bound at /bench, then execute on the host:

cc -O3 -mcpu=native -I /bench/upstream /bench/bench.c /bench/keccak-graviton5.S -o /bench/bench-gcc
python3 run.py graviton5 13 bench-gcc

The existing Graviton compiler did not recognize -mcpu=neoverse-v3; -mcpu=native compiled successfully. The hashing hot path is explicit assembly. No production code, enclave configuration, or host settings were changed. Named build containers remain stopped, and all experiment files are preserved.

Correctness and measurement limits

Every invocation checks the standard SHA3-256("abc") vector with scalar and accelerated code, then checks that all compiled variants produce an identical 1,000-step chain. The Python runner independently verifies that digest using hashlib.sha3_256. The final measured 10-million-step digests are also compared across implementations and machines. Setup/selftests occur outside the timer.

Final runs use three samples of ten million dependent hashes per variant, alternating variant order. Linux uses one thread pinned to CPU 13 on Graviton and guest vCPU 1 on Hetzner. The Mac is one thread, scheduled by macOS, without hard core affinity. The final measurement window is coordinated with other benchmark agents; ordinary background activity and production workloads remain. Hetzner physical host contention and physical-core exclusivity are unknown. Short measurements do not establish sustained multi-day performance or maxima.

This is ordinary SHA3-256, not RandomX and not an evaluated replacement for its memory-hard delay construction. Optimizing a dependent hash chain does not establish a lower bound for adversaries with different hardware or ASICs.

Sources