Repository navigation
[#18609][fix] Do not require encoder tokens for cross prefix reuse - #19450
100milliongold wants to merge 1 commit into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
WalkthroughThe capacity scheduler now avoids unset encoder-token optional access during cross-KV analysis. Two NVFP4 CuTe DSL tests now state that GEMM+SwiGLU fusion supports SM100 and SM103. ChangesCapacity scheduler guard
NVFP4 skip messages
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix · Severity of issue fixed: Medium Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2
✨ Finishing Touches 💡 1
Comment |
|
Tested on the original hardware today (RTX 5070 Ti / sm_120,
So the diagnosis holds: with everything else sized correctly (run 5 vs run 4), block reuse on vs off is exactly the difference between the scheduler crash and a working transcription. #19450 is aimed at the right thing. Run 2 exposes a second, independent problem that anyone will hit right after #19450 lands: Update — built and tested the PR itself. I applied the
So the guard does what it says on sm_120: block reuse stays on, the request reaches the model, and Whisper transcribes. Run 1 is the second, independent bug described above, now the only thing between a stock config and a working transcription. |
crazydemo
left a comment
There was a problem hiding this comment.
Review summary - Approve
Reviewed the full diff; no blocking or major issues found.
Automated review by NVCortex Lite, run by @crazydemo.
|
Thank you — that is the hardware verification this PR did not have. The body says plainly that I had not run it on a GPU and that it carries no regression test. Your runs 4 and 5 isolate block reuse as the only variable, and the rc25 + #19450 build closes the gap on the guard itself. I have updated the description so that caveat no longer stands unqualified. On the second problem: I read max_seq_len = fallback_max_seq_len or self._max_seq_len
max_input_len = getattr(self._llm_args, "max_input_len", None)
if isinstance(max_input_len, int) and max_input_len > 0:
max_seq_len = max_input_len # decoder limit overwrites encoder length
encoder_limit = self._get_config_int_attr(config, (...))
if encoder_limit is not None:
max_seq_len = min(max_seq_len, encoder_limit) # can only shrink it
For Please do open it separately. It is a different layer — Python pool sizing, not the C++ capacity scheduler — and it should not wait on this one. If you would rather not, say so and I will file it using your run 2 as the reproduction, credited to you. |
|
Agreed on the override being the defect — my patch removes it rather than just adding the attribute; the encoder's declared limit becomes authoritative and |
…ared length, not max_input_len For encoder-decoder models the cross KV pool was sized from llm_args.max_input_len (default 1024), which overwrote the engine length unconditionally; the config-derived encoder limit was then applied with min() and could only shrink it further. Whisper's encoder emits max_source_positions=1500 frames, so every request failed in prepare_cross_metadata with "max KV cache length of input sequences (1500) exceeds the KV cache manager's maximum supported length (1024)". Treat the encoder's declared position limit (now including max_source_positions) as authoritative for the cross pool and fall back to max_input_len only when the model declares no encoder length. Fixes NVIDIA#19514. Verified on RTX 5070 Ti (sm_120) with openai/whisper-large-v3 (release 1.3.0rc25 + Signed-off-by: ravo42-maker <ravo42-maker@users.noreply.github.com> NVIDIA#19450): the default-argument reproduction from NVIDIA#18609 transcribes.
…ared length, not max_input_len For encoder-decoder models the cross KV pool was sized from llm_args.max_input_len (default 1024), which overwrote the engine length unconditionally; the config-derived encoder limit was then applied with min() and could only shrink it further. Whisper's encoder emits max_source_positions=1500 frames, so every request failed in prepare_cross_metadata with "max KV cache length of input sequences (1500) exceeds the KV cache manager's maximum supported length (1024)". Treat the encoder's declared position limit (now including max_source_positions) as authoritative for the cross pool and fall back to max_input_len only when the model declares no encoder length. Fixes NVIDIA#19514. Verified on RTX 5070 Ti (sm_120) with openai/whisper-large-v3 (release 1.3.0rc25 + Signed-off-by: ravo42-maker <ravo42-maker@users.noreply.github.com> NVIDIA#19450): the default-argument reproduction from NVIDIA#18609 transcribes.
The capacity scheduler dereferences the encoder unique tokens at three places,
guarded only by the presence of a cross KV cache manager with block reuse on:
auto uniqueTokens = *(req->getEncoderUniqueTokens().value());
None of the three checks that the optional holds anything. It does not for an
encoder whose input is features rather than tokens: mEncoderUniqueTokens is
derived from mEncoderTokens alone (llmRequest.h:2272), while Whisper passes
encoder_input_features (modeling_whisper.py:788) and leaves
encoder_input_tokens unset (llm_request.py:928-934).
enable_block_reuse defaults to true and an encoder-decoder run has to set
cross_kv_cache_fraction, which is what creates the cross manager, so both guard
conditions hold on a default run and every request throws in the scheduler
before the forward pass:
RuntimeError: bad optional access
Add the missing condition. Prefix reuse matches on token prefixes, so a
features-based encoder has nothing for analyzePrefixReuse to walk; leaving the
cross summary unset is the state beneficialToSkip and registerContributedBlocks
already handle, both of which test has_value() before reading it. That is also
the state those two see today whenever there is no cross manager, block reuse is
off, or the manager is variable-window.
The same header already distinguishes the two shapes of encoder input:
tryGetEncoderInputLen() returns from mEncoderInputFeatures or from
mEncoderTokens and nullopt when neither is present (llmRequest.h:663).
Signed-off-by: 김재억 <gadian88@gmail.com>
3b82b78 to
147eb25
Compare
Fixes #18609.
Problem
Every generation request against
openai/whisper-large-v3on the PyTorch backend dies in the C++ capacity scheduler, before the forward pass:capacityScheduler.cppdereferences the encoder unique tokens at three places, guarded only by the presence of a cross KV cache manager with block reuse on:None of the three checks whether the optional holds anything.
Why it is empty
mEncoderUniqueTokensis derived from encoder tokens alone (llmRequest.h:2272):Whisper's encoder input is features, not tokens:
modeling_whisper.py:788passes"encoder_input_features", andllm_request.py:928-934leavesencoder_input_tokensasNone. So the optional is never populated.enable_block_reusedefaults toTrue(llm_args.py:4106), and an encoder-decoder run must setcross_kv_cache_fraction, which is what creates the cross manager. Both guard conditions therefore hold on a default run, which is why the reporter saw it on every request. It is not architecture-specific; the reporter's SM120 box is not what selects this path.Change
Add the missing condition at each of the three sites.
Prefix reuse matches on token prefixes, so a features-based encoder has nothing for
analyzePrefixReuseto walk. LeavingcrossSummaryunset is the state its two consumers already handle:and it is the state they already see whenever there is no cross manager, block reuse is off, or the manager is variable-window.
The same header already distinguishes the two shapes of encoder input, which is what made the token-only assumption visible (
llmRequest.h:663):Verification
clang-format16.0.0, the version pinned in.pre-commit-config.yaml, reports no diff on the file.grepfinds three dereferences ofgetEncoderUniqueTokens().value()in this file; all three now sit behindhas_value().What I have not done
I did not reproduce the failure or run this patch myself. I have no SM120 GPU and no Whisper setup; the analysis is from reading
main, and the reporter's trace is what located it.Since writing that, @ravo42-maker has run it. They applied this hunk onto the
v1.3.0rc25tag (785c948, where all three sites are identical), built a wheel for120-real, and ranopenai/whisper-large-v3on an RTX 5070 Ti. Withmax_input_len=1500and block reuse left on, stock rc25 raisesbad optional accessand rc25 + this change transcribes the clip in 3.95 s. Their five-run table isolates block reuse as the only variable between the crash and a working run. See the comment below; the verification is theirs, not mine.I also have not added a regression test. The natural one is a capacity-scheduler unit test with a request carrying encoder input features and no encoder tokens, with a cross manager and block reuse enabled. I would rather add it in the shape this repository prefers than guess, so please say if you want it here.
Note beyond this fix
The reporter lists four further gaps in the same issue: no
/v1/audio/transcriptionsroute, no Whisper chat template shipped, no Whisper example or test in the container, and no release that currently provides a working Whisper path. This change addresses only the scheduler crash.Dev Engineer Review
tests/integration/defs/accuracy/test_llm_api_pytorch.py.capacityScheduler.cppfix. The scheduler crash fix is therefore not verified by this diff.QA Engineer Review
tests/integration/defs/accuracy/test_llm_api_pytorch.pywas modified.Per-File QA Perspective
tests/integration/defs/accuracy/test_llm_api_pytorch.py: The changes affect formatting only and do not add behavioral coverage. Verify that the file remains included in its applicable CI test list.