Skip to content

Faster inference by caching the KV projections - #15

Merged
rushin682 merged 2 commits into
peng-lab:mainfrom
Harry25R:perf/inference-speedup-v2
Sep 9, 2026
Merged

rushin682 merged 2 commits into
peng-lab:mainfrom
Harry25R:perf/inference-speedup-v2

Conversation

@Harry25R

Copy link
Copy Markdown
Contributor

Hi @rushin682

As earlier #8, while running large scale inference runs with phoenix I noticed the sampler was doing a fair amount of repeated work, so I've removed that in this PR... It adds an opt-in 'fast path' that gives roughly a 1.4–1.5× speedup at 0.1/0.01 tolerances, with identical outputs to the current sampler.

Concretely, the sampling process integrates the learned velocity field with an adaptive ODE solver:

$$\frac{dx_t}{dt} = v_\theta(x_t, t, c), \qquad t: t_0 \to t_1$$

The solver calls $v_\theta$ many times (more in lower tol). $x_t$ and $t$ change on every call, but the image features $c$ never do, since it's a constant.

Inside each cross-attention block, the parts that depend on the iamge features are only the key and value projections. $$K = \mathrm{norm}(c W_K), V = c W_V$$ while the queries come from $x_t$.

So, in every solver step we can avoid re-running the conditioning encoder blocks and re-projecting the same $K, V$ in every layer, since they will be identical each time.

Only $Q$ actually varies across steps, everything downstream of $c$ can be moved out of the integrand.

Changes:

  1. New helpers/fast_sampler.py with OptimizedFlow, which runs the conditioning path once (prep(c)), caches the per-block $(K, V)$ pairs, and reuses them in every velocity evaluation. run_fast_flow is a drop-in replacement for run_flow.
  2. The attention blocks in flow_llama3 and flow_simple accept an optional precomputed kv tuple (if absent, nothing changes)
  3. FlowPipeline gains a fast=False flag, so the default behaviour is untouched (this is purely opt-in)

For the dtypes, the cache mirrors the existing dtype handling (fp32 conditioning path, bf16 inside the attention blocks), so the fast path produces the same tensors as the stock path. The inference savings intuitively increase with lower tolerances since you are making many more sampler evaluations.

Testing
Added CPU tests in tests/test_inference.py covering the fast sampler, including a check that fast=True and the default path agree.

I also benchmarked both paths across a range of tolerances in a notebook (happy to share it, but didn't include it here for brevity), per-step cost drops consistently, and outputs are identical.

Thanks again for the great contribution! Very happy to adjust anything to suit the codebase :)

Best,
Harry

The conditioning tensor is constant across ODE solver steps, so its
key/value projections in every cross-attention block can be computed
once and reused. Adds run_fast_flow / OptimizedFlow and a fast=True
flag on FlowPipeline; attention blocks accept an optional precomputed
kv tuple. Output is identical to the default path.
Copilot AI lite review requested due to automatic review settings August 22, 2026 04:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in “fast path” for flow-model inference that caches cross-attention K/V projections derived from the (loop-invariant) conditioning features, reducing repeated work across ODE solver evaluations while aiming to keep outputs identical to the existing sampler.

Changes:

  • Introduces helpers/fast_sampler.py with OptimizedFlow and run_fast_flow() to precompute conditioning and per-block cached (K, V) projections once per sample batch.
  • Extends cross-attention blocks in flow_simple and flow_llama3 to accept optional precomputed kv projections (default path unchanged).
  • Adds fast=False option to FlowPipeline and CPU tests asserting fast=True matches the baseline sampler.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/test_inference.py Adds tests for fast sampler equivalence, error paths, and CPU end-to-end execution via FlowPipeline(fast=True).
src/phoenix/models/flow_simple.py Extends attention/block forward APIs to accept precomputed kv for cross-attention reuse.
src/phoenix/models/flow_llama3.py Same as above for the optimized/flash-attn model variant.
src/phoenix/helpers/inference.py Adds fast flag to FlowPipeline and routes sampling through run_fast_flow when enabled.
src/phoenix/helpers/fast_sampler.py New implementation that precomputes conditioning + per-block cached K/V projections and reuses them during ODE integration.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +451 to +455
if kv is not None:
xk, xv = kv # precomputed by the caller
if xk.shape[0] != x_bsz:
raise ValueError(f"cached k/v batch {xk.shape[0]} != query batch {x_bsz}")
else:
Comment on lines +369 to +373
if kv is not None:
xk, xv = kv # precomputed by the caller
if xk.shape[0] != x_bsz:
raise ValueError(f"cached k/v batch {xk.shape[0]} != query batch {x_bsz}")
else:
Comment on lines +80 to +83
if self._kv is None:
raise RuntimeError("call prep(c) before velocity()")
for block, kv in zip(m.blocks, self._kv, strict=True):
x = block(x, t, None, kv)
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.71605% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 41.28%. Comparing base (6701382) to head (63c9e60).

Files with missing lines Patch % Lines
src/phoenix/models/flow_llama3.py 0.00% 12 Missing ⚠️
src/phoenix/helpers/fast_sampler.py 96.22% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #15      +/-   ##
==========================================
+ Coverage   37.96%   41.28%   +3.32%     
==========================================
  Files           9       10       +1     
  Lines         951     1015      +64     
==========================================
+ Hits          361      419      +58     
- Misses        590      596       +6     
Files with missing lines Coverage Δ
src/phoenix/helpers/inference.py 100.00% <100.00%> (ø)
src/phoenix/models/flow_simple.py 80.60% <100.00%> (+0.29%) ⬆️
src/phoenix/helpers/fast_sampler.py 96.22% <96.22%> (ø)
src/phoenix/models/flow_llama3.py 0.00% <0.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rushin682
rushin682 merged commit e0d5d60 into peng-lab:main Sep 9, 2026
8 checks passed
@Harry25R

Copy link
Copy Markdown
Contributor Author

Thanks team :) Best of luck with the exciting research direction!

@Harry25R
Harry25R deleted the perf/inference-speedup-v2 branch September 10, 2026 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants