The PceCache Parameter: A Single Edit That Reshaped the cuzk Synthesis Pipeline

Message: "I need to update synthesize_auto to accept an optional &PceCache parameter:" followed by an edit to /tmp/czk/extern/cuzk/cuzk-core/src/pipeline.rs.

At first glance, message <msg id=2135> appears deceptively simple. It contains just one line of reasoning and one successful edit notification. Yet this single message sits at the architectural fulcrum of a sweeping refactor: the replacement of global static PCE (Pre-Compiled Constraint Evaluator) caches with a unified, budget-aware memory management system for the cuzk GPU proving engine. The edit it describes — adding an optional &PceCache parameter to synthesize_auto — is the keystone that locks together all the preceding changes and enables the cascade of call-site updates that follow.

Context: Why This Message Exists

To understand why this message was written, one must trace the arc of the larger refactor. The cuzk proving engine had long relied on four static OnceLock<PreCompiledCircuit<Fr>> globals in pipeline.rs — one for each proof type (PoRep, WinningPoSt, WindowPoSt, and SnapDeals). These statics were populated once at startup via preload_pce_from_disk() and then never evicted. This design had two critical flaws: it consumed GPU memory permanently regardless of actual workload, and it provided no mechanism to release memory under pressure. As proof sizes grew (particularly 32 GiB PoRep proofs), the system became brittle, requiring manual tuning of concurrency limits to avoid out-of-memory crashes.

The specification document cuzk-memory-manager.md (authored in the previous segment, <msg id=...>) proposed a comprehensive solution: replace the static caches with a PceCache struct that integrates with a unified MemoryBudget system, supporting LRU eviction and budget-aware admission control. By message <msg id=2135>, the assistant had already laid most of the groundwork:

The Decision: Why Optional?

The assistant's choice to make the parameter Option<&PceCache> rather than &PceCache is a deliberate design decision with several motivations.

First, it preserves backward compatibility. The synthesis pipeline has multiple internal callers — some from the bench tool, some from the engine's hot path, some from test code. Not all callers have access to a PceCache instance at the point of invocation. By making the parameter optional, the assistant ensures that callers can pass None to fall back to the old synthesis path (which performs full constraint generation without a cached PCE). This is particularly important during the transition period, while the engine.rs integration is still in progress.

Second, it enables incremental integration. As the assistant notes in subsequent messages (<msg id=2141>), the plan is to initially pass None at all 9 call sites as a "safe temporary measure," then later thread the real PceCache through once engine.rs is fully updated. This staged approach reduces risk: each edit can be verified independently, and the system remains compilable and functional at every intermediate step.

Third, it reflects a pragmatic understanding of the system's runtime behavior. The PCE cache may not be available in all contexts — for example, during the very first proof of a given type, when the PCE hasn't been extracted yet. An optional parameter cleanly models this uncertainty at the type level, forcing callers to handle the absence case explicitly rather than relying on a global that might not be populated.

Assumptions Embedded in This Message

The message makes several implicit assumptions that are worth examining.

Assumption 1: The PceCache struct exists and is importable. At this point in the conversation, the assistant has already defined PceCache in pipeline.rs (in <msg id=2108>), so this is a safe assumption. But it's notable that the assistant doesn't verify this with a read or grep — it proceeds directly to the edit, confident in the structural changes already made.

Assumption 2: All callers of synthesize_auto can be updated consistently. The assistant assumes that the 9 call sites (later confirmed via rg in <msg id=2138>) all follow a uniform pattern and can be updated with a mechanical transformation. This turns out to be correct, but it's an assumption that could have been violated if any caller used a different invocation style (e.g., named arguments or method chaining).

Assumption 3: Passing None is a safe default. The assistant assumes that when synthesize_auto receives None for the PCE cache, it will correctly fall back to the non-PCE synthesis path. This requires that the function body has been updated to check Option and branch accordingly. The assistant doesn't show the function body in this message — it's relying on the edit being correct.

Assumption 4: The edit will compile. The assistant doesn't run a build check after this edit. This is reasonable in context (the edit is syntactically straightforward — adding a parameter to a function signature), but it's still an assumption that the broader type system is consistent.

Input Knowledge Required

To fully understand this message, one needs:

  1. The architecture of the cuzk proving pipeline: Understanding that synthesize_auto is the central synthesis function that converts circuit descriptions into GPU-ready constraint systems, and that it previously accessed PCEs through global statics.
  2. The PCE caching mechanism: Knowing that Pre-Compiled Constraint Evaluators are circuit structure snapshots that allow the prover to skip re-synthesis for repeated circuit types, and that they were previously stored in OnceLock globals.
  3. The PceCache struct design: Understanding that PceCache is a new struct (defined earlier in the same session) that wraps a HashMap<CircuitId, PreCompiledCircuit<Fr>> with eviction support and budget integration.
  4. The Rust type system: Knowing that Option<&T> is a borrowed reference to an optional value, and that adding such a parameter to a function signature requires updating all call sites.
  5. The conversation history: Recognizing that this message is part of a larger refactor (Step 5 of the memory manager implementation), and that the preceding edits have already removed the statics that synthesize_auto previously depended on.

Output Knowledge Created

This message produces several forms of knowledge:

  1. A modified function signature: synthesize_auto now accepts Option<&PceCache> as a third parameter, enabling it to use cached PCEs when available.
  2. A pattern for threading the cache: The optional parameter pattern establishes a convention for how the PCE cache flows through the synthesis pipeline — explicitly passed rather than globally accessed.
  3. A dependency relationship: The message establishes that synthesize_auto is the integration point between the PCE cache and the synthesis pipeline. All synthesis paths must go through this function, making it the natural choke point for cache access.
  4. A staging plan: The message implicitly creates a two-phase integration plan — first update the signature and pass None everywhere, then later thread the real cache. This plan is executed in the immediately following messages (<msg id=2136><msg id=2145>), where the assistant updates all 9 call sites.

The Thinking Process

The assistant's reasoning in this message is compressed but visible. The phrase "I need to update synthesize_auto to accept an optional &PceCache parameter" reveals several layers of analysis:

Broader Significance

This message, for all its brevity, represents the moment when the new memory-aware architecture becomes real. Before this edit, the PCE cache existed as a struct definition and a set of extraction functions, but it wasn't connected to the synthesis pipeline — the system's central nervous system. After this edit, the cache is threaded into the synthesis path, and the remaining work (updating call sites, wiring the budget in engine.rs) is mechanical rather than architectural.

The optional parameter also embodies a key engineering principle: when replacing a global with an explicit dependency, make the transition gradual. The Option type provides a bridge between the old world (where the cache was always available via a global) and the new world (where it must be explicitly provided). This pattern — introducing an optional parameter, then progressively eliminating the None cases as integration deepens — is a textbook example of incremental refactoring in a complex system.

In the messages that follow (<msg id=2136><msg id=2145>), the assistant methodically updates all 9 call sites, first manually and then with a sed command, confirming each change with rg. The engine.rs integration (starting at <msg id=2146>) then wires the real PceCache into the pipeline, completing the transformation. But it all starts here, with this one-line message and its single, decisive edit.