Skip to content

[feat]Implement depth-aware KV and asynchronous decode scheduling - #30

Merged
bjf-frz merged 8 commits into
ThinkFlowLab:mainfrom
bjf-frz:feature/cdb-async-runtime
Sep 17, 2026
Merged

bjf-frz merged 8 commits into
ThinkFlowLab:mainfrom
bjf-frz:feature/cdb-async-runtime

Conversation

@bjf-frz

@bjf-frz bjf-frz commented Sep 15, 2026 •

Copy link
Copy Markdown
Collaborator

Purpose

Implement depth-aware KV, delayed exit routing and asynchronous decode scheduling for Ouro. The latest update fixes long-context FA4 prefill by packing contiguous query chunks with request-level page tables, and adds bounded CUDA Graph replay for decode recurrent cores.

The table below distinguishes existing functionality from changes introduced by this PR and features that remain deferred.

Area Capability Status Remarks
Model Native Ouro checkpoint loading Existing Loads the model and its original early_exit_gate.
Execution stages Prefill, prelude, recurrent core, and coda Existing Preserves full-depth prefill and stage-based decoding.
Decode scheduling Stage queues and loop-level continuous batching Existing; extended Supports requests at different stages and recurrent depths.
Scheduling policy Refill and no-refill execution Existing; extended Both policies are supported by the new execution paths.
KV cache Paged, depth-aware LAST_EXITED layout Existing; extended Preserves ragged-depth KV semantics; integrates with asynchronous finalization.
KV cache SHARED layout Implemented in this PR Uses one physical KV plane across recurrent depths. This changes attention semantics relative to LAST_EXITED.
SHARED prefill Chunk-independent processing Implemented in this PR Completes all recurrent rounds for each position before advancing that request. Correctness-oriented; may be slower than packed prefill.
KV capacity Automatic GPU memory sizing Implemented in this PR Profiles runtime memory and accounts for configured utilization, buffers, and headroom. Supports explicit block-count or byte-budget overrides.
Request admission Bounded scanning and bypass Implemented in this PR Allows shorter requests to bypass a blocked request, with a bypass limit to protect waiting requests.
Chunked prefill Per-request chunk limits and decode interleaving Extended in this PR Adds configurable chunk ceilings and a bound on consecutive prefill batches before decode work.
Exit policy Original Ouro cumulative-hazard gate Existing; preserved Uses checkpoint gate weights with synchronous execution.
Exit policy Random Lookahead head Implemented in this PR Uses a separate, reproducible, untrained head. Intended for runtime validation; no quality guarantee.
Exit policy Delayed Ouro gate reuse (ouro_delayed) Implemented in this PR Reuses the checkpoint gate and cumulative-hazard policy, exiting one round after the decision. Supports synchronous and asynchronous execution.
Exit routing Pending exit decisions Implemented in this PR Preserves the final additional round's hidden state and KV; respects loop limits and disabled early exit.
CPU/GPU execution Asynchronous decode scheduling Implemented in this PR Overlaps CPU routing/metadata preparation with GPU execution, keeps sampled token IDs on device for the next prelude, and defers host output delivery using bounded in-flight submissions. Supports random Lookahead, delayed Ouro, and trace modes.
CUDA execution Separate core, boundary, and metadata-copy streams Implemented in this PR Uses events to enforce request dependencies. A single-stream option is available for comparison.
Memory lifetime Asynchronous buffer and request ownership Implemented in this PR Protects readback storage, hidden states, and KV reclamation across completion, cancellation, and request-ID reuse.
Execution buffers Reusable inputs, metadata, states, and readback buffers Implemented in this PR Intermediate model activations still use PyTorch allocation; execution is not allocation-free.
Batch execution Power-of-two padding Implemented in this PR Dummy rows have zero context and do not write KV, route exits, or sample tokens.
Replay Recorded exit-depth replay Implemented in this PR Bypasses gate evaluation and fixes per-token exit depths for controlled runtime comparisons.
Benchmarking Synthetic capture and replay benchmark Implemented in this PR Compares refill/no-refill and synchronous/asynchronous variants. Does not reproduce the paper's workload or establish its speedups.
Interfaces Python API, CLI, and HTTP serving configuration Extended in this PR Exposes the new cache, scheduling, exit-policy, and execution options.
Serving Tokenizer compatibility Fixed in this PR Removes reliance on a private Transformers byte-decoding helper.
Development Local pre-commit checks Added in this PR Runs Ruff lint, format checks, and CPU regression tests.
Attention Official FlashAttention paged adapters Added in the latest update flash_attn selects FA2/FA3/FA4 by GPU architecture; explicit versions are available. B300 validation uses FA4 4.0.0b30; FA2/FA3 hardware paths remain unverified.
FA4 prefill Packed variable-length queries and request-level page tables Fixed in the latest update Correct causal alignment with existing prefixes; avoids per-token table duplication and decode static workspaces. LAST_EXITED FA4 path; SHARED retains position-wise semantics.
Graph memory Automatic-sizing headroom Added in the latest update Default 1 GiB additional graph reserve during automatic KV sizing; explicit KV budgets remain user-controlled.
Large KV pools 64-bit Triton address arithmetic Fixed in the latest update Promotes physical block IDs before multiplying by large cache strides.
Async pipeline Device token handoff and pinned metadata staging Extended in the latest update Separates scheduled output progress from host-delivered outputs and preserves delayed EOS, abort, and resource ownership.
Lookahead training Distillation or trained next-round prediction Deferred Reusing the Ouro gate with delayed routing is a heuristic, not a trained Lookahead predictor.
CUDA Graphs Bounded decode recurrent-core capture and replay Implemented in the latest update Independent opt-in; dynamic positions/KV addresses; graph-owned buffers; eager fallback for unsupported batch sizes or cache limits. Prefill and sampling remain eager.
Model support Huginn Deferred This implementation targets Ouro.
KV management Incremental allocation, preemption, and prefix sharing Not implemented Requests continue to reserve KV for their full requested lifetime.
Paper reproduction Full accuracy and performance evaluation Not completed Published accuracy, throughput, and latency improvements are not claimed.

Known limitations: The final 612-trial campaign recorded differences in 196 trials against the respective policy reference: 182 had token differences and 168 had exit-depth differences (overlapping categories). Exact real-model BF16 equivalence and model quality are not established. Graphs help small batches substantially, but high-concurrency E2E can regress because graph coverage and scheduling/batch fragmentation remain limiting factors. See the measured results below. The latest commit contains runtime code and regression tests only; local experiment scripts and analysis documents are excluded.

Test Plan

Runtime parameters and activation

The following options are available in both the CLI and HTTP serving entrypoints.

