A real workload: an FHE bootstrap
A recorded OpenFHE bootstrap replayed stage by stage: utilisation, roofline, hot-spots and what binds.
Data table
| trace · stage | FPGA-only (ms) | NTTs on module (ms) | NTTs split (ms) | NTT share | binds (FPGA-only) | binds (module) |
|---|---|---|---|---|---|---|
| sparse16 · ModRaise | 19.81 | 946.5 | 479 | 0.426 | fpga | module |
| sparse16 · CoeffToSlot | 8.024 | 183.5 | 94.99 | 0.199 | hbm | module |
| sparse16 · EvalMod | 56.97 | 3,331 | 1,679 | 0.525 | fpga | module |
| sparse16 · SlotToCoeff | 2.141 | 26.97 | 14.45 | 0.106 | hbm | module |
| sparse16 · post-processing | 0.3633 | 18.99 | 9.592 | 0.47 | fpga | module |
| full14 · ModRaise | 0.176 | 16.23 | 9.287 | 0.848 | fpga | module |
| full14 · CoeffToSlot | 29.75 | 328.4 | 199.3 | 0.0923 | hbm | module |
| full14 · EvalMod | 49.66 | 2,570 | 1,480 | 0.469 | fpga | module |
| full14 · SlotToCoeff | 15.92 | 142.5 | 87.76 | 0.0737 | hbm | module |
What this stage does
Fully homomorphic encryption lets a server compute on encrypted data. Its most expensive step is the bootstrap, which refreshes a ciphertext’s noise budget. A CKKS bootstrap runs four stages: ModRaise, CoeffToSlot (homomorphic linear transforms built from rotations and key switching), EvalMod (a polynomial approximation of the modular reduction) and SlotToCoeff. Every stage is full of number-theoretic transforms over many residue limbs, which is why a photonic NTT engine is interesting here.
This chapter replays two real bootstraps recorded from instrumented OpenFHE: sparse16 (N = 65,536, 1,732 kernels) and full14 (N = 16,384, 3,917 kernels), on an FPGA card with HBM, a PCIe host link and the module behind one QSFP56 port.
How it’s modelled
Each kernel of the trace becomes a demand on six resources: HBM bytes (limbs, evaluation keys and plaintexts unless cached in on-chip SRAM), FPGA add-equivalents, optical symbol slots on the module, bits through the port in each direction, and PCIe for the ciphertext. Kernels run in recorded order (OpenFHE ran single-threaded); a kernel takes its largest demand over capacity plus a latency. A SimPy replay then cuts kernels into bursts that contend for the same servers and SRAM buffers, which gives the utilisation over time shown above.
The capacities: HBM at 819 GB/s datasheet with 0.8 illustrative achievable, one port at 25 GB/s datasheet, 64 illustrative butterfly units at 400 MHz illustrative, and the module at 14 GBd paper on 1 paper wavelength. The replay’s FPGA-only time is within a fraction of the analytic bound (ratio 1.011 for sparse16).
Data table
| stage | FPGA only | NTTs on the module | NTTs split |
|---|---|---|---|
| ModRaise | 19.81 | 946.5 | 479 |
| CoeffToSlot | 8.024 | 183.5 | 94.99 |
| EvalMod | 56.97 | 3,331 | 1,679 |
| SlotToCoeff | 2.141 | 26.97 | 14.45 |
| post-processing | 0.3633 | 18.99 | 9.592 |
What we found
FPGA-only, memory binds the key-switching stages. A bootstrap takes 87.44 ms (sparse16) and 95.59 ms (full14) on 64 butterfly units. CoeffToSlot and SlotToCoeff stream evaluation keys from HBM and are HBM-bound (CoeffToSlot is HBM-bound for 70.6 % of its time insparse16), while EvalMod and ModRaise are dominated by NTTs and bound by FPGA compute. Over the whole bootstrap HBM binds for 42.4 % of the time in sparse16 and 61 % in full14.
With the NTTs on the module, the module binds every stage. At the paper’s operating point the bootstrap is 51.55× slower for sparse16 (4.507 s) and 31.98× slower forfull14. The module is busy 93.7 % of the time and the return port 50.9 %: a w-bit butterfly costs w symbol slots, and one wavelength at this baud delivers far fewer butterflies than the FPGA’s units. Splitting the NTT stages between FPGA and module (hybrid) halves the damage but does not remove it.
The ceiling is low anyway. NTTs are 46.2 % of the FPGA-only time in sparse16 and 28.6 % in full14, so even infinitely fast NTTs would give at most 1.858× and 1.402×. More FPGA logic makes the bootstrap HBM-bound (from 87.44 ms at the default down to 52.97 ms at the largest swept FPGA, where memory has the last word); more HBM bandwidth alone gets sparse16to 61.05 ms at the top of the HBM sweep. Keeping up with the fastest swept module (112 GBd × 8 λ) would take 34.76 QSFP56 ports’ worth of bandwidth.
Roofs: 7.168 × 10¹¹ add-equivalents/s of FPGA compute and 655.2 GB/s of achievable HBM bandwidth.
Data table
| trace · placement · stage | intensity (ops/B) | achieved (ops/s) | binding |
|---|---|---|---|
| sparse16 · FPGA only · ModRaise | 1.5 | 5.279 × 10¹¹ | fpga |
| sparse16 · FPGA only · CoeffToSlot | 0.6551 | 3.393 × 10¹¹ | hbm |
| sparse16 · FPGA only · EvalMod | 1.353 | 5.084 × 10¹¹ | fpga |
| sparse16 · FPGA only · SlotToCoeff | 0.3888 | 2.302 × 10¹¹ | hbm |
| sparse16 · FPGA only · post-processing | 1.301 | 4.95 × 10¹¹ | fpga |
| sparse16 · NTTs on the module · ModRaise | 3.174 | 2.251 × 10¹⁰ | module |
| sparse16 · NTTs on the module · CoeffToSlot | 1.192 | 2.578 × 10¹⁰ | module |
| sparse16 · NTTs on the module · EvalMod | 3.286 | 2.018 × 10¹⁰ | module |
| sparse16 · NTTs on the module · SlotToCoeff | 0.6396 | 2.86 × 10¹⁰ | module |
| sparse16 · NTTs on the module · post-processing | 3.007 | 2.099 × 10¹⁰ | module |
| sparse16 · NTTs split · ModRaise | 2.337 | 3.275 × 10¹⁰ | module |
| sparse16 · NTTs split · CoeffToSlot | 0.9237 | 3.863 × 10¹⁰ | module |
| sparse16 · NTTs split · EvalMod | 2.32 | 2.825 × 10¹⁰ | module |
| sparse16 · NTTs split · SlotToCoeff | 0.5142 | 4.305 × 10¹⁰ | module |
| sparse16 · NTTs split · post-processing | 2.154 | 2.975 × 10¹⁰ | module |
| full14 · FPGA only · ModRaise | 2.18 | 6.154 × 10¹¹ | fpga |
| full14 · FPGA only · CoeffToSlot | 0.4647 | 2.656 × 10¹¹ | hbm |
| full14 · FPGA only · EvalMod | 1.35 | 5.08 × 10¹¹ | fpga |
| full14 · FPGA only · SlotToCoeff | 0.3857 | 2.309 × 10¹¹ | hbm |
| full14 · NTTs on the module · ModRaise | 6.211 | 1.81 × 10¹⁰ | module |
| full14 · NTTs on the module · CoeffToSlot | 0.6893 | 3.383 × 10¹⁰ | module |
| full14 · NTTs on the module · EvalMod | 3.056 | 2.108 × 10¹⁰ | module |
| full14 · NTTs on the module · SlotToCoeff | 0.5562 | 3.493 × 10¹⁰ | module |
| full14 · NTTs split · ModRaise | 4.483 | 2.282 × 10¹⁰ | module |
| full14 · NTTs split · CoeffToSlot | 0.593 | 4.809 × 10¹⁰ | module |
| full14 · NTTs split · EvalMod | 2.325 | 2.784 × 10¹⁰ | module |
| full14 · NTTs split · SlotToCoeff | 0.4831 | 4.946 × 10¹⁰ | module |
| kernel | count | time (ms) | stall (ms) |
|---|---|---|---|
| NTT | 456 | 33.64 | 249.9 |
| EW | 148 | 16.78 | 160.2 |
| BCONV | 176 | 10.18 | 74.8 |
| INTT | 456 | 7.57 | 46.66 |
| KS | 52 | 6.998 | 64.7 |
| RESCALE | 278 | 6.16 | 66.58 |
| kernel | count | time (ms) | stall (ms) |
|---|---|---|---|
| NTT | 456 | 3,849 | 27,410 |
| INTT | 456 | 865.6 | 5,127 |
| EW | 148 | 16.78 | 160.2 |
| BCONV | 176 | 10.18 | 74.8 |
| KS | 52 | 6.998 | 64.7 |
| RESCALE | 278 | 6.16 | 66.58 |
sparse16 trace ranked by time, with the queueing they accumulate (stall), FPGA-only and with NTTs on the module.Data table
| FPGA butterfly units | FPGA-only | HBM bound only (819 GB/s) |
|---|---|---|
| 8 | 484.7 | 52.33 |
| 16 | 245 | 52.33 |
| 32 | 137.8 | 52.33 |
| 64 | 87.44 | 52.33 |
| 128 | 62.46 | 52.33 |
| 256 | 52.97 | 52.33 |
| 512 | 52.97 | 52.33 |
| trace · stage | binds now | scale HBM down to flip | scale HBM up to flip |
|---|---|---|---|
| sparse16 · CoeffToSlot | hbm | — | 4× |
| sparse16 · EvalMod | fpga | 0.5× | — |
| sparse16 · ModRaise | fpga | 0.5× | — |
| sparse16 · post-processing | fpga | 0.5× | — |
| sparse16 · SlotToCoeff | hbm | — | 4× |
| full14 · CoeffToSlot | hbm | — | 4× |
| full14 · EvalMod | fpga | 0.5× | — |
| full14 · ModRaise | fpga | 0.25× | — |
| full14 · SlotToCoeff | hbm | — | 8× |
Data table
| HBM scale | sparse16 (ms) | sparse16 HBM-bound share | full14 (ms) | full14 HBM-bound share |
|---|---|---|---|---|
| 0.25× | 210 | 1 | 280.5 | 0.967 |
| 0.5× | 124.3 | 0.596 | 153.3 | 0.758 |
| 1× | 87.44 | 0.424 | 95.59 | 0.61 |
| 2× | 69.21 | 0.262 | 67.13 | 0.427 |
| 4× | 61.74 | 0.0803 | 54.36 | 0.2 |
| 8× | 61.15 | 0.00363 | 53.21 | 0.00672 |
| 16× | 61.05 | 0.00198 | 53.05 | 0.00385 |
What would need measured data
The trace is real: kernel kinds, counts and order come from OpenFHE. The costs are not. The FPGA’s logic budget and cost per butterfly, the HBM efficiency and latency, the SRAM caching policy, and the module’s framing and port behaviour are illustrative or datasheet values. To trust the absolute times one would need a real FPGA implementation of the bootstrap kernels (resources, clock and power), measured HBM traffic per kernel, and the module’s sustained throughput and latency behind a real port, including its framing and retries. The direction of the result is robust to all of these: the module would have to get orders of magnitude faster, and its port with it, before NTT placement matters more than HBM.