Costing an AI-powered SQL filter
The roofline model for AI-SQL latency · 200 level · October 2026
What it is
An AI-SQL query runs an LLM on every row. One WHERE clause asks the model
true or false about each record. Before running the query, an engine wants to know how long it
will take on a given model and GPU. Profiling answers that, but you redo it for every new model,
GPU, and workload. The roofline model answers with arithmetic instead: count the work a query
demands, divide by what the hardware can deliver, and you get a speed-of-light latency,
the fastest the query could run.
The method comes from a Full Stack Data Lab (Carnegie Mellon) post by Arnav Dhariya and Shreya Shankar, built for Quail, their AI-SQL execution engine. Quail uses the estimate to pick between query plans.
Roofline model
Latency is the larger of two times: the arithmetic divided by the GPU's compute rate, and the bytes moved divided by its memory bandwidth.
Speed-of-light latency
A lower bound from hardware limits. Count FLOPs and bytes, divide by the ceilings, with no profiling run.
Filter ordering
When a query chains several AI filters, the order changes the cost. The one that is cheap to run and rejects the most rows goes first.
The core idea: compute time or memory time, whichever is longer
Every GPU kernel does two things: it computes, and it moves data between HBM (the GPU's main memory) and the chip. A modern GPU overlaps the two, so the slower of them sets the kernel's time, not their sum.
Π (pi) is the GPU's peak compute rate in FLOP/s. β (beta) is its memory bandwidth in bytes/s. Divide the work a kernel needs by each ceiling and take the larger quotient. The ratio of work to data, operational intensity I = FLOPs / Bytes, decides which ceiling binds. The crossover sits at the ridge point I* = Π / β. Above the ridge point the kernel is compute-bound. A kernel below it is memory-bound, starved for data.
How it works, step by step
- Split the forward pass into three parts — the linear projections (query, key, value, output), the attention, and the MLP block. Each has its own FLOP count and byte count.
- Count the work. A dense matmul over the model's weights costs about 2 FLOPs per parameter per token. The bytes are the weights and activations each part reads from HBM.
- Divide by the ceilings and take the max per part. Projections and MLP run in FP8; attention runs in BF16 under FlashAttention, so each part uses its own Π.
- Sum the three parts to get one filter's latency over the whole batch of rows.
- For a chain of filters, reuse the KV cache. The first filter encodes each document once; later filters read those cached key/value vectors and pay only for their own question.
- Order the filters so a cheap one that rejects most rows runs first and shrinks the set the later filters must ask about.
- Add the per-filter times for the query's speed-of-light estimate.
What binds: compute or memory
Each part of the forward pass sits on a different side of the ridge point, and the side moves with how many tokens you push through at once.
| Part | Precision | Binds on | Crossover |
|---|---|---|---|
| Projections, MLP | FP8 | Compute, in this workload | Compute-bound above ~296 tokens per forward pass |
| Attention | BF16 (FlashAttention) | Compute here, memory for later filters | Dominates only past ~12,300 tokens per request |
Batch enough rows together and the projections and MLP become compute-bound, with the matmuls keeping the Tensor Cores busy. Attention stays small until requests get long, because a short review moves little data for the arithmetic it triggers.
A worked filter: 5,000 reviews in 6.64 seconds
The setup: Qwen3-4B in FP8 (3.6×109 non-embedding parameters, 36 layers, grouped-query attention with 8 KV heads), one H100, and 5,000 IMDB reviews averaging 298.8 tokens each. The filter F1 asks whether a review mentions a positive aspect. Every request is a preamble, the review, and the question; the model answers with a single token.
DOCUMENT:
<the review text>
Evaluate TRUE or FALSE for the following question: <filter predicate>
→ output: one token, true or false
The hardware ceilings that go into the roofline:
| H100 SXM spec | Value |
|---|---|
| Peak compute, BF16 (Π) | 989.5 × 1012 FLOP/s |
| Peak compute, FP8 (Π) | 1.979 × 1015 FLOP/s |
| HBM bandwidth (β) | 3.35 × 1012 bytes/s |
| HBM capacity | 80 GB |
| Ridge point, FP8 | ≈ 590.75 FLOP/byte |
| Ridge point, BF16 | ≈ 295.37 FLOP/byte |
Running F1 over all 5,000 reviews takes 16 forward passes. The roofline gives each part a compute time and a memory time, and you pay the larger.
| Component | Compute time | Memory time | Binds on |
|---|---|---|---|
| Projections | 1.6778 s | 0.0045 s | compute |
| Attention | 0.1850 s | 0.0774 s | compute |
| MLP | 4.7818 s | 0.0128 s | compute |
| Total (max per part, summed) | 6.64 s | 0.09 s | compute |
Every part is compute-bound, so memory time never shows up in the bill. The MLP block is the single biggest cost. Total: 6.64 seconds to run an LLM over 5,000 reviews.
Chaining filters, and why the order matters
Real queries chain several filters with AND. The trick is the KV cache. The first
filter processes each review once. Call that the scan, about 1.3 ms per document. That
work produces key/value vectors for the preamble and the document, and those stay cached. Every
later filter reuses them and pays only the ask: its own question tokens plus the one output
token, about 0.2 ms. The scan dwarfs the ask, so the second and third filters are nearly free next
to the first.
Because each filter passes only some rows to the next, the order sets how many documents reach each ask. The authors rank filters the way Hellerstein and Stonebraker ranked expensive database predicates: by cost over rejection power.
Here si is selectivity, the fraction of rows that pass. A low rank means the filter is cheap to ask and throws out a lot, so it goes first. Sort ascending by rank; the sort costs O(m log m) for m filters.
| Filter | Predicate | q (tokens) | selectivity s | ask (ms) | rank |
|---|---|---|---|---|---|
| F2 | discusses the ending | 45 | 0.2273 | 0.180 | 0.234 |
| F1 | mentions a positive aspect | 51 | 0.4856 | 0.203 | 0.394 |
| F3 | mentions a named actor | 49 | 0.6123 | 0.195 | 0.504 |
F2 rejects 77% of reviews and is the cheapest to ask, so it leads. The engine runs F2 → F1 → F3, not the written order. The payoff is modest because most of the cost is encoding each document once, which you pay no matter the order:
| Order | Speed-of-light time |
|---|---|
| F2 → F1 → F3 (chosen) | 6.8665 s |
| F1 → F2 → F3 (as written) | 7.1906 s |
| F3 → F2 → F1 | 7.2994 s |
Leading with F1 instead of F2 costs about 4.7% more. The whole three-filter query lands at 6.87 s, just above the 6.64 s for F1 alone, because KV reuse makes the two extra filters almost free.
Fitting the batch in memory
The KV cache has to fit in HBM. The authors budget it from the 80 GB on the card: about 76 GB usable, minus 4.5 GB for the FP8 weights and 18.08 GB for temporary buffers, leaves 53.42 GB for the cache. One document's prefix KV runs about 44.4 MB, so roughly 1,200 documents fit at once and the 5,000 reviews run in five batches. The query-wide estimate works out to 1.34×1016 FLOPs and about 6.86 seconds.
Limits and what to check
It's a lower bound
Speed-of-light assumes peak compute and bandwidth. Real GPUs fall short, so measured latency runs higher. Treat the number as a floor.
Mean length lies
Attention arithmetic grows with the square of length. Using the mean review length underestimates it when lengths vary.
Rough memory model
The buffer estimate is approximate; engines like vLLM profile activation memory with dummy inputs instead of a formula.
Independent selectivities
The ordering assumes each filter's pass rate is independent of the others. Correlated outcomes hurt the estimate.
No cross-filter overlap
The model charges each filter separately and doesn't let their compute and memory overlap across filters, so it overcounts a little.
Ordering rule has a limit
The rank rule is provably best only when every part is compute-bound. Mixed bottlenecks may need a different rule, which the authors leave open.
Further reading
- How to Cost Your AI-Powered Filters — the source post by Arnav Dhariya and Shreya Shankar, Full Stack Data Lab (CMU), Oct 1 2026. It carries an interactive playground for the filter-ordering effect (Qwen3-4B FP8 on H100).
- Williams, Waterman, Patterson, Roofline: An Insightful Visual Performance Model for Multicore Architectures (2009) — the original roofline model.
- Hellerstein and Stonebraker, Predicate Migration — the expensive-predicate ordering rule the filter sort is built on.