Parameter How to enable or configure Purpose
--attention-backend triton, flash_attn, or explicit flash_attn_2/3/4 Select paged attention. Automatic flash_attn selects FA4 on B300; no silent backend fallback.
--exit-mode ouro, ouro_delayed, random_lookahead, or trace; defaults to ouro Select the original Ouro gate, delayed reuse of that gate, an untrained Lookahead head, or recorded exit-depth replay.
--lookahead-seed Set an integer, e.g. 7; defaults to 0 Make random Lookahead initialization reproducible.
--async-scheduling Add the flag; disabled by default Enable asynchronous scheduling with ouro_delayed, random_lookahead, or trace. CUDA execution supports triton and flash_attn backends. Static buffers are independently opt-in.
--single-stream Add alongside --async-scheduling Use one CUDA stream instead of separate core, boundary, and copy streams.
--cuda-graphs Add the flag; disabled by default Capture decode recurrent cores on CUDA with Triton or FlashAttention. Independent of --static-buffers; does not capture prefill, sampling, or Python request bookkeeping.
--cuda-graph-max-batch-size Default 128 Larger batches fall back to eager.
--cuda-graph-max-graphs Default 16 Bound the number of cached actual batch sizes; unseen shapes beyond the cap fall back to eager.
--cuda-graph-memory-reserve-bytes Default 1073741824 Additional headroom for graph memory in automatic KV sizing.
--static-buffers Add the flag Enable reusable input, metadata, and request-state buffers for synchronous or asynchronous execution.
--pad-to-power-of-two Add alongside --static-buffers Pad execution batches to powers of two. Dummy rows must not write KV or produce outputs.
--kv-layout last_exited or shared; defaults to last_exited Select depth-aware LAST_EXITED storage or one shared physical KV plane.
--gpu-memory-utilization Set a fraction; defaults to 0.9 Control the GPU memory budget used by automatic KV sizing.
--memory-reserve-bytes Set a byte count; defaults to 268435456 Reserve additional memory headroom during automatic sizing.
--kv-cache-memory-bytes Set an explicit KV byte budget Override automatic sizing. Mutually exclusive with --num-blocks.
--num-blocks Existing option; now optional Explicitly override KV block count. When omitted, CUDA uses automatic sizing; CPU retains a 256-block fallback.
--prefill-chunk-size Set a token count; defaults to 128 Limit each request's contribution to one prefill batch.
--max-prefill-batches-before-decode Set a positive integer; defaults to 1 Bound consecutive prefill batches when recurrent work is ready.
--admission-scan-limit Set a positive integer; defaults to 64 Bound how many waiting requests admission examines.
--max-admission-bypasses Set a positive integer; defaults to 8 Limit how often a blocked request can be bypassed by later requests.
--min-coda-batch-size Set a positive integer; defaults to 1 Configure the coda batching threshold when recurrent work is also available.
--exit-trace Provide a JSON file containing depths_by_request with --exit-mode trace Replay recorded per-request output depths. Request IDs must match the trace.

Existing request parameters min_loops, max_loops, and exit_threshold also control the new exit modes. Set exit_threshold=1.0 to disable adaptive early exit.

Start the asynchronous runtime

Activate the existing environment in fish and start the server with the local checkpoint:

env CUDA_VISIBLE_DEVICES=0 python -m vllm_lt.entrypoints.serve \
  --model Ouro-1.4B \
  --served-model-name ouro \
  --device cuda \
  --dtype bfloat16 \
  --attention-backend triton \
  --exit-mode ouro_delayed \
  --async-scheduling \
  --static-buffers \
  --pad-to-power-of-two \
  --kv-layout last_exited \
  --max-num-seqs 8 \
  --host 127.0.0.1 \
  --port 8000

Use an available GPU. For a synchronous comparison, remove only --async-scheduling, keeping the exit mode, cache layout, padding, and request parameters unchanged. For single-stream async, add --single-stream. For SHARED KV, replace --kv-layout last_exited with --kv-layout shared.

To exercise the random Lookahead path, replace --exit-mode ouro_delayed with --exit-mode random_lookahead --lookahead-seed 7.

Lookahead weights: random_lookahead currently uses random, untrained weights. A meaningful Lookahead quality evaluation requires trained weights, such as weights obtained through distillation. Those weights are not available in this implementation yet. ouro_delayed reuses the trained original Ouro gate, but delays its decision by one round; it is not a distilled Lookahead predictor.

Send a request

Wait for /health to return HTTP 200, then send a completion request:

curl -i http://127.0.0.1:8000/health

curl -sS http://127.0.0.1:8000/v1/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "ouro",
    "prompt": "Please explain matrix multiplication.",
    "max_tokens": 512,
    "temperature": 0,
    "min_loops": 2,
    "max_loops": 4,
    "exit_threshold": 0.9,
    "stream": false
  }' \
  | python -c 'import sys,json; r=json.load(sys.stdin); print(r["choices"][0]["text"] if "choices" in r else json.dumps(r,ensure_ascii=False,indent=2))'

A single request checks generation functionality. Concurrent requests are needed to exercise cross-request overlap. Earlier FP32 comparisons passed for their recorded workloads; exact real-model BF16 multi-stream equivalence remains unresolved. FA4 requires FP16/BF16; use Triton for FP32 checks.

Test Results

Inputs: frozen independent prompts, 128 greedy output tokens, ignore EOS, max_num_batched_tokens=2048, prefill_chunk_size=2048, LAST_EXITED KV, page size 16 and identical explicit KV capacity per context across variants. Contexts/concurrencies: 1K/[1,32,128], 4K/[1,16,64], 16K/[1,4,16], 64K/[1,2,4]; the 64K prompt is 65,409 tokens. E2E uses 2*C requests with completion-driven refill; decode uses C independent real-prefilled prefixes with prefill excluded. Each context uses one B300 for sequential P8/P9 runs, with order reversed in round two; four contexts run concurrently. Every measurement follows warmup, and graph capture is frozen during measurement.

All three rounds are retained, including suspected slow outliers. The new throughput results are appended to the original E2E and decode stacking tables below, using medians of three trials. Latency changes discussed here use medians of per-trial quantiles, not pooled samples. Throughput increases are better; latency decreases are better. Timing is engine host delivery, excluding HTTP/network/tokenization. Small-C tails are not SLO estimates. CPU affinity was not pinned, and external-contention attribution is not established.

