Where does grammar-constrained decoding's runtime overhead actually come from?
answer
- two costs: compile once, mask every step
- the index is over the tokenizer's whole vocabulary
- cache the automaton by schema and tokenizer hash
- per-sequence masks limit batch amortisation
- shorter output and no retries offset the overhead
basics
~20 sTwo places: compiling the schema into an automaton plus its token index, which happens once and should be cached, and computing or looking up a token mask at every generation step for every sequence in the batch.
solid answer
~50 sSplit it into compile-time and per-step cost. **Compile time** turns the schema into an automaton and an index mapping each state to its allowed token set; for a large record — say a 200-key extraction schema — this is real work, it depends only on the schema and tokenizer, and so it should be cached by a hash of both. Pay it once, not per request. **Per-step cost** is applying a mask over a vocabulary of a hundred thousand-plus tokens before each sample. A naive implementation that re-derives legality from the partial text every step is what makes constrained decoding feel slow; mature implementations precompute masks per automaton state and apply them as bit operations, which brings the overhead close to negligible. Batching is where it bites: every sequence sits in its own automaton state, so masks are per-sequence rather than shared. Against that, constrained output is shorter and deterministic spans can be fast-forwarded, so end-to-end throughput sometimes improves.
go deeper
Recall the two-part shape: some cost to turn the schema into a state machine, then a small cost to filter the allowed tokens at each step. The first is cached; the second is usually minor.
Explain what the token index is — a map from automaton state to legal vocabulary tokens — why it depends on the tokenizer, and why incremental state tracking beats re-parsing the partial output each step.
Diagnose the real production problem: distinct-schema churn causing compile cache misses and p99 latency spikes, per-sequence masking limiting batch amortisation, and the retry tail that constrained decoding removes from the baseline.
Own the platform decision — one canonical schema set with a warm compile cache versus per-team schema freedom, and whether serving small constrained models or large unconstrained ones wins on cost per valid record at your volume.
## Two costs, not one People asking "is constrained decoding slow?" usually have one number in mind. There are two, with different shapes. **Compile cost** is one-off per schema: parse the schema or grammar, build the recogniser, and build the index that maps each automaton state to the set of vocabulary tokens legal from it. That index is the expensive artefact, because it is a function of the *tokenizer*, not just the grammar — every one of ~100k–260k tokens must be classified against each reachable state, or classified lazily and memoised. **Per-step cost** is paid on every generated token of every request: determine the current state, obtain its allowed-token set, and apply the mask to the logit vector before sampling. ## Why compile cost must be cached Compilation depends only on (schema, tokenizer) — never on the prompt, the user, or the sampled text. That makes it trivially cacheable, and caching it is the single biggest practical difference between a fast and a slow deployment. Keyed by a hash of the schema plus the tokenizer identity, a warm cache reduces the cost to a lookup. What goes wrong in production is **cache-miss storms**: a service that generates schemas dynamically — per tenant, per document type, with a field list assembled at runtime — produces a fresh cache key on nearly every call and pays compilation each time. That shows up as inflated first-token latency and a p99 far worse than the median. The fixes are to stabilise the schema set (a fixed record for all tenants, with tenant-specific rules moved into the validator), and to pre-warm the cache for known schemas at startup. Size matters here too. A 200-key schema with nested objects has many more reachable states than a five-field flat record, and permissive regex patterns can blow the automaton up. Both compile time and index memory grow with grammar complexity, which is another reason flat, tight schemas are the production shape. ## Why per-step cost is usually small — and when it is not The naive implementation is the one to avoid: re-parse the partial output, work out what is legal, and scan the whole vocabulary, every step. That is genuinely expensive relative to a forward pass on a small model. Mature implementations do three things instead. They keep the automaton state incrementally, advancing it with each accepted token rather than re-deriving it. They precompute and cache masks per state, so a step is a lookup plus a bitwise apply. And they exploit the fact that most steps have large, stable allowed sets, storing masks compactly and applying them on the accelerator alongside the rest of the sampling pipeline. With those, reported overhead on structured workloads is a small single-digit percentage or less, and the mask work overlaps with the forward pass. Where it still hurts: - **Batching.** Each sequence in a batch occupies a different automaton state, so you cannot share one mask across the batch — masking becomes a per-sequence operation, and the more heterogeneous the schemas in flight, the less amortisation you get. - **Small models.** The mask cost is roughly independent of model size, so as a fraction of step time it matters far more for a small extraction model than for a frontier one. - **Pathological grammars.** Unanchored or heavily alternating regexes, deep nesting, and large unions produce big indices and more state churn. ## The savings side, which is often forgotten Constrained decoding also *removes* work: - **Fewer output tokens.** No preamble, no markdown fence, no closing commentary. On a short record that can be a third of the output. - **Fast-forwarding.** When only one continuation is legal for a stretch — the fixed characters of a key name, a closing bracket — some implementations emit those tokens without a forward pass. - **No retry tail.** Unconstrained extraction pays a full extra generation on every malformed response, and those retries dominate p99 latency far more than any per-step mask. So the honest answer to "what does it cost?" is often "less than the unconstrained pipeline it replaces", once retries are counted. ## How to measure it Don't argue from first principles; run the comparison on your own workload: - output tokens per second, constrained versus unconstrained, at equal batch size, - time to first token on a **cold** schema versus a warm one — this isolates compile cost, - p50 and p99 end-to-end latency with retries included on the unconstrained arm, since that is the real baseline, - total tokens per record, which is where the savings show up, and - the distinct-schema rate in production, as the leading indicator of cache-miss storms. The deciding metric is cost and latency per *valid* record, not per request.
- A service builds a schema per tenant at request time and first-token latency is erratic. What do you suspect?Cache-miss storms on grammar compilation. Every distinct schema is a new cache key, so a large share of requests pay the compile of the automaton and token index, which lands on time-to-first-token and wrecks p99 while the median looks fine. Collapse to one shared record schema, move tenant-specific rules into the validator, and pre-warm the cache for the schemas you keep.
- How would you design the benchmark that decides whether to enable constrained decoding?Measure cost and latency per *valid* record, not per request. The unconstrained arm must include its retry tail and its discard rate; the constrained arm must be run both cold and warm on the schema cache, at production batch size, on the real model size. Report p50 and p99, output tokens per record, and the truncation rate on both arms.
- Why does the mask overhead matter more for a small model than a large one?Because mask cost scales with vocabulary size and grammar complexity, not with parameter count, while the forward pass scales with the model. On a frontier model the per-step mask disappears into the step time; on a small extraction model serving high QPS it can be a visible fraction of it — which is exactly the deployment where people notice constrained decoding at all.
saying these in an interview costs you the question
- Assuming the grammar is recompiled on every request
- Thinking one mask can be shared across a batch
- Reporting overhead without counting the retries it removes
- Believing mask cost scales with model size
- Generating a distinct schema per request without caching