Repository navigation
[Bugfix][Quantization] Fix Humming FP8 linear for per-tensor/per-channel FP8 weights - #60163
Conversation
…nel weights Fp8LinearMethod and the online per-tensor/per-channel FP8 methods hand the kernel a (K, N) weight but drop its dim tags, so Humming treats it as (N, K) and fails to view it as int32. Retag the weight as CompressedTensorsW8A8Fp8 already does. In batch-invariant mode, call Humming's own apply instead of dequantizing a weight it has repacked. On Ampere, Humming is the only FP8 linear kernel in batch-invariant mode, so these models could not start with VLLM_BATCH_INVARIANT=1. Fixes vllm-project#60162 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: wenfei qi <ms1419344875@outlook.com>
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment Once the PR is approved or has the If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
|
This pull request has merge conflicts that must be resolved before it can be |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: wenfei qi <ms1419344875@outlook.com>
|
@yewentao256 @jinzhen-lin could you take a look? On Ampere with |
yewentao256
left a comment
There was a problem hiding this comment.
Thanks for the work!
Please do the following:
- full reproduce command line, error log in main and the same command line works in current PR
- e2e accuracy using
lm_eval... - e2e benchmark using
vllm bench serve...
When doing reproduce / e2e test, please attach with raw output log in case some agents fake the data. DO NOT use AI generated Table here
For example:
pytest tests/v1/kv_connector/extract_hidden_states_integration/test_extraction.py::test_extract_hidden_states_tp2
# main
| (EngineCore pid=1580) File "/usr/local/lib/python3.12/dist-packages/vllm/v1/engine/core.py", line 1070, in __init__
| (EngineCore pid=1580) super().__init__(
| (EngineCore pid=1580) File "/usr/local/lib/python3.12/dist-packages/vllm/v1/engine/core.py", line 160, in __init__
| (EngineCore pid=1580) self.scheduler: SchedulerInterface = Scheduler(
| (EngineCore pid=1580) ^^^^^^^^^^
| (EngineCore pid=1580) File "/usr/local/lib/python3.12/dist-packages/vllm/v1/core/sched/async_scheduler.py", line 14, in __init__
| (EngineCore pid=1580) super().__init__(*args, **kwargs)
| (EngineCore pid=1580) File "/usr/local/lib/python3.12/dist-packages/vllm/v1/core/sched/scheduler.py", line 277, in __init__
| (EngineCore pid=1580) self.kv_cache_manager = KVCacheManager(
| (EngineCore pid=1580) ^^^^^^^^^^^^^^^
| (EngineCore pid=1580) File "/usr/local/lib/python3.12/dist-packages/vllm/v1/core/kv_cache_manager.py", line 151, in __init__
| (EngineCore pid=1580) self.coordinator = get_kv_cache_coordinator(
| (EngineCore pid=1580) ^^^^^^^^^^^^^^^^^^^^^^^^^
| (EngineCore pid=1580) File "/usr/local/lib/python3.12/dist-packages/vllm/v1/core/kv_cache_coordinator.py", line 891, in get_kv_cache_coordinator
| (EngineCore pid=1580) return HybridKVCacheCoordinator(
| (EngineCore pid=1580) ^^^^^^^^^^^^^^^^^^^^^^^^^
| (EngineCore pid=1580) File "/usr/local/lib/python3.12/dist-packages/vllm/v1/core/kv_cache_coordinator.py", line 563, in __init__
| (EngineCore pid=1580) assert all(
| (EngineCore pid=1580) ^^^^
| (EngineCore pid=1580) AssertionError: Each KV cache group's real block_size must be divisible by hash_block_size. block_sizes=[544, 544, 544, 544, 181], hash_block_size=98464
| FAILED
| v1/kv_connector/extract_hidden_states_integration/test_extraction.py::test_extract_hidden_states_tp2
# Now
================= 1 passed, 23 warnings in 174.57s (0:02:54) =================="lm_eval --model local-completions --model_args "base_url=http://127.0.0.1:9256/v1/completions,model=$MODEL,num_concurrent=1024" --tasks gsm8k --trust_remote_code"
# Now
|Tasks|Version| Filter |n-shot| Metric | |Value | |Stderr|
|-----|------:|----------------|-----:|-----------|---|-----:|---|-----:|
|gsm8k| 3|flexible-extract| 5|exact_match|↑ |0.9212|± |0.0074|
| | |strict-match | 5|exact_match|↑ |0.9212|± |0.0074|
# main
|Tasks|Version| Filter |n-shot| Metric | |Value | |Stderr|
|-----|------:|----------------|-----:|-----------|---|-----:|---|-----:|
|gsm8k| 3|flexible-extract| 5|exact_match|↑ |0.9151|± |0.0077|
| | |strict-match | 5|exact_match|↑ |0.9158|± |0.0076|
`lm_eval run --model local-chat-completions --model_args base_url=http://127.0.0.1:9256/v1/chat/completions,model=$MODEL,num_concurrent=1024,seed=256 --tasks aime25 --trust_remote_code --apply_chat_template`
# now
|Tasks |Version|Filter|n-shot| Metric | |Value | |Stderr|
|------|------:|------|-----:|-----------|---|-----:|---|-----:|
|aime25| 0|none | 0|exact_match|↑ |0.7333|± |0.0821|
# main
|Tasks |Version|Filter|n-shot| Metric | |Value | |Stderr|
|------|------:|------|-----:|-----------|---|-----:|---|-----:|
|aime25| 0|none | 0|exact_match|↑ |0.7333|± |0.0821|vllm bench serve --model zai-org/GLM-5.2-FP8 --dataset-name random --host 127.0.0.1 --port 9256 --random-input-len 8192 --random-output-len 1 --request-rate inf --num-prompts 128 --num-warmups 16
# Now
============ Serving Benchmark Result ============
Successful requests: 128
Failed requests: 0
Benchmark duration (s): 3.08
Total input tokens: 1048576
Total generated tokens: 128
Request throughput (req/s): 41.50
Output token throughput (tok/s): 41.50
Peak output token throughput (tok/s): 76.00
Peak concurrent requests: 128.00
Total token throughput (tok/s): 340041.81
---------------Time to First Token----------------
Mean TTFT (ms): 1669.15
Median TTFT (ms): 1669.02
P99 TTFT (ms): 2724.38
-----Time per Output Token (excl. 1st token)------
Mean TPOT (ms): 0.00
Median TPOT (ms): 0.00
P99 TPOT (ms): 0.00
---------------Inter-token Latency----------------
Mean ITL (ms): 0.00
Median ITL (ms): 0.00
P99 ITL (ms): 0.00
==================================================
# main
============ Serving Benchmark Result ============
Successful requests: 128
Failed requests: 0
Benchmark duration (s): 3.23
Total input tokens: 1048576
Total generated tokens: 128
Request throughput (req/s): 39.63
Output token throughput (tok/s): 39.63
Peak output token throughput (tok/s): 50.00
Peak concurrent requests: 128.00
Total token throughput (tok/s): 324671.20
---------------Time to First Token----------------
Mean TTFT (ms): 1668.69
Median TTFT (ms): 1649.00
P99 TTFT (ms): 3126.43
-----Time per Output Token (excl. 1st token)------
Mean TPOT (ms): 0.00
Median TPOT (ms): 0.00
P99 TPOT (ms): 0.00
---------------Inter-token Latency----------------
Mean ITL (ms): 0.00
Median ITL (ms): 0.00
P99 ITL (ms): 0.00
==================================================|
@yewentao256 thanks, here are the runs. One RTX 3090 (sm86, driver 580.65.06, CUDA 13.0) on Runpod, image Reproduce. On Ampere with $ env VLLM_BATCH_INVARIANT=1 vllm serve neuralmagic/Meta-Llama-3-8B-Instruct-FP8 --port 8000 --max-model-len 4096 --gpu-memory-utilization 0.9 --no-enable-prefix-caching
# main
(EngineCore pid=9547) INFO 10-20 06:25:05 [__init__.py:808] Selected HummingFP8ScaledMMLinearKernel for Fp8LinearMethod
(EngineCore pid=9547) ERROR 10-20 06:25:09 [core.py:1543] File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/kernels/linear/scaled_mm/humming.py", line 82, in process_weights_after_loading
(EngineCore pid=9547) ERROR 10-20 06:25:09 [core.py:1543] convert_linear_layer_to_humming_standard(layer=layer, name_map=name_map)
(EngineCore pid=9547) ERROR 10-20 06:25:09 [core.py:1543] File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/layers/quantization/utils/humming/linear.py", line 39, in convert_linear_layer_to_humming_standard
(EngineCore pid=9547) ERROR 10-20 06:25:09 [core.py:1543] RuntimeError: self.stride(-1) must be 1 to view Float8_e4m3fn as Int (different element sizes), but got 4096
# PR
(EngineCore pid=12519) INFO 10-20 06:45:57 [__init__.py:808] Selected HummingFP8ScaledMMLinearKernel for Fp8LinearMethod
INFO: Application startup complete.Batch invariance on the PR server started with that command:
|
| # Transposed to (K, N); retag so layout-aware kernels see it. | ||
| layer.weight.input_dim = 0 | ||
| layer.weight.output_dim = 1 |
There was a problem hiding this comment.
Please take a look at this AI generated comment:
One question about the PTPC path: `Fp8PtpcOnlineLinearMethod` requires per-token FP8 activation quantization, but Humming appears to use a weight-only path with FP16/BF16 activations.
With this layout fix, PTPC can now successfully run through Humming. Does Humming preserve the requested activation quantization semantics here, or should we reject this combination as we already do for Marlin?There was a problem hiding this comment.
Good catch, it doesn't. HummingFP8ScaledMMLinearKernel.process_weights_after_loading calls prepare_humming_linear_layer_config() without an input schema. Humming then uses its default HummingInputSchema() (a_dtype=None), which means 16-bit activations with input quantization disabled. That is W8A16, the same as Marlin FP8. With the layout fix alone (the earlier version of this PR), PTPC would silently run as weight-only FP8 whenever it lands on Humming. That happens in two cases:
- on Ampere with
VLLM_BATCH_INVARIANT=1, where the other FP8 kernels need SM89+ and Marlin opts out; - with
--linear-backend humming.
970068e adds Humming to the existing Marlin check in Fp8PtpcOnlineLinearMethod.create_weights, so PTPC now raises the same ValueError, and the message names the selected kernel. The commit also drops the layout tag that the earlier version set in PTPC, since PTPC no longer reaches Humming. Fp8LinearMethod and fp8_per_tensor still run on Humming. They already fall back to W8A16 Marlin on GPUs without FP8 support, so Humming matches their existing behavior.
A40, nightly 00b6c4c5:
VLLM_BATCH_INVARIANT=1 vllm serve Qwen/Qwen3-8B --port 8000 --max-model-len 4096 --gpu-memory-utilization 0.9 --quantization fp8_per_channel
main
(EngineCore pid=5214) INFO 10-26 21:37:40 [__init__.py:817] Selected HummingFP8ScaledMMLinearKernel for Fp8PtpcOnlineLinearMethod
(EngineCore pid=5214) ERROR 10-26 21:37:42 [core.py:1574] RuntimeError: self.stride(-1) must be 1 to view Float8_e4m3fn as Int (different element sizes), but got 12288
layout fix applied to PTPC too, without the new check
(EngineCore pid=5749) INFO 10-26 21:38:22 [__init__.py:817] Selected HummingFP8ScaledMMLinearKernel for Fp8PtpcOnlineLinearMethod
(APIServer pid=5423) INFO 10-26 21:39:27 [entry.py:139] Starting vLLM server on http://0.0.0.0:8000
this PR
(EngineCore pid=6497) INFO 10-26 21:40:02 [__init__.py:817] Selected HummingFP8ScaledMMLinearKernel for Fp8PtpcOnlineLinearMethod
(EngineCore pid=6497) ERROR 10-26 21:40:03 [core.py:1574] ValueError: FP8 PTPC online quant requires a kernel that honors per-token activation quantization; HummingFP8ScaledMMLinearKernel is W8A16 weight-only. Requires SM89+ for Cutlass FP8 or ROCm MI3xx for rowwise scaled_mm.
this PR, without BI: vllm serve Qwen/Qwen3-8B --port 8000 --max-model-len 4096 --gpu-memory-utilization 0.9 --quantization fp8_per_channel --linear-backend humming
(EngineCore pid=7034) INFO 10-26 21:40:41 [__init__.py:817] Selected HummingFP8ScaledMMLinearKernel for Fp8PtpcOnlineLinearMethod
(EngineCore pid=7034) ERROR 10-26 21:40:42 [core.py:1574] ValueError: FP8 PTPC online quant requires a kernel that honors per-token activation quantization; HummingFP8ScaledMMLinearKernel is W8A16 weight-only. Requires SM89+ for Cutlass FP8 or ROCm MI3xx for rowwise scaled_mm.
main, same command with --linear-backend marlin, for comparison
(EngineCore pid=7536) INFO 10-26 21:41:17 [__init__.py:817] Selected MarlinFP8ScaledMMLinearKernel for Fp8PtpcOnlineLinearMethod
(EngineCore pid=7536) ERROR 10-26 21:41:18 [core.py:1574] ValueError: FP8 PTPC online quant requires a kernel that honors per-token activation quantization; MarlinFP8 is W8A16 weight-only. Requires SM89+ for Cutlass FP8 or ROCm MI3xx for rowwise scaled_mm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: wenfei qi <ms1419344875@outlook.com>
Humming's FP8 scaled-mm kernel runs W8A16, so PTPC online quant on it would drop the per-token FP8 activation quantization it asks for. Raise the same error as for Marlin and drop the now-unused weight retag in the PTPC method. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: wenfei qi <ms1419344875@outlook.com>
After vllm-project#62326, replace_parameter keeps the ModelWeightParameter, whose input_dim/output_dim are read-only. Use the layer attribute that the other FP8 methods and Humming now use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: wenfei qi <ms1419344875@outlook.com>
|
Merged main and added two commits:
Re-run on one A40 (sm86, driver 595.91.07, CUDA 13.2) on Runpod, image Unit test
main PR Llama-3-8B-Instruct-FP8 (
|
yewentao256
left a comment
There was a problem hiding this comment.
LGTM, thanks for the work!
|
✅ @Manfredss, CI is now available for this PR.
|
|
/ci run |
|
✅ Triggered Buildkite CI #97784 for commit |
|
/ci run |
|
✅ Triggered Buildkite CI #98070 for commit |
|
/ci run |
|
✅ Triggered Buildkite CI #98101 for commit ℹ️ This PR is 2 commits behind upstream |
|
/ci run |
|
✅ Triggered Buildkite CI #98201 for commit |
Overview
Fixes #60162. With the Humming FP8 linear kernel,
fp8checkpoints with per-tensor weight scales and onlinefp8_per_tensor/fp8_per_channelquantization crash while loading weights. On Ampere withVLLM_BATCH_INVARIANT=1, Humming is the only FP8 linear kernel left (Marlin opts out of batch-invariant mode), so these models cannot start in that mode. Humming FP8 is W8A16, sofp8_per_channel(PTPC, which asks for per-token FP8 activations) now gets the same clear error as on Marlin instead of running.Claims
Fp8LinearMethod(non-block) andFp8PerTensorOnlineLinearMethodload and run on Humming. GSM8K accuracy is within about one standard error of Marlin.VLLM_BATCH_INVARIANT=1, these models start and are batch-invariant under the default torch.compile + CUDA graphs.Fp8PtpcOnlineLinearMethodrejects Humming with the same error it already raises for Marlin.Validation
Latest run, after merging main (Oct 26): one A40 (SM86) on Runpod, official image
vllm/vllm-openai:nightly-00b6c4c565a8732c131e1f452d4a17803bb0842b, with this PR's diff applied to the installed package. Raw logs: PTPC, everything else.pytest tests/quantization/test_fp8.py::test_model_load_and_runhummingcase fails (stride error)VLLM_BATCH_INVARIANT=1--quantization fp8_per_tensor,VLLM_BATCH_INVARIANT=1--quantization fp8_per_channel,VLLM_BATCH_INVARIANT=1ValueError: Humming is W8A16 (same check as Marlin)--quantization fp8_per_channel,--linear-backend hummingValueError* From the earlier runs below; main's code path for these is unchanged.
GSM8K is lm_eval
gsm8k(5-shot, all 1319 questions), flexible-extract. For reference, on the Oct 20 RTX 3090 run, Llama-3-8B-Instruct-FP8 in default mode (Marlin on both sides) scored 0.7642 on main and with this PR.Earlier runs (Oct 6, Oct 15, Oct 20)
Rows that show
fp8_per_channelstarting on Humming predate the PTPC check.Oct 20, RTX 3090, nightly
e68efc68: repro, lm_eval and bench with raw logs in this comment.Re-validated after merging main (Oct 15). Head
671702b6e(main984c242ede+ this PR). Since main's latest wheel was not published yet, I used the precompiled extensions from388a610dfb(the only build change in between is the DeepGEMM tag) with torch 2.14.0, humming-kernels 0.1.17 and flashinfer 0.7.1, on an A40 inside the official nightly image's CUDA environment. Toggling between main and this PR was checked by SHA-256 of the touched files.VLLM_BATCH_INVARIANT=1fp8_per_tensor/fp8_per_channel,VLLM_BATCH_INVARIANT=1--linear-backend hummingtest_model_load_and_runwith this PR's testhummingfails (stride error)tests/quantization/test_fp8.pytest_fp8_reloadingfailuresGSM8K uses 500 questions, 5-shot.
The original validation below was done before the merge, on nightly
92044241.Run on the official image
vllm/vllm-openai:nightly-92044241a02f05de51420d654daf669de20b4691on an A40 (SM86), with the container's environment. The installed source files were checked against commit92044241by SHA-256 before testing. "This PR" means this diff applied to the installed package; the touched files are unchanged between92044241and this PR's base. Batch invariance uses the reproducer from #59086: request A alone, alone again, then batched with a longer request B; 48 greedy tokens; 3 rounds.neuralmagic/Meta-Llama-3-8B-Instruct-FP8(fp8, per-tensor)VLLM_BATCH_INVARIANT=1self.stride(-1) must be 1 to view Float8_e4m3fn as Int ... but got 4096Qwen/Qwen3-8B --quantization fp8_per_tensorVLLM_BATCH_INVARIANT=1Qwen/Qwen3-8B --quantization fp8_per_channelVLLM_BATCH_INVARIANT=1--linear-backend humming--quantization fp8_per_tensor--linear-backend hummingQwen/Qwen3-8B-FP8(block, control)VLLM_BATCH_INVARIANT=1GSM8K:
tests/evals/gsm8k/gsm8k_eval.py --num-questions 500(5-shot).test_model_load_and_runnow also runs withlinear_backend="humming"(pytest tests/quantization/test_fp8.py::test_model_load_and_run -v, same image):The rest of
tests/quantization/test_fp8.pygives the same result with and without this PR on this GPU. Threetest_fp8_reloadingcases fail on the A40 either way with identical errors, and are unrelated to this change.pre-commitpasses on the changed files.Details
Missing layout tag.
process_fp8_weight_tensor_strategyreturns the weight as a(K, N)transposed view, andFp8PerTensorOnlineLinearMethodtransposes it itself. Humming'sconvert_linear_layer_to_humming_standardreadslayer.processed_weight_dims(falling back to the parameter's checkpointinput_dim/output_dim) to detect the layout. Without it, Humming assumes(N, K)and calls.view(torch.int32)on the transposed view, which fails. Since #62326, CompressedTensors, fbgemm, ModelOpt and Quark FP8 setprocessed_weight_dims = (0, 1)after transposing. This PR does the same in the two methods that lacked it. Humming is the only FP8 scaled-mm kernel that reads it, so other kernels are unaffected.PTPC.
Fp8PtpcOnlineLinearMethodalready refuses Marlin because PTPC requires per-token FP8 activations and Marlin FP8 is W8A16.HummingFP8ScaledMMLinearKernelbuilds its layer without an input schema, so it is W8A16 too. With the layout fixed, PTPC would silently run as weight-only FP8 on Humming, so this PR adds Humming to that check.Fp8LinearMethodandfp8_per_tensoralready accept Marlin's W8A16 fallback, and Humming matches it.Batch-invariant
apply. In batch-invariant mode these methods call the kernel only whenfp8_scaled_mm_is_batch_invariant()(from #52231) says so; otherwise they dequantizelayer.weightand runF.linear. After Humming'sprocess_weights_after_loading,layer.weightis in Humming's packed layout, so that fallback fails (a and b must have same reduction dim, but got [s, 4096] X [256, 24576]). Humming has its own batch-invariant mode (use_batch_invariantin its compute config), and the block-FP8 path and compressed-tensors already call it in this mode. This PR makesfp8_scaled_mm_is_batch_invariant()return True for Humming. Humming FP8 is weight-only here, so the dynamic per-tensor activation amax that the helper guards against doesn't apply.Notes.
VLLM_BATCH_INVARIANT=1they were. vLLM only promises this in batch-invariant mode, and it does not come from this change.hummingcase is intests/quantization/test_fp8.py, whose Quantization step is temporarily disabled by [CI] Disable jobs with unlocked transitive dependencies #63608. The layout bug does not depend on the GPU architecture. I validated on SM86 only.Not a duplicate. Searched open issues and PRs for Humming FP8 linear, the stride error message, the layout tags, and FP8 batch invariance on Ampere. #59460 also edits
scaled_mm/humming.py(activation-quantization fallbacks) but not the weight layout or batch-invariant dispatch, and this PR does not touchhumming.py.Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.