Interpretation and remaining issues

  • Short-context high-concurrency decode improves: 1K/C32 +3.18%, 1K/C128 +8.42%, 4K/C64 +2.84%. At 1K/C128, E2E throughput improves 4.80%, TTFT p50 falls 8.85%, TPOT p50 falls 5.88%, and E2E p95 falls 3.74%. These real-gate comparisons still include output/depth differences; they do not isolate an equal-work implementation speedup.
  • Long-context decode is essentially flat. E2E does not improve universally: 16K/C4 −2.31%, with TPOT p50 +5.17% and ITL p95 +5.81%; this case has matching tokens/depths across variants and warrants profiling. At 4K/C64 E2E throughput is −0.30%, while TTFT p50 is +6.20%.
  • The 64K/C4 P9 E2E trials are 18.61 / 23.10 / 22.97 tok/s, versus P8 23.11 / 23.05 / 23.01. The median delta is −0.35%. The first P9 slow trial did not repeat, but its cause is unresolved; it remains in the report. The slow P8 1K/C32 second round is also retained. Neither had graph captures during measurement; compilation or external interference has not been proved.
  • Fewer per-request copies are a plausible contributor to short-context gains, but the campaign does not quantify eliminated GPU idle time. Remaining model launches, event waits, graph coverage and prefill/decode interference require profiling for causal attribution.
  • Raw logs, per-request timings, output IDs, exit depths and frozen source snapshots remain local under walkthrough_artifacts/async_state_p9_20260916/; they are not attached to this PR. This commit contains runtime/dependency changes and regression tests only, with no benchmark scripts or analysis documents.

Historical update (c0d7a5a)

Check Result Scope
.b_rdma pre-commit Passed before commit Ruff lint, format check, and CPU regressions.
Local full CPU/GPU regression 304 passed, 11 skipped Includes runtime regressions and locally retained benchmark tests; experimental scripts/tests are not included in this commit. GPU cases were executed.
Graph regression Covered Triton/FA4, sync/single-stream async/multi-stream, both KV layouts, static on/off Includes replay with changing metadata, request reuse, abort and bounded-cache eager fallback.
Packed FA4 prefill Passed targeted FP16/BF16 oracle checks and integration regressions Ragged prefixes, cross-page writes, different depths and future-token masking.
Real-model Graph toggle check Passed for the bounded workload 1K input, C4, 64 outputs, fixed 4 rounds; matching token IDs/depths per execution mode, warmup plus 3 measurements. Does not override discrepancies in the larger real-gate matrix.
Final stacking campaign 612/612 completed; all 8 workers exited 0 324 decode P0–P8 trials and 288 E2E P1–P8 trials. Historical E2E P0 is separate.
Measured Graph captures Zero during measured P8 trials Warmed shapes were frozen; uncovered shapes used eager fallback.
Model quality / exact BF16 equivalence Not established 196 trials with token and/or depth differences; no accuracy or paper-reproduction claim.

Runtime Python source in this commit matches the frozen source used by the completed campaign. The earlier report that FA4 prefill was still wired exclusively as length-one query sequences is superseded by this fix. Full CUDA Graph execution of all stages is not claimed.

Final protocol and stage definitions

B300, Ouro-1.4B BF16, FA4 4.0.0b30, LAST_EXITED KV, block size 16. Inputs: 1,024 / 4,096 / 16,384 / 65,409 tokens. Output: 128 tokens, greedy, ignore EOS. max_num_batched_tokens=2048, prefill_chunk_size=2048, max_num_seqs=C. Adaptive threshold 0.2, min/max rounds 2/4; fixed-depth threshold 1.0. E2E uses 2C requests with closed-loop replacement; decode uses C restored independent prefixes. Each configuration has full warmup and three measured trials. Input IDs are frozen LongBench-v2 head/tail-truncated questions; this is not an accuracy evaluation.

Decode stages share a common corrected-FA4 prefix. The earlier campaign used Triton prefixes, so cross-campaign decode comparisons are not strict same-prefix A/Bs. E2E includes actual prefill and drain time, excluding HTTP, tokenization, loading and warmup. Stage order is interleaved on the same GPU per context/scope. Percentages are ratios of unrounded trial medians; small changes are not established statistically significant improvements.

Stage Incremental change
P0 Triton, fixed 4 rounds, synchronous single stream
P1 Corrected FA4, including packed multi-query prefill
P2 Original checkpoint early-exit gate
P3 Delayed reuse of that gate: one additional round after triggering
P4 Async scheduling, single stream
P5 Multiple core/boundary/copy streams
P6 Static buffers
P7 Power-of-two padding
P8 Decode recurrent-core CUDA Graphs

P2→P3 changes exit semantics; it is not a pure scheduling change. P8 currently caches actual valid row counts (up to 16 shapes, batch size up to 128) and strips inactive padded rows in its graph path; it is not a power-of-two graph-bucket implementation. Its incremental result includes these implementation choices.

Complete E2E stacking table

Output tokens/s, median of three trials. Each cell after P0 shows throughput (change vs previous stage); the final columns show P9 / P0 − 1 and P9 / P1 − 1 against historical baselines. Percentages are calculated from unrounded medians.

P0 is historical, not rerun.* Consequently P1 vs P0* and the final baseline comparison are historical comparisons, not matched same-campaign A/B results. Rows without a complete historical P0 baseline show N/A for those comparisons. All P1–P8 values are from the new campaign.

The two columns following historical P8 append the new async-state experiment: P8 rerun with fixed FA4 split=1, then P9 with resident async state and its change against that rerun P8. The original P0–P8 columns retain the historical automatic-split results. The final P9/P0 and P9/P1 columns are cumulative cross-campaign comparisons, not controlled single-feature A/Bs: P0–P7 were not rerun and the FA4 split policy changed. These percentages use the displayed historical baseline values and the unrounded P9 medians; missing historical P0 stays N/A. The split=1 P8 rerun is not an isolated split-policy A/B. All three new repetitions are retained. High-concurrency output/depth differences remain unresolved.

