§ 01 · native hashing baseline
Per-core scalar hashing: M5 Max vs M4 Max
Single-threaded software hashing (no crypto-extension instructions), the reference for Flock's single-threaded prover. Per-core, the two chips are a wash — the M5 Max's advantage for this prover is core count and bandwidth, not single-core speed.
§ 02 · keccak-f[1600] throughput
Thread scaling at the 2¹⁴ keccak point
Flock's keccak MT optimum is 2¹⁴ (KECCAK3_KS=24576). The auto-default pins to 6 = perflevel0 and leaves the chip under-used; throughput peaks at 12–18 threads. 12 is the reproducible sweet spot — 18 only edges ahead on best-case runs.
data
| threads | prove_fast | throughput | speedup |
|---|---|---|---|
| 1 (ST) | 698 ms | 35.2 K/s | 1.0× |
| 6 (default) | 146.5 ms | 167.8 K/s | 4.8× |
| 8 | 123.1 ms | 199.6 K/s | 5.7× |
| 12 | 99.5 ms | 247.0 K/s | 7.0× |
| 18 | 95.6 ms | 257.1 K/s | 7.3× |
§ 03 / 04 · throughput vs batch size
Per-hash proving throughput (12 threads)
Throughput climbs with batch size for every hash. The proving-cost order is BLAKE3 (cheapest) → SHA-256 → keccak (heaviest), matching their R1CS weight. BLAKE3 is still rising at 2¹⁸; keccak shown at its single 2¹⁴ profile point.
data — thousands/sec
| size | BLAKE3 | SHA-256 | keccak |
|---|---|---|---|
| 2¹⁰ | 177.5 | 135.8 | — |
| 2¹² | 317.3 | 221.3 | — |
| 2¹⁴ | 522.9 | 291.2 | 246.5 |
| 2¹⁶ | 645.8 | 333.9 | — |
| 2¹⁸ | 697.7 | — | — |
§ 06 · per-phase prover breakdown
Where prove time goes (2¹⁴, 12 threads)
zerocheck dominates every hash, with the recursive pcs::open the next-largest. Open is also the phase that scales worst with threads — it regresses past 12, which is exactly why 12 threads beats 18 in typical runs.
§ 07 · parallel scaling (BLAKE3)
Single-thread vs multi-thread, and the speedup
One core saturates at ~94–97 K/s from 2¹⁴ up. 18 threads only overtake 12 from 2¹⁶ onward — below that, too little work to feed all cores. Peak is 815.8 K/s at 2²⁰ (9.0× single-core); note the consistent dip at 2¹⁹ (both thread counts), a tiling/load-imbalance artifact.
data — full grid, both speedups (K comp/s)
| size | ST (1t) | MT (12t) | 12t spd | MT (18t) | 18t spd |
|---|---|---|---|---|---|
| 2¹⁰ | 53.0 | 181.0 | 3.4× | 140.9 | 2.7× |
| 2¹¹ | 71.3 | 237.8 | 3.3× | 137.9 | 1.9× |
| 2¹² | 83.3 | 334.0 | 4.0× | 229.2 | 2.8× |
| 2¹³ | 89.8 | 422.7 | 4.7× | 327.4 | 3.6× |
| 2¹⁴ | 93.9 | 520.3 | 5.5× | 476.8 | 5.1× |
| 2¹⁵ | 95.8 | 586.5 | 6.1× | 568.0 | 5.9× |
| 2¹⁶ | 96.6 | 652.8 | 6.8× | 700.9 | 7.3× |
| 2¹⁷ | 95.8 | 655.4 | 6.8× | 706.6 | 7.4× |
| 2¹⁸ | 94.3 | 694.9 | 7.4× | 796.8 | 8.5× |
| 2¹⁹ | 92.7 | 672.7 | 7.3× | 755.0 | 8.1× |
| 2²⁰ | 90.7 | 690.0 | 7.6× | 815.8 | 9.0× |
| 2²¹ | 88.2 | 632.9 | 7.2× | 695.8 | 7.9× |
Single clean run, all 12 sizes × 3 thread counts. Speedups vs single-thread. 18t overtakes 12t only from 2¹⁶ up; peak 815.8 K/s (9.0×) at 2²⁰. Note the consistent 2¹⁹ dip at both thread counts — see below.
Why 2¹⁹ dips. At both 12t and 18t, 2¹⁹ falls below 2¹⁸ and 2²⁰ — a reproducible kink, not noise. 2¹⁹ = 524,288 work units doesn't divide evenly across 12 (43,690.67) or 18 (29,127.1) cores, so the parallel phases end with a lopsided tail where a few cores finish late and the rest idle (load imbalance). The even powers 2¹⁸/2²⁰ land on clean boundaries the prover splits evenly, so they skip the remainder tax. Cache/TLB stride effects at ~15 GB likely compound it. Same root cause as §08's under-utilization, sharper here.
§ 05 · keccak 2¹⁴ profile — fast vs slim
The rate-1/4 proof-size tradeoff
At matched 12 threads, the slim prover costs ~31% more prove time (130 vs 99 ms) to cut the proof ~48% smaller (200 vs 386 KiB). Verify (~5 ms) and the 128 MB committed size are unchanged. Bars show prove time; proof size labeled on each.
data
| config | throughput | prove | verify | proof | peak |
|---|---|---|---|---|---|
| Fast (12t) | 247.0 K/s | 99.5 ms | 5.3 ms | 386.0 KiB | 1.72 GB |
| Fast (18t) | 257.1 K/s | 95.6 ms | 5.6 ms | 386.0 KiB | 1.72 GB |
| Slim (12t) | 188.6 K/s | 130.3 ms | 5.2 ms | 199.6 KiB | 2.00 GB |
A third variant, Flock-secure (~120-bit), proves at essentially the same speed as fast (249 vs 246 K/s) for a larger proof (558 KiB) — see the cross-prover section below for how all three stack up against other systems.
§ 02–05 · cross-prover comparison
Flock vs Binius64 vs Hashcaster vs Plonky3 (12 threads, 2¹⁴)
At matched threads and comparable security, Flock proves keccak ~5.9× faster than Binius64, ~6.2× faster than Hashcaster, and ~42× faster than Plonky3, and ~11–12× faster than the others on BLAKE3 (Hashcaster has no SHA-256/BLAKE3 circuit).
Throughput isn't the only gap — Flock's verification is far cheaper too (keccak 2¹⁴, log scale):
data — keccak 2¹⁴, 12 threads
| system | sec | throughput | prove | verify | proof | peak |
|---|---|---|---|---|---|---|
| Flock-fast | ~100 | 246.5 K/s | 100 ms | 5.3 ms | 386.0 KiB | 1.72 GB |
| Flock-secure | ~120 | 249.5 K/s | 99 ms | 5.7 ms | 557.8 KiB | 1.72 GB |
| Flock-slim | ~100 | 190.8 K/s | 129 ms | 5.2 ms | 199.6 KiB | 2.00 GB |
| Binius64 | ~96 | 42.0 K/s | 390 ms | 281 ms | 457.4 KiB | 2.49 GB |
| Hashcaster | 100 | 39.5 K/s | 623 ms | 31.5 ms | 664.2 KiB | 0.37 GB |
| Plonky3 | ~101 | 5.9 K/s | 3.68 s | 18.7 ms | 3.35 MiB | 10.61 GB |
Caveats: security bits differ slightly (Binius64 ~96, Plonky3 ~101, Flock & Hashcaster ~100); throughput = hashes / prove, end-to-end incl. trace/witness gen; these are independent compressions, not a chain. Hashcaster is the lightest on memory (367 MB) but has the largest non-Plonky3 proof (664 KiB). Plonky3's proofs are far larger (3.35 MiB keccak, 9.48 MiB BLAKE3) and it peaks at 10.6 GB on keccak.
§ 08 · why it caps at 816K — and the path to 1M
The bottleneck, and what would break past it
§07 shows BLAKE3 peaking at 816K compressions/sec (2²⁰, 18 threads) and declining beyond. This section is the diagnosis: why it caps there, and whether 1M is reachable. The headline — it's not memory bandwidth. The witness at 2²⁰ is 2 GB; commit and zerocheck move only ~10 GB/s against the M5 Max's 614 GB/s ceiling, roughly 2% of available bandwidth, so the bus is nearly idle. The real limit is idle cores: across the full prove, user/real time holds at ~12.5× (three runs), so only ~12 of 18 cores are busy. Zerocheck and commit — 77% of prove time — are under-parallelized and leave ~6 cores unused.
per-phase breakdown — 2²⁰, 18 threads
| phase | time | share |
|---|---|---|
| zerocheck::prove_packed | 553 ms | 42% |
| pcs::commit | 455 ms | 35% |
| pcs::open (ligerito) | 195 ms | 15% |
| lincheck::prove | 101 ms | 8% |
| gen_witness + lincheck | 70 ms | 5% |
Recursion / partitioning does not help. Four independent 2¹⁸ proofs run concurrently (4 threads each) finished in 7.7 s wall — ~136K/s effective, ~6× slower than the single 2²⁰ run. Splitting adds contention without filling the idle cores, since the cores sit idle inside each proof's serial sections, not for lack of work. (Memory verified clean: swap never engaged, the prover got the full 128 GB.) Conclusion — 1M is reachable on this chip, in software. It's neither bandwidth- nor compute-bound but parallelism-bound: better parallelization of the commit (NTT/Merkle) and zerocheck (sumcheck) phases to keep all 18 cores busy clears the 816K→1M gap — no recursion, bigger batches, or new hardware needed. The fix belongs in the Flock prover's inner loops, made and correctness-tested in-tree. Footnotes: isolated single-phase benches show even lower utilization (commit ~4.8×, zerocheck ~2.3×) but include serial setup, so the trustworthy figure is the in-context ~12.5×; and 18 cores beat 12 here (~18% at 2²⁰), unlike small keccak where the PCS open over-threads past 12 — scaling is size-dependent.