Architecting the Slotted Pipeline: A Turning Point in Groth16 Proof Generation

Introduction

In the course of optimizing the SUPRASEAL_C2 Groth16 proof generation pipeline for Filecoin's Proof-of-Replication (PoRep), there comes a moment when the architecture itself is questioned. Message [msg 1563] is precisely such a moment. It is not a commit, a code edit, or a benchmark run. It is a piece of analytical reasoning that reframes the entire proving pipeline from a batch-oriented model to a continuous, slotted pipeline — a shift that would ultimately reduce peak memory by 2.5× and improve single-proof latency by 1.7×.

This message, written by the AI assistant in response to the user's observation about finer-grained pipelining, is the hinge point between Phase 5 (Pre-Compiled Constraint Evaluator) and Phase 6 (slotted pipeline) of the optimization roadmap. It synthesizes timing data from previous benchmarks, identifies the critical bottleneck that batch processing imposes, and articulates the mathematical case for a fundamentally different architecture. To understand this message is to understand how a performance optimization effort transitions from tuning individual components to redesigning the system's core dataflow.

The Context: What Led to This Message

The conversation leading up to [msg 1563] had been a deep-dive investigation into the SUPRASEAL_C2 proof generation pipeline. The project had already achieved significant wins: the Pre-Compiled Constraint Evaluator (PCE) eliminated redundant constraint re-synthesis, reducing synthesis time from 50.4s to 35.5s. The daemon architecture allowed proof-level pipelining, where one proof's GPU work overlapped with the next proof's synthesis, achieving 96% GPU utilization in steady state.

But two problems remained. First, single-proof latency was still 69.5s — the sum of synthesis (35.5s) and GPU proving (34.0s) — because there was no overlap when only one proof was in flight. Second, peak memory was ~136 GiB for a single sector, because all 10 partition circuits were synthesized in parallel before any GPU work began. These were not independent problems; they were symptoms of the same architectural choice: batch everything, then prove everything.

The user's question at [msg 1562] — "On the slotted pipeline, the idea is two-fold — more fine-tunable memory requirements, and also more full GPU utilization" — was the catalyst. The assistant's response at [msg 1563] takes this intuition and grounds it in data.

Laying Out the Math

The message opens with a direct acknowledgment: "You're right, and the data makes this very compelling. Let me lay out the math." This framing is significant. The assistant is not speculating; it is building a quantitative case from previously measured timings.

The first diagram shows the current situation for a single proof with 10 circuits:

CPU synth:  |=== 35.5s (PCE) or 50.4s (old) ===|
GPU:                                              |=== 34.0s ===|
                                                                 ^ proof done at 69.5s

This is a sequential pipeline: CPU synthesis completes entirely before GPU proving begins. The GPU sits idle for 35.5 seconds while the CPU works, then the CPU sits idle for 34.0 seconds while the GPU works. Total wall time: 69.5 seconds. Memory peaks at ~136 GiB during synthesis because all 10 circuits' intermediate data is held simultaneously.

The second diagram shows the proof-level pipeline that Phase 2 had already implemented:

Proof 1 synth: |=== 35.5s ===|
Proof 1 GPU:                   |=== 34.0s ===|
Proof 2 synth:                 |=== 35.5s ===|
Proof 2 GPU:                                  |=== 34.0s ===|

Here, Proof 2's synthesis overlaps Proof 1's GPU work. Throughput drops to ~35.5s per proof, and GPU utilization reaches 96%. This is excellent for throughput when multiple proofs are queued. But it does nothing for single-proof latency (still 69.5s) and does nothing for peak memory (still 136 GiB per proof).

The assistant then states the problem bluntly: "That only works when you have multiple proofs queued. If you have a single proof, you still wait 35.5s + 34.0s = 69.5s latency. And more importantly: you're holding all 10 circuits in RAM simultaneously during synthesis (~136 GiB working set)."

The Key Insight: GPU Per-Circuit Cost

The message then pivots to the critical insight that enables the slotted pipeline:

"The key insight from the data: GPU cost is ~3.4s/circuit with near-zero fixed overhead. So sending smaller batches doesn't waste GPU time."

This is the linchpin of the entire Phase 6 design. The GPU's proving cost per circuit is approximately 3.4 seconds, and crucially, there is negligible fixed overhead per batch. This means the GPU can process circuits incrementally — one at a time, or in small groups — without incurring a penalty. The GPU does not need all 10 circuits at once to be efficient.

Why is this true? The Groth16 proving pipeline on GPU consists of several phases: multi-scalar multiplication (MSM), number-theoretic transform (NTT), and various field operations. The data from previous benchmarks (detailed in cuzk-project.md and the task results visible in [msg 1561]) showed that the GPU's per-circuit cost scales linearly with circuit count, with essentially zero fixed overhead. This is characteristic of GPU workloads where kernel launch overhead is negligible compared to the computation itself, and where memory transfer bandwidth is the primary bottleneck rather than batch setup costs.

This insight is non-obvious. In many GPU workloads, batching is essential for efficiency because kernel launch overhead, memory allocation, and data transfer setup dominate for small batches. The fact that the cuzk GPU backend has near-zero fixed overhead is a property of its implementation — likely due to CUDA graph capture, persistent kernels, or carefully managed memory pools — and it is this property that makes the slotted pipeline viable.

The Problem Statement Reframed