Context C P0* historical P1 FA4 P2 early exit P3 delayed P4 async P5 multi-stream P6 static P7 padding P8 Graph P8 Graph (split=1 rerun) P9 + resident async state (vs rerun P8) P9 vs P0 (historical baseline) P9 vs P1 (historical baseline)
1K 1 33.45 27.11 (-19.0%) 43.64 (+61.0%) 31.74 (-27.3%) 32.42 (+2.1%) 32.46 (+0.1%) 32.26 (-0.6%) 32.05 (-0.7%) 80.50 (+151.2%) 80.58 80.24 (-0.41%) +139.89% +195.99%
1K 32 390.61 637.77 (+63.3%) 806.07 (+26.4%) 666.14 (-17.4%) 665.97 (-0.0%) 667.30 (+0.2%) 661.20 (-0.9%) 664.87 (+0.6%) 616.39 (-7.3%) 635.42 629.00 (-1.01%) +61.03% -1.38%
1K 128 514.47 1358.30 (+164.0%) 1519.80 (+11.9%) 1377.41 (-9.4%) 1371.50 (-0.4%) 1364.79 (-0.5%) 1339.63 (-1.8%) 1347.27 (+0.6%) 1266.18 (-6.0%) 1274.57 1335.73 (+4.80%) +159.63% -1.66%
4K 1 14.10 27.21 (+93.1%) 44.90 (+65.0%) 32.48 (-27.7%) 32.43 (-0.2%) 32.57 (+0.4%) 32.04 (-1.6%) 32.18 (+0.4%) 78.16 (+142.9%) 68.21 68.88 (+0.99%) +388.52% +153.15%
4K 16 44.00 272.31 (+518.9%) 348.85 (+28.1%) 298.71 (-14.4%) 297.44 (-0.4%) 294.84 (-0.9%) 296.95 (+0.7%) 294.58 (-0.8%) 289.45 (-1.7%) 306.63 306.97 (+0.11%) +597.66% +12.73%
4K 64 49.34 435.90 (+783.5%) 505.08 (+15.9%) 463.07 (-8.3%) 481.08 (+3.9%) 452.64 (-5.9%) 449.83 (-0.6%) 450.28 (+0.1%) 411.00 (-8.7%) 444.90 443.56 (-0.30%) +798.98% +1.76%
16K 1 2.28 24.97 (+995.8%) 37.85 (+51.6%) 29.28 (-22.6%) 29.27 (-0.0%) 29.24 (-0.1%) 29.04 (-0.7%) 28.95 (-0.3%) 55.87 (+93.0%) 38.34 38.22 (-0.31%) +1576.31% +53.06%
16K 4 3.12 70.16 (+2145.9%) 90.87 (+29.5%) 77.05 (-15.2%) 76.77 (-0.4%) 77.14 (+0.5%) 76.80 (-0.4%) 76.51 (-0.4%) 92.20 (+20.5%) 80.32 78.46 (-2.31%) +2414.70% +11.83%
16K 16 3.43 113.16 (+3203.9%) 134.34 (+18.7%) 120.52 (-10.3%) 124.21 (+3.1%) 118.22 (-4.8%) 117.39 (-0.7%) 116.31 (-0.9%) 114.05 (-1.9%) 109.43 110.75 (+1.20%) +3128.77% -2.13%
64K 1 0.20 16.00 (+7840.3%) 20.48 (+28.0%) 17.58 (-14.2%) 17.57 (-0.1%) 17.55 (-0.1%) 17.53 (-0.1%) 17.45 (-0.4%) 23.10 (+32.3%) 13.06 13.02 (-0.28%) +6410.72% -18.62%
64K 2 — 22.20 (N/A) 26.05 (+17.4%) 23.64 (-9.2%) 23.76 (+0.5%) 23.70 (-0.2%) 23.63 (-0.3%) 23.71 (+0.4%) 25.34 (+6.9%) 17.33 17.26 (-0.40%) N/A -22.27%
64K 4 — 25.37 (N/A) 28.97 (+14.2%) 26.52 (-8.4%) 27.45 (+3.5%) 26.59 (-3.1%) 26.64 (+0.2%) 26.50 (-0.5%) 27.35 (+3.2%) 23.05 22.97 (-0.35%) N/A -9.48%

E2E latency — fixed-split P8 → P9

Each cell shows P8 → P9 (percentage change); lower is better. All values are milliseconds, using medians of three per-trial percentiles (not pooled percentiles). Both variants use FA4 split=1; these are the same runs as the P8 split=1 rerun and P9 throughput columns above. All repetitions, including slow outliers, are retained. Small-concurrency p95 values are descriptive, not SLO estimates. TTFT is time to first delivered token; TPOT is each request's average interval after its first token; ITL describes individual delivered-token intervals; E2E is request completion latency. Timing excludes HTTP/network/tokenization overhead.

Context C Paired reps TTFT p50 ms TTFT p95 ms TPOT p50 ms TPOT p95 ms ITL p50 ms ITL p95 ms E2E p50 ms E2E p95 ms
1K 1 3/3 57.00 → 56.93 (-0.13%) 58.99 → 59.61 (+1.05%) 11.29 → 11.34 (+0.39%) 12.82 → 12.86 (+0.38%) 10.67 → 10.71 (+0.38%) 14.19 → 14.24 (+0.37%) 1493.22 → 1499.51 (+0.42%) 1683.87 → 1690.75 (+0.41%)
1K 32 3/3 83.43 → 81.48 (-2.34%) 3159.12 → 3170.61 (+0.36%) 38.38 → 38.24 (-0.36%) 49.61 → 48.97 (-1.30%) 30.14 → 29.43 (-2.35%) 96.51 → 96.75 (+0.24%) 5299.01 → 5336.11 (+0.70%) 9106.65 → 9185.87 (+0.87%)
1K 128 3/3 285.07 → 259.83 (-8.85%) 7099.20 → 7019.52 (-1.12%) 79.90 → 75.21 (-5.88%) 104.32 → 99.00 (-5.10%) 51.06 → 46.12 (-9.68%) 282.25 → 280.76 (-0.53%) 11058.39 → 10530.92 (-4.77%) 19104.65 → 18390.44 (-3.74%)
4K 1 3/3 161.80 → 160.35 (-0.89%) 162.00 → 164.06 (+1.28%) 13.27 → 13.13 (-1.06%) 13.72 → 13.57 (-1.10%) 12.29 → 12.15 (-1.09%) 16.36 → 16.17 (-1.16%) 1848.70 → 1827.82 (-1.13%) 1904.49 → 1888.25 (-0.85%)
4K 16 3/3 2209.71 → 2220.08 (+0.47%) 3431.71 → 3435.24 (+0.10%) 31.95 → 31.86 (-0.29%) 39.62 → 39.63 (+0.03%) 23.77 → 23.54 (-0.96%) 102.35 → 102.89 (+0.53%) 5955.18 → 5942.76 (-0.21%) 7689.18 → 7683.71 (-0.07%)
4K 64 3/3 5853.34 → 6216.51 (+6.20%) 12147.26 → 12519.42 (+3.06%) 95.31 → 95.18 (-0.13%) 123.58 → 121.37 (-1.78%) 53.38 → 51.23 (-4.03%) 217.82 → 213.65 (-1.92%) 16292.63 → 16123.47 (-1.04%) 24847.40 → 24774.91 (-0.29%)
16K 1 3/3 627.64 → 623.41 (-0.67%) 630.95 → 631.62 (+0.11%) 20.70 → 20.80 (+0.48%) 21.96 → 22.08 (+0.52%) 19.30 → 19.39 (+0.48%) 25.70 → 25.81 (+0.46%) 3257.10 → 3264.51 (+0.23%) 3420.08 → 3433.50 (+0.39%)
16K 4 3/3 914.73 → 926.70 (+1.31%) 2515.46 → 2537.49 (+0.88%) 33.79 → 35.53 (+5.17%) 45.33 → 47.16 (+4.05%) 27.37 → 29.04 (+6.12%) 40.77 → 43.14 (+5.81%) 6024.94 → 6258.33 (+3.87%) 8099.34 → 8345.71 (+3.04%)
16K 16 3/3 9574.77 → 9425.20 (-1.56%) 12881.26 → 12695.42 (-1.44%) 57.83 → 56.22 (-2.78%) 96.57 → 96.82 (+0.27%) 43.63 → 43.39 (-0.53%) 131.53 → 128.23 (-2.51%) 16579.66 → 16364.58 (-1.30%) 25145.25 → 25018.35 (-0.50%)
64K 1 3/3 3203.22 → 3208.37 (+0.16%) 3250.65 → 3254.99 (+0.13%) 50.79 → 50.91 (+0.24%) 52.81 → 53.00 (+0.35%) 46.17 → 46.31 (+0.31%) 61.52 → 61.72 (+0.33%) 9698.20 → 9674.06 (-0.25%) 9910.17 → 9985.57 (+0.76%)
64K 2 3/3 5890.61 → 5901.97 (+0.19%) 6444.81 → 6460.19 (+0.24%) 63.78 → 64.21 (+0.67%) 75.73 → 76.20 (+0.61%) 61.97 → 62.32 (+0.57%) 92.71 → 93.25 (+0.59%) 14516.20 → 14583.59 (+0.46%) 15575.73 → 15626.88 (+0.33%)
64K 4 3/3 12508.58 → 12537.36 (+0.23%) 13061.28 → 13200.39 (+1.07%) 71.09 → 71.18 (+0.13%) 82.15 → 82.25 (+0.13%) 63.30 → 63.34 (+0.07%) 93.62 → 93.99 (+0.40%) 21658.20 → 21693.51 (+0.16%) 23351.61 → 23488.52 (+0.59%)

