How we measured
Every number here comes from the same public tool anyone can run at
/benchmark/. Each algorithm is exercised through a full
generate keys β encrypt β decrypt round-trip against the production API. The page
itself takes the median of 9 timed runs per phase and discards one warm-up run
to absorb TLS/connection setup. We then ran that entire 35-algorithm suite
5 times end to end
(163s, 156s, 115s, 166s, 161s wall-clock each) and took the median across runs.
Cross-run stability was high. The expensive clusters varied by well under 2% between runs, so the medians below are trustworthy to about the millisecond for the slow algorithms and to a few milliseconds for the fast ones.
For the distribution work (the next section) we ran a separate, dedicated collection: the same end-to-end operation repeated 60 times for each lightweight algorithm and 25 times for each heavyweight one, gently paced to respect the API. That gives us honest percentiles β with the fair caveat that at these sample sizes p95 is well-supported while p99 sits close to the single worst observation, so we treat p99 as "how bad the worst run in a hundred got," not a stable point.
The tail inverts the ranking
This is the finding no reference microbenchmark can give you, because it only exists once you measure the same operation hundreds of times through a real system. Rank the algorithms by their median and you get the cost story in the sections below. Rank them by their worst-case latency and the order turns over: the cheapest KEMs become the least reliable, and the slowest signers become the most reliable.
Two things happen as you climb from the median into the tail. First, the modest, consistent part: across the lightweight KEMs the 95th percentile sits at a median of 1.33× their own p50, versus 1.20× for the SPHINCS+ engines β the fast ones spread wider even at p95. Second, the dramatic part: a cheap operation is capable of a catastrophic single spike that a heavy one simply cannot produce.
Bar = p95 as a multiple of that algorithm's own median; annotation adds the worst single run. β· = SPHINCS+ signed. The slow signers cluster tight; the fast KEMs fan out.
For anyone deploying this, the tail is the number that matters more than the median. A service with a p99 latency budget is better served by an algorithm whose cost is boring than one whose average is low but whose worst case is a wild card. Post-quantum choice, measured honestly, is as much about variance as about speed β and variance is exactly what a one-number spec sheet throws away.
The network floor hides most of the crypto
The browser-native classical baseline β ECDH P-256 in the Web Crypto API, no server involved β completes in 0.20ms. That is the speed of the math alone. Every other row on this page adds one thing: an API call. And the instant you do, the floor jumps to ~45ms and stays there for every cheap algorithm, whether it is doing X25519, ML-KEM-1024, or an X-Wing hybrid of both.
Total round-trip, median of 5 suites. The first bar is browser-only; the rest each add one API call and converge on the same ~45ms floor.
This is the most important context on the page: for key exchange, post-quantum is not slower than classical in any way a user could feel. ML-KEM-1024's actual lattice operations take tens of microseconds; they are invisible next to the millisecond-scale cost of simply making the request. Anyone who tells you PQC key exchange is "too slow for production" is measuring their network, not the cryptography.
The SPHINCS+ cliff
Sort the whole suite by encrypt time and it splits cleanly into two populations with almost nothing in between: a low band around 18ms, and a high band around 764ms. The dividing line is a single design choice β whether the engine attaches a SPHINCS+-SHA2-256s signature (the FIPS 205 SLH-DSA hash-based scheme, "small" parameter set).
s (small) and f (fast) flavours. The s
variants shrink the signature by building a taller hash tree β which means vastly more hash
evaluations at signing time. SPHINCS+-SHA2-256s trades signing speed for a compact signature
on purpose. Our ~764ms is exactly what that trade-off looks like, and it is fixed
cost: it does not shrink no matter how cheap the surrounding KEM is.
Encrypt phase only. β· = attaches a SPHINCS+-SHA2-256s signature. The gap is the signature, not the KEM.
The clearest proof that this is the signature and not the KEM: Multi-KEM (one KEM), Multi-KEM-Triple (three KEMs), and Quad-Layer (four layers) all land within a few milliseconds of each other on encrypt (781ms, 745ms, 764ms). Adding two extra key encapsulations changed the total by less than the run-to-run noise, because each KEM is ~10ms and the shared SPHINCS+ signature is ~764ms. When one component is dozens of times the others, stacking the cheap ones is free.
f variant, or an ML-DSA / FN-DSA
lattice signature (our FN-DSA-512 signs inside the ~18ms
floor). SPHINCS+-256s earns its place only where signature size and hash-based
conservatism matter more than signing latency β code signing, firmware, long-lived roots.
The KEMs, ranked β and why they land where they do
Strip out the signatures and the pure key-encapsulation mechanisms tell a tidy story that matches the math. Here is every KEM by total round-trip, split into key generation, encrypt and decrypt so you can see where each one spends its time.
Median of 5 suites. Bars scaled to Classic McEliece, the outlier that makes the structure visible.
The two honest outliers
Post-ZK-Homomorphic is the slowest thing in the suite at 3.54s, and it should be. Almost all of that is key generation (2.12s) β building a fully-homomorphic evaluation key is one of the most expensive operations in applied cryptography, orders of magnitude beyond a KEM. This is not a regression; it is the price of computing on encrypted data, and it is in line with what FHE keygen costs everywhere.
Classic McEliece, covered above, is the other: a KEM whose cost lives entirely in keygen. Both outliers share a lesson β amortise the expensive phase. Generate the keypair or evaluation key once, reuse it across many messages, and the eye-watering headline number turns into a rounding error per operation.
Does this match what the field expects?
Every ranking here lines up with the published behaviour of these primitives β which is the point of measuring: to confirm the system behaves like the mathematics says it should.
The one thing a naΓ―ve reading would get wrong is treating the ~764ms engines as "slow post-quantum encryption." They are not slow encryption; they are fast encryption plus a deliberately slow signature. Keeping those two costs separate is the whole point of this analysis.
Full results β all 35 algorithms
Median of 5 full-suite runs; each cell is itself the median of 9 timed operations. Violet rows attach a SPHINCS+ signature. "CV" is the coefficient of variation of the total across runs β a stability check.
| Algorithm | Key Gen | Encrypt | Decrypt | Total | Range | CV |
|---|---|---|---|---|---|---|
| Browser baseline | ||||||
| Classical ECDH P-256 | 0.10ms | 0.10ms | 0.00ms | 0.20ms | 0.10msβ0.60ms | 0% |
| Classical (API) | ||||||
| Classical | 16ms | 16ms | 15ms | 47ms | 28msβ156ms | 26.2% |
| Password-Classical | 58ms | 58ms | 59ms | 179ms | 150msβ920ms | 89.1% |
| NIST KEM | ||||||
| X25519MLKEM768 | 12ms | 12ms | 12ms | 36ms | 28msβ852ms | 24.5% |
| ML-KEM-1024 | 15ms | 15ms | 15ms | 45ms | 28msβ87ms | 21.4% |
| HQC-128 | 16ms | 19ms | 20ms | 56ms | 43msβ752ms | 12.7% |
| FrodoKEM-976 | 27ms | 34ms | 29ms | 88ms | 62msβ1.90s | 126% |
| HQC-192 | 22ms | 29ms | 34ms | 93ms | 68msβ855ms | 16% |
| FrodoKEM-1344 | 32ms | 38ms | 35ms | 106ms | 84msβ156ms | 9.3% |
| HQC-256 | 24ms | 39ms | 50ms | 112ms | 101msβ223ms | 16.1% |
| Classic McEliece | 293ms | 311ms | 171ms | 800ms | 567msβ1.79s | 12.3% |
| FN-DSA sig | ||||||
| FN-DSA Transition-Stack | 26ms | 16ms | 16ms | 59ms | 38msβ105ms | 12.6% |
| FN-DSA ZK-Stack | 23ms | 17ms | 16ms | 59ms | 40msβ101ms | 17% |
| FN-DSA-512-Compact | 24ms | 18ms | 17ms | 60ms | 39msβ79ms | 7% |
| FN-DSA FP-Hardened | 39ms | 17ms | 15ms | 72ms | 54msβ120ms | 9.7% |
| FN-DSA-1024-Security | 47ms | 18ms | 22ms | 88ms | 53msβ134ms | 16.5% |
| FN-DSA Dual-Signature β· | 79ms | 772ms | 17ms | 868ms | 824msβ1.73s | 4.1% |
| Hybrid/multi | ||||||
| Post-Quantum | 14ms | 17ms | 16ms | 47ms | 30msβ785ms | 20.6% |
| Hybrid | 16ms | 22ms | 17ms | 53ms | 32msβ88ms | 18.3% |
| Multi-Algorithm β· | 55ms | 743ms | 20ms | 817ms | 784msβ1.86s | 6.7% |
| Multi-KEM-Triple β· | 54ms | 745ms | 19ms | 818ms | 776msβ1.51s | 2.1% |
| Multi-KEM β· | 54ms | 781ms | 16ms | 854ms | 777msβ1.52s | 3.5% |
| Lattice-Code-Hybrid β· | 140ms | 823ms | 43ms | 999ms | 892msβ4.21s | 14.3% |
| Max-secure | ||||||
| Max-Secure-Pure-PQ | 15ms | 17ms | 18ms | 49ms | 33msβ142ms | 13.7% |
| Max-Secure-Lightweight | 16ms | 16ms | 18ms | 50ms | 34msβ1.90s | 13.4% |
| Max-Secure-Hybrid-Transition | 16ms | 19ms | 18ms | 53ms | 39msβ2.23s | 14.9% |
| Max-Secure-Crypto-Agile | 43ms | 18ms | 19ms | 80ms | 51msβ150ms | 22.2% |
| Max-Secure-Stateless | 59ms | 16ms | 19ms | 93ms | 71msβ2.25s | 19.6% |
| Max-Secure-PQC-ZK β· | 58ms | 747ms | 20ms | 822ms | 772msβ2.72s | 9% |
| Advanced | ||||||
| AI-Synthesized-Crypto-Agile | 16ms | 17ms | 15ms | 52ms | 32msβ2.13s | 32.4% |
| PQ3-Stack | 17ms | 19ms | 18ms | 55ms | 32msβ1.92s | 16.8% |
| Quantum-Resistant-Consensus | 47ms | 21ms | 18ms | 81ms | 59msβ2.67s | 28.4% |
| Entropy-Orchestrated | 39ms | 35ms | 15ms | 90ms | 64msβ2.11s | 10.4% |
| Quantum-Lattice-Fusion | 70ms | 84ms | 44ms | 199ms | 166msβ2.32s | 16% |
| Post-ZK-Homomorphic | 2.12s | 536ms | 901ms | 3.54s | 3.04sβ6.96s | 6.6% |
| PQ Crypta | ||||||
| \u2605 Dual-KEM parallel | 35ms | 52ms | 69ms | 157ms | 113msβ1.95s | 14.7% |
| Maximum | ||||||
| Quad-Layer β· | 58ms | 764ms | 22ms | 875ms | 781msβ2.73s | 4.7% |
/benchmark/
and click Run. Your absolute numbers will differ with your distance to the API, but the
shape β a flat floor for the KEMs, a cliff at SPHINCS+, and two keygen-heavy outliers β
is the invariant. β· marks the 7 engines that sign with
SPHINCS+-SHA2-256s.