With the GPU cost structure established, the message implicitly reframes the problem. The current architecture makes two assumptions:

  1. All circuits must be synthesized before GPU work begins (because GPU proving is a batch operation).
  2. All circuits must be held in memory simultaneously (because synthesis and GPU are sequential phases). Both assumptions are baked into the gpu_prove() interface, which takes all circuits at once. But if the GPU can process circuits incrementally at 3.4s each, then: - Synthesis can produce circuits one at a time (or in small groups). - The GPU can consume them as they become ready. - Memory is bounded by the number of circuits in flight, not the total number of circuits. This is the classic producer-consumer pipeline, applied at partition granularity instead of proof granularity. The message does not yet specify the exact slot size (that would come in the Phase 6 design document), but it establishes the mathematical foundation: with 10 partitions at 3.4s each, a slot size of 2 circuits yields a 6.8s GPU window, which can be overlapped with synthesis of the next slot.## Assumptions and Their Validity The reasoning in [msg 1563] rests on several assumptions that deserve scrutiny. Assumption 1: GPU per-circuit cost is stable at small batch sizes. The message states that GPU cost is "~3.4s/circuit with near-zero fixed overhead." This is derived from benchmarks showing that a 10-circuit batch takes ~34s total. The implicit assumption is that a 1-circuit batch would take ~3.4s, not, say, 10s due to kernel launch overhead, memory allocation, or data transfer setup. This assumption is critical because the slotted pipeline sends circuits to the GPU in smaller groups. If the fixed overhead is not truly near-zero, the GPU utilization would drop and the pipeline would stall. The evidence for this assumption comes from the task result at [msg 1561], which analyzed the GPU interface and found that the proving backend uses a persistent CUDA context with pre-allocated memory pools. Kernel launch overhead is amortized across the batch internally, and the per-circuit work is dominated by MSM and NTT operations that scale linearly with circuit size. The assumption is well-supported, but it is not proven for single-circuit batches — the smallest batch tested was likely 10 circuits. This is a risk, but a manageable one: if single-circuit batches are inefficient, the slot size can be increased to 2 or 3 circuits to amortize overhead. Assumption 2: Synthesis parallelism is already saturated with 10 circuits. The message notes that "synthesis parallelism already saturates CPU with 10 circuits — we don't need all 10 at once." This assumes that the CPU's parallel synthesis capacity is fully utilized at 10 concurrent circuits, and that reducing the number of concurrent circuits would not reduce synthesis throughput. This is supported by earlier profiling (see [msg 1544]) showing that synthesis is CPU-bound with ~142 cores used. However, if the slotted pipeline reduces concurrent circuits to, say, 2, the CPU might be underutilized for synthesis, potentially increasing per-circuit synthesis time. The Phase 6 design would need to balance slot size against CPU utilization. Assumption 3: The GPU proving phase has no dependency on the synthesis phase beyond circuit data. This is inherent in the Groth16 protocol: synthesis produces the proving assignment (witnesses and input values), and GPU proving consumes this assignment to generate the proof. There is no feedback loop or ordering constraint within a single proof. This assumption is sound.

The Thinking Process Visible in the Message

The message reveals a structured analytical process. It begins with visual modeling — the ASCII pipeline diagrams that make temporal relationships explicit. These diagrams are not decorative; they are reasoning tools. The first diagram shows the problem (sequential phases, idle GPU). The second diagram shows the proof-level solution and its limitations. The implied third diagram — the slotted pipeline — is not drawn but is the logical conclusion.

The message then performs a cost structure analysis. It isolates the GPU per-circuit cost (3.4s) and the fixed overhead (near-zero). This decomposition is essential: it reveals that the GPU is not a batch-oriented resource but a continuous-processing resource. The batch orientation was an artifact of the software architecture, not a hardware constraint.

Finally, the message engages in constraint relaxation. It identifies two implicit constraints — "all circuits must be synthesized before GPU work begins" and "all circuits must be held in memory simultaneously" — and shows that neither is actually required by the hardware or protocol. This is the essence of architectural innovation: recognizing that a constraint is imposed by the implementation, not by the problem.

The Broader Significance

Message [msg 1563] is significant not just for its content but for its role in the optimization journey. The project had spent Phase 4 optimizing synthesis hotpaths (Boolean::add_to_lc, Vec recycling pools, async deallocation) and Phase 5 implementing the PCE to eliminate redundant constraint evaluation. These were important optimizations, but they operated within the existing batch-oriented architecture. The slotted pipeline represents a phase transition — a change to the architecture itself.

This is a common pattern in performance optimization: you tune individual components until you hit a wall, then you question the architecture. The wall here was peak memory (136 GiB per proof) and single-proof latency (69.5s). The PCE had already halved synthesis time, but memory remained high because all circuits were synthesized in parallel. The slotted pipeline attacks both problems simultaneously by changing the dataflow.

The message also demonstrates a key skill in systems optimization: knowing which numbers matter. The assistant could have focused on many metrics — total GPU time, memory bandwidth, CPU utilization — but it zeroed in on the GPU per-circuit cost and the fixed overhead. These two numbers determine whether the slotted pipeline is viable. Everything else follows from them.

Conclusion

Message [msg 1563] is a turning point in the SUPRASEAL_C2 optimization effort. It takes the user's intuition about finer-grained pipelining and grounds it in quantitative analysis, identifying the GPU per-circuit cost of 3.4s with near-zero fixed overhead as the enabling property for a slotted architecture. The message reframes the problem from "how do we make batch proving faster" to "how do we make proving continuous," setting the stage for Phase 6's slotted pipeline design that would achieve 2.5× memory reduction and 1.7× single-proof latency improvement.

What makes this message exemplary is not the depth of its analysis — though the analysis is sound — but its timing and framing. It arrives at the moment when the team has enough data to see the limitations of the current architecture and enough context to envision a better one. It is a message that says: we have been optimizing the wrong thing. Let us optimize the architecture instead.