Complete decode-only stacking table

Output tokens/s, median of three trials. Each cell after P0 shows throughput (change vs previous stage); the final columns show P9 / P0 − 1 and P9 / P1 − 1 against historical baselines. Percentages are calculated from unrounded medians.

All stages were rerun with a common corrected-FA4 prefix; prefill time is excluded.

The two columns following historical P8 append the new async-state experiment: P8 rerun with fixed FA4 split=1, then P9 with resident async state and its change against that rerun P8. The original P0–P8 columns retain the historical automatic-split results. The final P9/P0 and P9/P1 columns are cumulative cross-campaign comparisons, not controlled single-feature A/Bs: P0–P7 were not rerun and the FA4 split policy changed. These percentages use the displayed historical baseline values and the unrounded P9 medians; missing historical P0 stays N/A. The split=1 P8 rerun is not an isolated split-policy A/B. All three new repetitions are retained. High-concurrency output/depth differences remain unresolved.

Context C P0 Triton P1 FA4 P2 early exit P3 delayed P4 async P5 multi-stream P6 static P7 padding P8 Graph P8 Graph (split=1 rerun) P9 + resident async state (vs rerun P8) P9 vs P0 (historical baseline) P9 vs P1 (historical baseline)
1K 1 33.43 27.51 (-17.7%) 48.49 (+76.2%) 34.36 (-29.1%) 34.88 (+1.5%) 34.81 (-0.2%) 34.90 (+0.3%) 34.82 (-0.2%) 91.25 (+162.1%) 90.12 89.70 (-0.47%) +168.32% +226.06%
1K 32 878.72 821.67 (-6.5%) 1126.43 (+37.1%) 882.43 (-21.7%) 894.12 (+1.3%) 882.45 (-1.3%) 872.17 (-1.2%) 876.81 (+0.5%) 1205.51 (+37.5%) 1146.05 1182.53 (+3.18%) +34.57% +43.92%
1K 128 1842.42 2467.41 (+33.9%) 3186.85 (+29.2%) 2607.46 (-18.2%) 2578.52 (-1.1%) 2481.69 (-3.8%) 2322.11 (-6.4%) 2406.18 (+3.6%) 2600.17 (+8.1%) 2768.85 3002.00 (+8.42%) +62.94% +21.67%
4K 1 18.93 20.78 (+9.8%) 34.92 (+68.1%) 24.67 (-29.4%) 24.13 (-2.2%) 24.13 (+0.0%) 24.32 (+0.8%) 24.14 (-0.7%) 85.37 (+253.7%) 74.31 74.11 (-0.27%) +291.48% +256.63%
4K 16 242.57 312.55 (+28.8%) 431.75 (+38.1%) 344.51 (-20.2%) 337.21 (-2.1%) 334.57 (-0.8%) 333.44 (-0.3%) 331.99 (-0.4%) 608.56 (+83.3%) 616.86 617.97 (+0.18%) +154.76% +97.72%
4K 64 593.60 878.56 (+48.0%) 1272.15 (+44.8%) 942.37 (-25.9%) 1041.08 (+10.5%) 1008.50 (-3.1%) 887.58 (-12.0%) 875.41 (-1.4%) 1090.71 (+24.6%) 1210.09 1244.46 (+2.84%) +109.65% +41.65%
16K 1 6.24 27.58 (+341.9%) 42.61 (+54.5%) 32.06 (-24.7%) 32.30 (+0.7%) 32.33 (+0.1%) 32.20 (-0.4%) 32.09 (-0.3%) 74.23 (+131.3%) 45.56 45.34 (-0.47%) +626.63% +64.40%
16K 4 24.42 107.81 (+341.6%) 165.28 (+53.3%) 124.92 (-24.4%) 124.17 (-0.6%) 122.31 (-1.5%) 123.83 (+1.2%) 122.95 (-0.7%) 173.11 (+40.8%) 136.06 135.91 (-0.11%) +456.57% +26.07%
16K 16 81.47 279.19 (+242.7%) 426.86 (+52.9%) 320.45 (-24.9%) 349.58 (+9.1%) 308.24 (-11.8%) 308.10 (-0.0%) 298.99 (-3.0%) 322.25 (+7.8%) 317.87 319.06 (+0.37%) +291.63% +14.28%
64K 1 1.69 16.67 (+884.2%) 27.64 (+65.8%) 19.87 (-28.1%) 21.30 (+7.2%) 20.07 (-5.8%) 19.88 (-0.9%) 20.16 (+1.4%) 55.80 (+176.8%) 19.85 19.81 (-0.17%) +1072.42% +18.86%
64K 2 3.37 33.34 (+889.5%) 52.51 (+57.5%) 40.22 (-23.4%) 39.89 (-0.8%) 39.45 (-1.1%) 40.05 (+1.5%) 36.88 (-7.9%) 70.19 (+90.3%) 30.56 30.57 (+0.05%) +807.11% -8.31%
64K 4 6.71 73.50 (+995.5%) 112.07 (+52.5%) 84.13 (-24.9%) 91.21 (+8.4%) 83.98 (-7.9%) 83.05 (-1.1%) 83.70 (+0.8%) 92.00 (+9.9%) 57.79 57.78 (-0.02%) +761.05% -21.39%

