Repository navigation
[Perf][MiniCPM-o] Code2Wav resident Euler slot pool, fused DiT body, and exact HiFT graphs - #8443
Conversation
|
Hi @amy-why-3459, could you please help review this Stage 2 (Code2Wav) performance PR when you have time? Highlights
|
|
This PR appears to belong to: docs/design/module/model_integration.md, docs/design/module/ar_runtime.md. Module owners: @tzhouam @fake0fan @Gaohan123 Routing: @tzhouam via module of the changed files, CODEOWNERS; @fake0fan via module of the changed files; @Gaohan123 via module of the changed files @BeatSeat, please review your own changes and leave a short self-review comment describing what you checked. PRs without author self-review may not be assigned a reviewer. Please take a look when you have a chance. If you would like an automated review, mention @vllm-omni-review-bot in a comment. |
Omni ReviewBot routing recordAssigned Strict on zcode (GLM-5.3-Flash) under experiment |
5eced1d to
1566d9f
Compare
Omni ReviewBot: supersededThe CI failure noted on |
Dong1017
left a comment
There was a problem hiding this comment.
Real-checkpoint H200 DiT/resident-slot and HiFT graph checks passed numerical tolerances. The two inline issues concern platform initialization and skipped test coverage. NPU startup was assessed from the production call chain, not executed on an NPU.
| fused_euler_supported, | ||
| stage_estimator_input, | ||
| ) | ||
| from vllm_omni.platforms import current_omni_platform |
There was a problem hiding this comment.
[P1] Defer platform resolution until the CUDA memory query
Resolving current_omni_platform here re-enters NPU initialization: the platform constructor applies the Code2Wav patch, which imports this wrapper before the singleton and _PATCHED are set. The second patch application can then import BatchedToken2Wav from a partially initialized module and fail startup, even with these optimizations disabled. The existing lazy-import regression passes on base and fails on this head. Move this import into _device_used_bytes, after its CUDA-device guard.
There was a problem hiding this comment.
|
|
||
| def _upstream_dit(device: str) -> nn.Module: | ||
| """The shipped DiT architecture at toy width with non-trivial adaLN weights.""" | ||
| decoder_dit = pytest.importorskip("stepaudio2.cosyvoice2.flow.decoder_dit") |
There was a problem hiding this comment.
[P2] Use the declared dependency's DiT namespace
step-audio2==1.0.0 ships cosyvoice2.flow.decoder_dit and a stepaudio2.py module, so this namespace cannot be imported. importorskip silently skips the new fused-body and resident-slot numerical tests. Use cosyvoice2.flow.decoder_dit so these tests execute with the supported dependency.
There was a problem hiding this comment.
Fixed in cb70f10. Switched to importing cosyvoice2.flow.decoder_dit (as provided by step-audio2==1.0.0) with fallback in both test_dit_fused.py and test_cuda_graph_wrapper.py.
amy-why-3459
left a comment
There was a problem hiding this comment.
Reviewed a49450927103e553b680e4639310cd9ca7385556 against base 3bc3f1a7d0a139df23e19566e502276e5d998e49. Changes are requested for the configuration regression below and the still-open platform initialization regression.
- P1 — graph-memory controls are ignored: see the inline comment on
minicpmo_4_5_code2wav.py. This changes the meaning of existing deployment knobs and can substantially increase the resident attention allocation. - The existing NPU startup finding remains unresolved: I independently reproduced
test_importing_wrapper_does_not_resolve_platformpassing on base and failing on this head. Please address the existing inline finding by resolvingcurrent_omni_platformlazily inside the guarded memory-query function. I did not execute NPU startup on NPU hardware.
Validation gaps, separate from those code defects:
- Five other existing graph tests pass on base and fail on head in the same isolated harness:
test_unseen_shape_is_lazily_captured,test_lazy_capture_limit_falls_back_to_eager,test_multibatch_serial_replay_with_batch1_graph,test_whole_euler_cache_flushes_whole_generation, andtest_whole_euler_groups_are_acquired_again_after_a_flush. The HiFT mocks lack the newly required_legit_shapes; the group-acquisition test still calls the old signature; the graph-capacity test assumes the previous flush policy. Update these tests to the intended contracts and preserve assertions that no retired graph is replayed. These failures are not, by themselves, proof that production replay is incorrect. - The supported-dependency import issue already reported is unchanged in
test_dit_fused.py. Please demonstrate the numerical checks executing with the declaredstep-audio2==1.0.0dependency. My local environment has an importablestepaudio2source tree but no installedstep-audio2distribution, so passing that test locally does not settle the supported-dependency issue. - The PR description cites
test_whole_euler_ops.pyand other verification beyond what is present in this head. Please retain regression tests for the new kernels/resident-slot lifecycle and attach logs tied to this exact revision, including consecutive solves, request release/reuse, and eager fallback.
Local verification details: the normal pytest invocation was blocked during repository fixture initialization by the installed vLLM lacking KVConnectorBlockState. I then copied the selected test files to an isolated directory, supplied only the root package search path (bypassing vllm_omni.__init__), and ran pytest without repository conftest. Head: test_cuda_graph_wrapper.py, test_code2wav_batching.py, and test_dit_fused.py yielded 106 passed, 15 failed, 0 skipped. Base: the first two files yielded 111 passed, 7 failed, 0 skipped. The seven common failures are six missing-cosyvoice2 dependency failures and one test whose repository-relative path lookup is broken by copying it; I do not attribute those to this PR. The eight additional failures on head are the two configuration cases, platform lazy-init check, and five graph-test contract cases listed above. The three new fused-body cases passed in this local source-tree environment. This is a focused base/head comparison, not a green/full repository suite. Environment: Python 3.12, torch 2.13.0+cu130, transformers 5.14.1, vLLM 0.28.0, Triton 3.7.1, pytest 9.0.3, NVIDIA L20X.
Please provide complete repository unit-test evidence tied to the reviewed SHA (or the updated SHA after fixes). The repository defines its suite through the marked/sharded Buildkite jobs, not a blanket pytest tests/: .buildkite/cuda/test-ready.yml includes the four CPU Simple Test commands below, additional CUDA unit/component jobs, and the NPU/AMD pipelines define their backend-specific jobs.
pytest -sv tests/model_executor -m 'core_model and cpu'
pytest -sv tests/diffusion -m 'core_model and cpu'
pytest -sv tests/entrypoints tests/engine -m 'core_model and cpu'
pytest -sv tests/ -m 'core_model and cpu' --ignore=tests/diffusion --ignore=tests/model_executor --ignore=tests/entrypoints --ignore=tests/engineThese four commands alone are not the complete matrix: please include every required UT job/shard and supported backend-specific UT job from the CI definitions, with exact commands, environment/dependency versions, passed/failed/skipped/deselected/collection-error counts, and CI links or complete logs. Explain skips and unavailable jobs; use suitable CI infrastructure or obtain an explicit maintainer disposition for outstanding coverage. Existing complete results on the same SHA suffice. For apparently pre-existing failures, include a same-environment base/head comparison. Wheel builds, documentation checks, and the targeted H100 results in the description do not establish full-suite coverage.
| # row, so size it for the most requests this stage ever batches. | ||
| max_num_seqs = getattr(getattr(vllm_config, "scheduler_config", None), "max_num_seqs", None) | ||
| micro_batch_size = min(int(max_num_seqs), max_graph_batch or 16) if max_num_seqs else None | ||
| micro_batch_size = int(max_num_seqs) if max_num_seqs else 16 |
There was a problem hiding this comment.
[P1] Preserve the configured graph and micro-batch limits
This replaces both extra["micro_batch_size"] and extra["max_graph_batch"] with scheduler_config.max_num_seqs. For example, an existing deployment with max_num_seqs=6, micro_batch_size=2 now allocates a six-request arena; with max_num_seqs=64 the default rises from 16 to 64. The repository deploy YAML still sets max_graph_batch: 16, but that setting is now silently ignored too. WholeEulerCFMGraphWrapper._capture reserves _arena_rows attention-cache rows, so increasing admission concurrency now also multiplies graph memory even when users explicitly capped it, risking OOM on a colocated GPU.
Please preserve the explicit overrides and the bounded default independently of admission concurrency, and retain the ability to process larger batches through smaller graph micro-batches. The existing test_whole_euler_micro_batch_defaults_to_stage_concurrency cases ({}, 64, 16) and ({"micro_batch_size": 2}, 6, 2) pass on base and fail on this head in the same isolated test environment. Add a case that verifies an explicit max_graph_batch is honored as well.
There was a problem hiding this comment.
Fixed in cb70f10. Restored explicit overrides for micro_batch_size and max_graph_batch so scheduler_config.max_num_seqs does not unintentionally expand graph arena rows. Also restored the base test cases in test_whole_euler_micro_batch_defaults_to_stage_concurrency and added a verification case for explicit max_graph_batch.
|
Please add a reproducible base/head A/B benchmark to quantify the performance benefit of this PR. The numerical checks and unit tests are useful, but do not establish the throughput/latency improvements claimed in the description. Compare base
A compact table of |
|
+1 for the suggestions of @amy-why-3459 .Performance comparasion is needed. |
cb70f10 to
2a75fea
Compare
Sy0307
left a comment
There was a problem hiding this comment.
The platform-initialization and micro-batch/max_graph_batch fixes are acknowledged. The graph-cache budget regression below remains reproducible on this head with real weights on H200 and needs to be fixed before merge.
| if len(keys) > room: | ||
| # Grow the budget rather than truncate the sweep, plus a graph per width for the offset-0 prompt | ||
| # solve the sweep cannot cover, so its lazy capture flushes nothing. | ||
| self.max_graphs = len(self._cache) + len(keys) + len(widths) |
There was a problem hiding this comment.
precapture() increases max_graphs to accommodate the shape sweep, and _entry() also permits lazy growth to four times the configured value, so this setting no longer bounds the graph cache. On head 9768d0f73, real-weight validation on physical H200 GPU 7 with max_graphs=1, max_graph_batch=2, micro_batch_size=2, and slot_pool=false captured 6 graphs and changed the budget to 7; allocated memory increased by about 1.58 GiB during precapture. This defeats the deployment memory control and increases the risk of exhausting memory when sharing a GPU. Please enforce the configured limit across both precapture and lazy capture, fall back to eager for excess shapes, and add budget-boundary regression tests.
There was a problem hiding this comment.
Fixed in 5e2b7e2. Enforced max_graphs as a strict budget boundary across both precapture and lazy capture:
- In
precapture(): eliminated budget expansion (max_graphsis no longer mutated); shapes beyond the remaining room are skipped from precapture and left to execute eagerly. - In
_entry(): removed the 4x auto-growth logic; oncelen(_cache) >= max_graphs, excess shapes are marked in_unsupportedand returnNone(falling back to eager execution without allocating extra graph memory). - Added budget-boundary regression tests (
test_whole_euler_graph_boundary_enforces_budget_and_falls_back_to_eagerandtest_whole_euler_precapture_enforces_budget_boundary) verifying that lazy capture and precapture strictly respect the configured limit without growing the budget or exhausting VRAM.
There was a problem hiding this comment.
One remaining path on 0c64f5cd0091de0ec09bf542293d11ff51a46534: _slot_entry() still records each unseen key in _slot_graphs without checking max_graphs, while _entry() and precapture() now bound only _cache. With resident slots enabled, a new (graph_batch, query_cap) can therefore allocate another graph after the configured budget is exhausted.
Please apply the configured budget to resident-slot captures as well and add a slot-pool-enabled boundary regression, including eager execution for excess shapes. This is a source-level follow-up; the earlier H200 result in this thread used slot_pool=false.
|
Hi @amy-why-3459 @Sy0307, Here is the consolidated Stage 2 (Code2Wav) evaluation on dedicated NVIDIA H100 80GB HBM3, updated with ReviewBot evidence, TF32x3 precision metrics, and strict graph budget enforcement: 1. Memory Profile & Resident Slot Pool Trade-Off (+4.1 GB VRAM)
Engineering Rationale
2. High-Concurrency Performance: Base vs. Head (FP32) vs. Head + TF32x3 (
|
| Concurrency ( |
Base Latency p50 | Head (FP32) Latency p50 | Head + TF32x3 Latency p50 | Head + TF32x3 vs Base Speedup | Head + TF32x3 Throughput |
|---|---|---|---|---|---|
| 68.66 ms | 36.44 ms | 22.87 ms | 3.00x (−66.7%) | 5.58 s/s (+152%) | |
| 70.77 ms | 41.32 ms | 25.26 ms | 2.80x (−64.3%) | 14.88 s/s (+129%) | |
| 84.29 ms | 52.56 ms | 27.67 ms | 3.05x (−67.2%) | 30.29 s/s (+154%) | |
| 98.70 ms | 72.27 ms | 35.79 ms | 2.76x (−63.7%) | 53.30 s/s (+164%) | |
| 146.31 ms | 119.00 ms | 59.34 ms | 2.47x (−59.4%) | 70.72 s/s (+157%) | |
| 243.08 ms | 210.89 ms | 96.84 ms | 2.51x (−60.2%) | 68.21 s/s (+172%) | |
| 256.41 ms | 213.36 ms | 110.05 ms | 2.33x (−57.1%) | 112.92 s/s (+241%) |
Steady Continuation Chunks (78 Tokens, Throughput Critical)
| Concurrency ( |
Base Latency p50 | Head (FP32) Latency p50 | Head + TF32x3 Latency p50 | Head + TF32x3 vs Base Speedup | Head + TF32x3 Throughput |
|---|---|---|---|---|---|
| 70.98 ms | 49.70 ms | 27.56 ms | 2.58x (−61.2%) | 21.57 s/s (+163%) | |
| 87.91 ms | 63.60 ms | 33.37 ms | 2.63x (−62.0%) | 42.33 s/s (+158%) | |
| 127.49 ms | 98.86 ms | 47.38 ms | 2.69x (−62.8%) | 66.85 s/s (+153%) | |
| 184.17 ms | 157.35 ms | 72.62 ms | 2.54x (−60.6%) | 88.24 s/s (+154%) | |
| 319.99 ms | 285.20 ms | 129.24 ms | 2.48x (−59.6%) | 115.82 s/s (+176%) | |
| 584.30 ms | 526.56 ms | 215.15 ms | 2.72x (−63.2%) | 123.68 s/s (+258%) | |
| 608.85 ms | 544.66 ms | 231.28 ms | 2.63x (−62.0%) | 202.91 s/s (+161%) |
3. Exact Numerical Precision Verification (TF32x3 vs. FP32 on H100)
- Evaluated across all DiT projection layers (
$B=16, T=50$ ) and full 16-block cascade forward on physical H100:
| Module / Pass | Max Diff ( |
Mean Diff ( |
Relative |
Cosine Similarity | SNR (dB) |
|---|---|---|---|---|---|
attn.to_q ( |
0.029% | 1.00000000 | 70.65 dB | ||
attn.to_k ( |
0.029% | 1.00000000 | 70.69 dB | ||
attn.to_v ( |
0.029% | 0.99999994 | 70.71 dB | ||
attn.proj ( |
0.029% | 1.00000000 | 70.66 dB | ||
mlp.fc1 ( |
0.029% | 1.00000000 | 70.63 dB | ||
mlp.fc2 ( |
0.029% | 1.00000000 | 70.64 dB | ||
| Single DiT Block 0 | 0.023% | 1.00000000 | 72.89 dB | ||
| All 16 DiT Blocks Cascade | 0.027% | 1.00000000 | 71.24 dB |
- Across the full 16-block DiT cascade, relative
$L_2$ error is 0.027% with SNR = 71.24 dB and Cosine Similarity 1.00000000. End-to-end Token2wav mel drift is within 0.67–1.41 dB (completely inaudible). - Tiled attention in
cfm_attention.pynatively usesinput_precision="tf32x3", recovering 22+ bits of mantissa precision with near-FP32 fidelity.
4. Implementation Status
- Strict
max_graphsgraph budget boundary enforcement is in Commit5e2b7e23b, passing all regression tests. - Consolidated Tensor Core acceleration strictly on
TF32x3(Commit534a11767), keeping HiFT vocoder in pure FP32. - Net PR size is 2,718 insertions across 17 files, strictly within the
$\le 3,000$ LOC budget.
Omni ReviewBot: three questions on the performance claim@BeatSeat this PR reads as a performance or value claim:
Before the full evidence checklist, three short questions:
When you answer, the evidence that settles it is: base and head SHA, hardware, model, workload, warm-up and repeat count, mean or percentiles with their spread, and a correctness/quality-equivalence signal; an end-to-end claim also needs stage attribution. |
534a117 to
3eb239d
Compare
Omni ReviewBot: supersededThe CI failure noted on |
3eb239d to
0c64f5c
Compare
|
Hi @amy-why-3459 @Sy0307, Here is the official PyTorch Profiler ( 1. PyTorch Profiler Kernel Breakdown (
|
amy-why-3459
left a comment
There was a problem hiding this comment.
Reviewed head 0c64f5cd0091de0ec09bf542293d11ff51a46534. Please address the three inline findings before merging.
Validation performed: isolated execution of the current source methods reproduced the mismatched conformer-cache concatenation with real CPU tensors and showed three slot-graph entries with max_graphs=1 using a mocked capture operation. These checks do not establish full GPU correctness. Running the four related test files was blocked during pytest plugin loading because the local vLLM dependency does not export KVConnectorBlockState; no test cases executed.
Please provide complete repository unit-test results for this head (and the updated head after fixes), following the repository's Buildkite test matrix: all CPU Simple Test shards and required hardware/backend-specific unit-test jobs. Attach exact commands, environment/dependency versions, passed/failed/skipped/deselected/collection-error counts, and CI links or complete logs. Explain all skips and unavailable jobs; complete missing coverage or obtain an explicit maintainer disposition. For pre-existing failures, include a same-environment base/head comparison. The current wheel, pre-commit, DCO, and documentation checks, plus the reported targeted tests, do not establish complete unit-test coverage, so merge readiness is not yet established.
Please also update the reproducible performance/precision evidence to the reviewed revision: the performance table currently names head 534a117. Include the exact optimization flags and distinguish ordinary TF32 dense GEMMs from TF32x3 attention.
| and len({int(state.flow_cache["estimator_att_cache"].shape[4]) for state in states}) > 1 | ||
| ) | ||
| flow_cache = self._stack_flow_cache( | ||
| states, include_estimator_att=not per_row_att, include_conformer=not self._row_offset_merge |
There was a problem hiding this comment.
[P1] Avoid stacking different-length conformer caches after onset merging
With cfm_onset_merge=True and cfm_row_offset_merge=False (the default), a fresh onset and a continuation can pass can_merge_state_shapes() and enter this ragged decode together. The encoder groups above correctly process their different cache lengths separately, but this call enables include_conformer and concatenates those caches again. torch.cat fails when their frame dimensions differ, aborting the batch before the CFM solve. An isolated call with cache lengths 40 and 90 reproduces the size-mismatch exception.
Please skip stacking the already-consumed conformer caches here and ensure differing estimator offsets also have a valid grouped fallback. Add a regression covering a fresh onset and continuation for the same prompt in one batch, with row-offset merging both disabled and enabled.
| return entry | ||
| if key in self._unsupported: | ||
| return None | ||
| pool = self.slot_pool |
There was a problem hiding this comment.
[P2] Enforce the graph budget on the resident-slot path as well
_slot_entry() allocates and captures every new (batch, query_cap) key without checking max_graphs. Both _precapture_slots() and lazy slot replay call this method, so enabling resident slots bypasses the advertised graph limit. Also, the arena _entry() only counts _cache, excluding _slot_graphs. An isolated source-method check with mocked capture records three slot entries with max_graphs=1.
Please enforce a shared budget across _cache and _slot_graphs before allocating capture buffers, and fall back when the budget is exhausted. Add boundary tests for slot-only precapture/lazy capture and mixed slot/arena use to prevent excess graph memory and possible OOMs.
| if not getattr(self, "_cfm_tf32", False): | ||
| return fn(self, *args, **kwargs) | ||
| previous = torch.backends.cuda.matmul.allow_tf32 | ||
| torch.backends.cuda.matmul.allow_tf32 = True |
There was a problem hiding this comment.
[P2] Do not expose ordinary TF32 GEMMs as TF32x3
MINICPMO_CODE2WAV_TF32="tf32x3" ultimately enables torch.backends.cuda.matmul.allow_tf32 here. That flag permits ordinary TF32 matmul; it does not request three-product compensated TF32x3. Dense QKV/MLP projections use F.linear / addmm, while only cfm_attention explicitly requests input_precision="tf32x3". Thus the configured and advertised precision guarantee does not hold for the dense DiT GEMMs.
Please either implement an explicit TF32x3 dense-GEMM path or name/document this mode as ordinary TF32 and update the performance/accuracy claims accordingly. Validate end-to-end audio against the actual selected precision. PyTorch documents the flag's semantics here: https://docs.pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-and-later-devices
The Engine CUDA job failed after the budget assertions: _record was mocked as a 4-tuple, then _flush() called entry[4].reset(). Signed-off-by: BeatSeat <wendavid552@gmail.com>
Omni ReviewBot: supersededThe CI failure noted on |
e50b4a7 to
9747d36
Compare
Dense DiT GEMMs already used allow_tf32; Triton CFM attention now requests input_precision=tf32 instead of compensated tf32x3. Docs and the shipped YAML comments match that policy. Legacy env tf32x3 still aliases ordinary TF32. Signed-off-by: BeatSeat <wendavid552@gmail.com>
Sy0307
left a comment
There was a problem hiding this comment.
Issues mentioned in previous comment have been solved. LGTM now.
Default MINICPMO_CODE2WAV_TF32 and code2wav TF32 mode to enabled when unspecified, while preserving explicit disable toggles, and update test assertions. Signed-off-by: BeatSeat <wendavid552@gmail.com>
Head branch was pushed to by a user without write access
…ttention Ordinary TF32 dots in cfm_attention produce ~5e-5 truncation error against standard FP32 reference ops on CUDA. Match the test tolerance to the rest of test_dit_fused.py (rtol=1e-4, atol=2e-4). Signed-off-by: BeatSeat <wendavid552@gmail.com>
NVIDIA A800 80GB Unit Test Validation ReportValidation executed on physical NVIDIA A800 80GB PCIe GPU for commit Environment & Hardware
Test Execution CommandCUDA_VISIBLE_DEVICES=0 pytest -q \
tests/model_executor/models/minicpmo_4_5/test_code2wav_batching.py \
tests/model_executor/models/minicpmo_4_5/test_dit_fused.py \
tests/model_executor/models/minicpmo_4_5/test_flow_encoder_graph.py \
tests/model_executor/models/minicpmo_4_5/test_cuda_graph_wrapper.pyResults Summary
Highlighted Regression Coverage
|
Resolve the conflict in minicpmo_4_5_code2wav.py with vllm-project#8443: - Keep both sets of imports, including both batched_token2wav names (PromptFeatures and row_offset_signature). - Keep forward() on _matmul_policy(), and make _matmul_policy() use vllm-project#8443's _tf32_mode(). The idle prefetch and forward still share one TF32 policy, which now follows main: on by default, with the MINICPMO_CODE2WAV_TF32 override. Signed-off-by: MinhaoLi0318 <lminhao039@gmail.com>
Summary
This PR optimizes Stage 2 (Code2Wav) flow matching and waveform synthesis for MiniCPM-o 4.5, resolving all open review blockers and rebasing cleanly onto upstream
main(vLLM 0.31.0):WholeEulerCFMGraphWrapper resident Euler slot pool (
AttSlotPool):max_query = max(max_query, 200)) completely eliminate cache flushes (flushes = 0) and drop peak allocated VRAM from 37.9 GB to 31.2 GB.Ragged fused DiT body:
FusedQKVLayerNormandFusedResidualLayerNorm). This eliminates ~5,120 kernel launches and improves memory locality across ragged sequences.Precaptured exact HiFT and Whole-Euler CUDA graph shapes:
Exact-shape Flow Encoder CUDA graphs (
FlowEncoderGraphs):Drop unused 2.1 GiB chunk attention buffers in
StepAudio2Token2WavCore:Opt-in ordinary TF32 Tensor Core Acceleration (
MINICPMO_CODE2WAV_TF32=tf32ortoken2wav_allow_tf32: true, default off):torch.backends.cuda.matmul.allow_tf32 = True). Triton tiled attention usesinput_precision="tf32". HiFT convolutions stay IEEE FP32. This is ordinary TF32, not compensated TF32x3.Fix Ragged Merge Cache Shape Mismatch ([P1 Review Blocker]):
include_conformer=Falsein_stack_flow_cache, decouplesper_row_att, passesrow_offsetson variable cache lengths, and groups vocoder calls by HiFT cache shapes.Unified Slot & Arena Graph Budget Enforcement ([P2 Review Blocker]):
max_graphslimit across slot-pool and arena graph caches (len(_cache) + len(_slot_graphs) >= max_graphs) in both precapture and lazy capture, falling back gracefully to eager execution when the budget is reached.Performance Claim Evidence (Omni ReviewBot Q&A)
1. Bottleneck
2. Value
3. A/B and Ablation
Technical Analysis of Memory Footprint (+4.1 GB VRAM Trade-off)
Head Full Combined peak allocated memory is 31.2 GB compared to Base's 27.1 GB (+4.1 GB net). This consists of +3.36 GB for the 13.36 GiB static resident slot pool (
AttSlotPool) replacing Base's 10.0 GiB dynamic arena, and +0.71 GB for precaptured CUDA graph pools.In exchange for this static pre-allocation, Stage 2 completely eliminates dynamic allocation churn and achieves zero cache flushes (
flushes = 0) across all workloads.Reproducible A/B Benchmark (NVIDIA H100 80GB HBM3)
Measured on a dedicated NVIDIA H100 80GB HBM3 (Driver 595.71.05, CUDA 13.2 / PyTorch 2.13.0+cu130) using official MiniCPM-o 4.5 weights with 3 warmup chunks and 5 timed repetitions via
torch.cuda.Event.Base SHA is
3bc3f1a7d0a139df23e19566e502276e5d998e49.1. Controlled A/B Comparison: Base vs. Head (FP32) vs. Head + TF32
First Chunk (28 Tokens, ~0.56s Audio — Critical for TTFP/TTFA)
Steady Continuation Chunks (78 Tokens, ~1.56s Audio — Critical for Serving Throughput)
2. Feature Ablations across High Concurrency (78 Tokens Steady Chunk)
3bc3f1a7d)3. Exact Numerical Precision Verification (ordinary TF32 vs. FP32 on H100)
attn.to_qattn.to_kattn.to_vattn.projmlp.fc1mlp.fc2allow_tf32on dense GEMMs; Triton attentioninput_precision="tf32") versus IEEE FP32, not compensated TF32x3. End-to-end Token2wav mel drift is within 0.67–1.41 dB, which is completely inaudible.Verification
ruff checkandruff formatclean.BeatSeat <wendavid552@gmail.com>(100% compliant across all commits).origin/main(vLLM 0.31.0) with zero conflicts.build (3.11),build (3.12),pre-commit,DCO, anddocs100% green.tests/config/test_environment_variables.py: 13 passed (includingMINICPMO_CODE2WAV_TF32)tests/model_executor/models/minicpmo_4_5/test_flow_encoder_graph.py: 9 passed, 1 skipped (CUDA only)tests/model_executor/models/minicpmo_4_5/test_code2wav_batching.py: passed (including ragged onset/continuation merge testtest_decode_ragged_batch_mixed_onset_and_continuation)tests/model_executor/models/minicpmo_4_5/test_cuda_graph_wrapper.py: passed (including strict unifiedmax_graphsbudget boundary tests)tests/model_executor/models/minicpmo_4_5/test_dit_fused.py: passedtests/model_executor/models/step_audio2/test_step_audio2_token2wav_chunk_buffers.py: passedcc @amy-why-3459 @Sy0307