Decode-only latency — fixed-split P8 → P9

Each cell shows P8 → P9 (percentage change); lower is better. All values are milliseconds, using medians of three per-trial percentiles (not pooled percentiles). Both variants use FA4 split=1; these are the same runs as the P8 split=1 rerun and P9 throughput columns above. All repetitions, including slow outliers, are retained. Small-concurrency p95 values are descriptive, not SLO estimates. TTFT and request E2E are not reported for decode-only runs because prefix construction occurs outside the measured interval.

Context C Paired reps TPOT p50 ms TPOT p95 ms ITL p50 ms ITL p95 ms
1K 1 3/3 11.17 → 11.22 (+0.42%) 11.17 → 11.22 (+0.42%) 10.54 → 10.59 (+0.44%) 14.03 → 14.09 (+0.44%)
1K 32 3/3 26.49 → 25.91 (-2.21%) 28.04 → 27.12 (-3.29%) 26.69 → 26.04 (-2.45%) 35.99 → 35.48 (-1.41%)
1K 128 3/3 43.03 → 39.19 (-8.92%) 45.44 → 41.35 (-9.01%) 40.43 → 37.30 (-7.74%) 58.27 → 55.60 (-4.58%)
4K 1 3/3 13.55 → 13.59 (+0.23%) 13.55 → 13.59 (+0.23%) 12.14 → 12.17 (+0.24%) 16.15 → 16.19 (+0.26%)
4K 16 3/3 24.82 → 24.76 (-0.23%) 26.12 → 26.07 (-0.22%) 23.75 → 23.70 (-0.21%) 29.77 → 29.71 (-0.20%)
4K 64 3/3 51.22 → 49.69 (-2.99%) 52.64 → 51.32 (-2.50%) 51.02 → 47.29 (-7.31%) 64.11 → 66.21 (+3.28%)
16K 1 3/3 22.12 → 22.21 (+0.44%) 22.12 → 22.21 (+0.44%) 19.39 → 19.48 (+0.44%) 25.82 → 25.93 (+0.43%)
16K 4 3/3 28.58 → 28.58 (+0.00%) 29.62 → 29.62 (+0.01%) 27.22 → 27.20 (-0.09%) 40.15 → 40.14 (-0.03%)
16K 16 3/3 48.86 → 48.63 (-0.47%) 50.71 → 50.49 (-0.43%) 46.88 → 46.65 (-0.48%) 58.77 → 58.52 (-0.43%)
64K 1 3/3 50.77 → 50.85 (+0.16%) 50.77 → 50.85 (+0.16%) 46.07 → 46.14 (+0.15%) 61.39 → 61.50 (+0.17%)
64K 2 3/3 64.27 → 64.23 (-0.06%) 65.96 → 65.92 (-0.06%) 62.08 → 61.90 (-0.29%) 92.68 → 92.53 (-0.16%)
64K 4 3/3 67.44 → 67.44 (-0.01%) 69.75 → 69.74 (-0.01%) 63.21 → 63.18 (-0.04%) 79.95 → 79.16 (-0.98%)

Incremental E2E gain or regression

Each column compares with the immediately preceding stage. P1/P0 is omitted because E2E P0 is historical.

Context/C Early exit Delayed Async Multi-stream Static Padding Graph
1K/1 +61.0% -27.3% +2.1% +0.1% -0.6% -0.7% +151.2%
1K/32 +26.4% -17.4% -0.0% +0.2% -0.9% +0.6% -7.3%
1K/128 +11.9% -9.4% -0.4% -0.5% -1.8% +0.6% -6.0%
4K/1 +65.0% -27.7% -0.2% +0.4% -1.6% +0.4% +142.9%
4K/16 +28.1% -14.4% -0.4% -0.9% +0.7% -0.8% -1.7%
4K/64 +15.9% -8.3% +3.9% -5.9% -0.6% +0.1% -8.7%
16K/1 +51.6% -22.6% -0.0% -0.1% -0.7% -0.3% +93.0%
16K/4 +29.5% -15.2% -0.4% +0.5% -0.4% -0.4% +20.5%
16K/16 +18.7% -10.3% +3.1% -4.8% -0.7% -0.9% -1.9%
64K/1 +28.0% -14.2% -0.1% -0.1% -0.1% -0.4% +32.3%
64K/2 +17.4% -9.2% +0.5% -0.2% -0.3% +0.4% +6.9%
64K/4 +14.2% -8.4% +3.5% -3.1% +0.2% -0.5% +3.2%

Updated analysis

FA4 prefill is fixed, not merely relabeled. The previous adapter represented a chunk as independent length-one query sequences and duplicated the page table per token. The new LAST_EXITED FA4 path packs each contiguous request chunk, supplies query boundaries and the final visible prefix length, and uses request-level page tables with bottom-right causal alignment. It bypasses decode static metadata and does not reduce input length, prefill rounds or token budget. Historical P1→current P1 comparisons show the size of the improvement, but are not interleaved cross-version A/Bs:

P1 workload Old→new TTFT p50 (s) Old→new E2E p50 (s)
16K/C4 47.90→2.40 58.75→7.25
16K/C16 189.55→9.69 223.16→17.82
64K/C1 216.88→3.23 221.66→7.99

Original early exit remains valuable, especially at high concurrency. At the maximum tested concurrency for 1K/4K/16K/64K, E2E P2/P1 gains are about +11.9%/+15.9%/+18.7%/+14.2%. Lower prefill cost makes decode savings more visible. Mean decode rounds are approximately 2.4–2.5 for P2 and 3.3–3.4 for delayed P3. The extra round still consumes a substantial part of the original early-exit benefit; policy-changing comparisons do not establish equal model quality.

Async and multiple streams remain workload-dependent. Maximum-concurrency decode P4/P3 improves about +10.5% at 4K/C64, +9.1% at 16K/C16 and +8.4% at 64K/C4, but loses about 1.1% at 1K/C128. Multiple streams can reverse gains. In this campaign 4K/C64 decode P4/P5 has essentially unchanged recurrent batch size and call count, so its regression cannot be attributed to the old campaign's fragmentation pattern. Other long-context cases still exhibit smaller batches and more calls. Profiling is needed to apportion event, launch and resource-contention costs.

Static E2E regressions are reduced, not eliminated. At 1K/C128 the static increment changes from approximately −14.8% in the old campaign to −1.8% now; at 4K/C64 from −9.6% to −0.6%. This is consistent with removing the old prefill workspace overhead. Decode static regressions remain: approximately −6.4% at 1K/C128 and −12.0% at 4K/C64. Padding has small, mixed effects and is not a uniformly beneficial default.

Graph replay strongly helps small batches. C1 decode gains over P7 are +162.1%/+253.7%/+131.3%/+176.8% for 1K/4K/16K/64K; E2E gains are +151.2%/+142.9%/+93.0%/+32.3%. This is consistent with reducing recurrent-core host/launch overhead, but is not a measurement of GPU idle-time percentage. Only warmed core shapes are graphed; first-use capture latency is excluded from these steady-state numbers.

High-concurrency Graph regressions involve both coverage and batch fragmentation. The table below uses E2E data. Hit rate is the median per-trial fraction of recurrent calls replayed, not token coverage.

E2E case P8/P7 throughput Graph hit rate Mean recurrent batch P7→P8 Recurrent calls P7→P8
1K/32 -7.3% 38.4% 29.99→21.51 923→1291
1K/128 -6.0% 27.7% 111.28→83.63 992→1319
4K/16 -1.7% 100.0% 14.94→7.62 905→1773
4K/64 -8.7% 49.2% 43.50→24.82 1251→2195
16K/16 -1.9% 100.0% 11.10→7.01 1207→1910
64K/4 +3.2% 100.0% 3.00→2.90 1121→1170

16K/C16 and 4K/C16 still regress despite 100% graph hits; increasing cache capacity alone cannot resolve this. Faster core execution changes refill/exit-retirement timing, and the measured batches become smaller with more calls at similar average depth. Stable-input copies, output clones and event protection also cost time. Batch statistics support the fragmentation finding; they do not establish each cost's causal contribution without profiling.

Full stacking is not universally fastest. P8 is fastest in all measured E2E C1 cases and slightly ahead at 16K/C4. P2 is fastest in the other E2E cases. Comparing these policies does not establish answer-quality equivalence. Investigate same-policy output differences, graph batching/bucketing and high-concurrency scheduling before expanding the matrix again.

Correctness and measurement limitations

  • 196 of 612 trials differ from their scope/context/concurrency/policy reference: 182 have token differences and 168 have depth differences, with overlap. Different exit policies are not compared for equivalence. Reference trials themselves are not independent correctness proofs.
  • E2E P1 has no newly measured Triton reference, so repeat consistency does not prove FA4/Triton equivalence. Trial order/reference selection differs from the previous campaign; comparing the raw discrepancy count 196 versus 154 does not establish worsening or improvement.
  • BF16 numerical effects may contribute, but first-divergence logits/decisions have not been diagnosed. Structural checks and exit code 0 are not accuracy qualification.
  • Three repetitions are limited evidence. For example, decode 4K/C1 P1 throughput was 20.81/20.78/16.91 tokens/s. Small deltas and cross-campaign differences should not be overinterpreted.
  • TTFT/TPOT/E2E percentiles are over requests; ITL percentiles are over token intervals within each trial. Reported percentiles are medians of trial percentiles, not pooled percentiles. Small-C tail latency is not a stable SLO estimate.
  • Raw timing, token IDs, exit depths, p99, repeat ranges, graph counters and source snapshots remain in local experiment artifacts. Experimental scripts and analysis documents are deliberately excluded from the PR file changes.

Code pointers

Add automatic KV sizing, bounded admission bypass, chunked prefill controls, shared KV, reusable buffers, padding, and trace replay. Support random lookahead and delayed exits using the checkpoint Ouro gate in synchronous and asynchronous execution.

Add runtime regression tests and local pre-commit checks. Document the unresolved BF16 multi-stream output discrepancy observed with real Ouro weights.

Signed-off-by: bjf-frz <frz123db@gmail.com>

@hsliuustc0106 hsliuustc0106 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed e525f64 against 6e8abbc. Found two P2 issues, detailed inline. Synchronous execution remains a useful fallback while the async path is validated.

Validation: Ruff lint, Python formatting, syntax parsing of all 43 Python files, and git diff --check passed. Ruff 0.16.3 also flagged formatting in one Markdown example in docs/cdb_runtime.md. Runtime tests were not run because isolation for executing the fork's code was unavailable on the reviewer host. The disclosed BF16 multi-stream discrepancy remains unverified.

Comment thread vllm_lt/core/memory.py
* math.ceil(config.max_position_embeddings / cache.block_size)
* 4
)
budget = min(free_before, int(total * cache.gpu_memory_utilization) - reserved)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] Include reusable allocator memory in the KV budget

After an engine is deleted, PyTorch can retain its freed KV storage in the caching allocator. Both free_before and the utilization limit minus reserved count that storage as unavailable. Recreating an engine in the same process can therefore produce a much smaller cache or fail with "no memory remains" despite sufficient reusable memory. Account for reserved-but-unallocated storage when calculating capacity, while preserving the utilization limit and runtime headroom. See PyTorch's allocator semantics.

engine = LLMEngine(
model,
cache_config=CacheConfig(num_blocks=args.num_blocks, block_size=args.block_size),
**runtime_configs(args),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] Provide a request-ID mapping before enabling HTTP trace replay

Passing the shared runtime configuration here enables --exit-mode trace --exit-trace ... in the HTTP server, but RequestChannel assigns a fresh cmpl-<uuid> ID to every request. LLMEngine.add_request() requires that exact ID in the startup trace, and the HTTP request schema provides no way to select one. Normal replay requests therefore fail trace validation with HTTP 400. Add a replay-ID mapping, or reject trace mode at server startup until the serving interface supports it.

@hsliuustc0106

Copy link
Copy Markdown
Collaborator

run the A/B test

@hsliuustc0106

Copy link
Copy Markdown
Collaborator

Owner review

Reviewed e525f6460a47adc6805ba0d0c54281786a523deb against base 6e8abbc (full diff read plus frozen-tree verification; CPU-side checks executed locally; no GPU jobs run by this review).

Verdict: no new P1/P2 findings; 4×P3. The two P2s from the earlier review remain the open blockers — both re-verified.

Earlier P2s — re-verified, still open

  1. KV budget vs caching-allocator residue (vllm_lt/core/memory.py) — confirmed by read: budget = min(free_before, int(total * gpu_memory_utilization) - reserved) counts reserved-but-unallocated storage as unavailable on both terms, so recreating an engine in the same process under-sizes the cache or fails with "no memory remains" despite reusable memory.
  2. HTTP + trace mode unusable (vllm_lt/entrypoints/serve.py, vllm_lt/serving/worker.py:25) — confirmed: RequestChannel assigns cmpl-<uuid> IDs; LLMEngine.add_request requires that exact ID in depths_by_request, so every replay request fails validation with HTTP 400.

New findings

P3 — auto-sizing probe ignores cache.layout, under-provisioning SHARED (vllm_lt/core/memory.py, the probe = KVCacheManager(...) call): the profiling cache omits layout=cache.layout, so it always allocates total_ut_steps planes of temporary KV. For SHARED, the measured peak (doubled under async multi-stream) includes planes steady state never holds, shrinking the computed budget. Conservative — no wrong outputs — but it contradicts the stated "block accounting follows the physical plane count". Reachable via --kv-layout shared with automatic CUDA sizing. Fix: pass layout=cache.layout and size probe blocks by storage_depths.

P3 — runtime_configs claims legacy-namespace support it doesn't provide (vllm_lt/entrypoints/runtime_args.py): the comment says load_engine is also called directly with older argparse namespaces, and the SchedulerConfig kwargs defensively use getattr(args, ..., default) — but CacheConfig(num_blocks=args.num_blocks, ...) raises AttributeError on any namespace lacking num_blocks, because that flag is defined in cli.py/serve.py, not add_runtime_args, so the defaults merge doesn't include it. Either fold num_blocks/block_size into the merged defaults or drop the dead getattrs.

P3 — cross-module private reach-ins: vllm_lt/worker/buffers.py re-implements _prepare_batch addressing through cache._validate_rows/cache._plane/allocation.block_tables (a second copy of the manager's address logic to keep in sync); benchmarks/cdb_runtime.py reads engine.model_runner.last_submitted_size/.synchronize(); new tests poke scheduler._admit() and cache._free_blocks. A small public surface on KVCacheManager/ModelRunner would remove the drift risk.

P3 — docs evidence inconsistencies (docs/cdb_runtime.md): the "Recorded validation" section cites 199 passed, 11 skipped while the ouro_delayed section and the PR body cite 215 passed, 11 skipped, without labeling which run/commit each number belongs to; the startup examples hardcode machine-specific paths (.b_rdma, /home/zjy/..., B300).

Verified sound (full-diff read + tracing)

Async event chains (core round → finalize KV copies → coda gather), readback slot leases (bounded at max_num_seqs + 2 per dtype, provably sufficient), request-ID-reuse identity skip in _update, padding hygiene (dummy rows never acquire KV addresses; zero-length attention rows return zeros in both backends), write-before-attend contract preserved (models/ouro.py), delayed-exit sync/async equivalence round-by-round, and scheduler PRELUDE-first ordering (no starvation path; the async schedule() is None case is handled by blocking-collect of pending coda). One benign nuance: aborting a request with in-flight coda leaves the ticket in _pending_coda until the next step() collects it lazily — on CUDA this holds one readback lease through idle only, and collection precedes new submissions, so it self-heals.

Validation (reviewed SHA e525f64)

  • Full CPU suite locally: 193 passed, 33 skipped, 0 failed (torch 2.8.0, transformers 5.2.0, macOS; skips are GPU-gated plus lm_eval). Notably tests/test_serving.py passes 39/39 at this head, while base 6e8abbc reproduces the known 14 serving-test failures on the same machine — the IncrementalText byte-alphabet fix (verified equivalent to transformers' bytes_to_unicode mapping) is what repairs them under transformers 5.2.0.
  • Ruff lint + format: clean at head.
  • Adversarial stress (CPU): 40 randomized trials across both KV layouts × refill/no-refill × sync/async × static/padding × all three exit modes, with request-ID reuse and ~35%-per-step abort storms; asserted full drain plus zero leaked KV blocks, state slots, and signals: 547 completions, zero leaks.
  • GPU results in the PR (device suites, B300 real-model FP32/BF16 checks) are author-reported and were not re-verified by this review.

On "run the A/B test": per the repo's A/B policy this is an execution refactor with no speed claim, so a speed A/B is not the required evidence here; the relevant gate is output equivalence (tiny-model CUDA sync/async comparisons and replay variants, provided). The disclosed BF16 multi-request multi-stream token divergence is the qualification gap that must be resolved before any async quality or throughput claim.

@bjf-frz

bjf-frz commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

run the A/B test

  • Test results updated in pr description.
  • A faulty implementation of FA4 causes the prefill phase to take an extremely long time in long-sequence, high-concurrency scenarios. A fix is in progress.

Signed-off-by: bjf-frz <frz123db@gmail.com>

@hsliuustc0106 hsliuustc0106 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed c0d7a5a against base 6e8abbc, focusing on changes since e525f64. Found one new P2, detailed inline.

Both earlier P2 findings remain open after rechecking this head:

Validation: Ruff lint, Python formatting, syntax parsing across all 49 Python files, and git diff --check passed. Runtime tests were not run because isolation for executing the fork's code remains unavailable on the reviewer host. BF16 equivalence remains unresolved; campaign artifacts and before/after single-request profiling are not attached.

Comment thread vllm_lt/kernels/flash_attention.py Outdated
major, minor = capability
automatic = 4 if major in (10, 12) else 3 if major == 9 else 2
selected = automatic if backend == "flash_attn" else int(backend.rsplit("_", 1)[1])
supported = {2: major in (8, 9), 3: major == 9, 4: major in (9, 10, 12)}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] Reject SM12 for the pinned FA4 paged backend

The selector accepts SM12 and automatically chooses FA4, but the pinned flash-attn-4==4.0.0b30 implementation explicitly rejects page tables on SM12. In flash_attn/cute/interface.py:1368, its SM12 branch checks assert page_table is None with the message "Paged KV not supported on SM 12.0 in this PR". Both attention paths in this adapter always supply a page table, so this configuration fails during automatic KV sizing or first inference. Reject this combination with guidance to use Triton until a compatible paged implementation is available, and update the architecture-selection test accordingly.

Verified by static inspection of the exact pinned FA4 wheel, with its SHA-256 checked against PyPI metadata.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[P2] Reject SM12 for the pinned FA4 paged backend

The selector accepts SM12 and automatically chooses FA4, but the pinned flash-attn-4==4.0.0b30 implementation explicitly rejects page tables on SM12. In flash_attn/cute/interface.py:1368, its SM12 branch checks assert page_table is None with the message "Paged KV not supported on SM 12.0 in this PR". Both attention paths in this adapter always supply a page table, so this configuration fails during automatic KV sizing or first inference. Reject this combination with guidance to use Triton until a compatible paged implementation is available, and update the architecture-selection test accordingly.

Verified by static inspection of the exact pinned FA4 wheel, with its SHA-256 checked against PyPI metadata.

resolved

Signed-off-by: bjf-frz <frz123db@gmail.com>
Signed-off-by: bjf-frz <frz123db@gmail.com>
Signed-off-by: bjf-frz <frz123db@gmail.com>
Signed-off-by: bjf-frz <frz123db@gmail.com>
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.

2 participants