Repository navigation
[XPU] Add W8A8 FP8 linear kernel with multi-granularity quant support - #43645
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces separate kernels for W8A8 and W8A16 FP8 quantization on XPU (XPUW8A8FP8LinearKernel and XPUW8A16FP8LinearKernel), replacing the single XPUFP8ScaledMMLinearKernel. It also enables dynamic token and tensor quantization support for XPU platforms. A review comment suggests that XPUW8A16FP8LinearKernel should inherit directly from FP8ScaledMMLinearKernel instead of XPUW8A8FP8LinearKernel to prevent a confusing inheritance hierarchy.
|
Hi @chaojun-zhang, the pre-commit checks have failed. Please run: uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-filesThen, commit the changes and push to your branch. For future commits, Tip Is
|
eeaa914 to
7333788
Compare
|
Hi @chaojun-zhang, the pre-commit checks have failed. Please run: uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-filesThen, commit the changes and push to your branch. For future commits, Tip Is
|
5cc2270 to
feae6eb
Compare
|
cc @zufangzhu |
| @@ -108,7 +108,7 @@ def __init__(self): | |||
|
|
|||
| self.weight_quant_key = kFp8StaticTensorSym | |||
| # Use per-token quantization for better perf if dynamic and cutlass | |||
| if cutlass_fp8_supported(): | |||
| if cutlass_fp8_supported() or current_platform.is_xpu(): | |||
There was a problem hiding this comment.
Have you compare per-token and per-tensor performance on xpu?
There was a problem hiding this comment.
Yes, I ran a systematic comparison (TP=2, meta-llama/Llama-3.2-1B-Instruct).
W-tensor / A-dyn-token
| Workload | out-tok/s | TTFT mean(ms) | TTFT median(ms) | TTFT P99(ms) | TPOT mean(ms) | TPOT median(ms) | TPOT P99(ms) |
|---|---|---|---|---|---|---|---|
| np=1, in=128, out=512 | 31.1 | 4733.9 | 4733.9 | 4733.9 | 22.96 | 22.96 | 22.96 |
| np=4, in=128, out=512 | 334.0 | 267.1 | 334.9 | 335.6 | 11.46 | 11.34 | 11.79 |
| np=13, in=128, out=512 | 985.4 | 132.7 | 122.6 | 156.8 | 12.94 | 12.95 | 13.02 |
| np=1, in=1024, out=512 | 91.6 | 91.4 | 91.4 | 91.4 | 10.75 | 10.75 | 10.75 |
| np=4, in=1024, out=512 | 336.9 | 197.8 | 231.5 | 232.8 | 11.49 | 11.44 | 11.66 |
| np=13, in=1024, out=512 | 1083.4 | 410.3 | 435.7 | 599.7 | 11.19 | 11.14 | 11.71 |
| np=1, in=2048, out=128 | 58.9 | 148.8 | 148.8 | 148.8 | 15.94 | 15.94 | 15.94 |
| np=4, in=2048, out=128 | 224.5 | 330.2 | 396.4 | 397.2 | 15.31 | 14.83 | 16.70 |
| np=13, in=2048, out=128 | 682.7 | 817.4 | 817.1 | 1150.5 | 12.59 | 12.60 | 17.21 |
| np=32, in=512, out=256 | 2410.1 | 557.3 | 578.6 | 754.9 | 11.08 | 11.01 | 12.52 |
| np=32, in=256, out=512 | 2842.9 | 354.9 | 431.0 | 433.5 | 10.56 | 10.42 | 10.98 |
W-tensor / A-dyn-tensor
| Workload | out-tok/s | TTFT mean(ms) | TTFT median(ms) | TTFT P99(ms) | TPOT mean(ms) | TPOT median(ms) | TPOT P99(ms) |
|---|---|---|---|---|---|---|---|
| np=1, in=128, out=512 | 34.7 | 3703.5 | 3703.5 | 3703.5 | 21.60 | 21.60 | 21.60 |
| np=4, in=128, out=512 | 278.7 | 216.5 | 273.9 | 274.9 | 13.92 | 13.84 | 14.14 |
| np=13, in=128, out=512 | 1099.8 | 140.7 | 129.3 | 165.9 | 11.54 | 11.56 | 11.62 |
| np=1, in=1024, out=512 | 74.1 | 103.6 | 103.6 | 103.6 | 13.32 | 13.32 | 13.32 |
| np=4, in=1024, out=512 | 348.0 | 208.8 | 243.3 | 244.5 | 11.09 | 11.04 | 11.26 |
| np=13, in=1024, out=512 | 1064.1 | 501.2 | 464.8 | 631.6 | 11.23 | 11.30 | 11.88 |
| np=1, in=2048, out=128 | 83.6 | 164.0 | 164.0 | 164.0 | 10.75 | 10.75 | 10.75 |
| np=4, in=2048, out=128 | 280.5 | 353.4 | 423.0 | 424.5 | 11.53 | 11.02 | 13.00 |
| np=13, in=2048, out=128 | 634.4 | 878.8 | 878.6 | 1235.3 | 13.54 | 13.57 | 18.45 |
| np=32, in=512, out=256 | 2226.1 | 586.9 | 617.9 | 797.1 | 12.05 | 11.93 | 13.47 |
| np=32, in=256, out=512 | 2281.2 | 366.5 | 455.9 | 461.3 | 13.30 | 13.14 | 13.74 |
Conclusion
Neither granularity consistently wins — the optimal choice is workload-dependent:
- Per-token is better for decode-heavy + high-concurrency scenarios (e.g. np=32, in=256, out=512: +24% out-tok/s, TPOT 10.56 vs 13.30 ms)
- Per-tensor is better for large-batch long-prefill scenarios (e.g. np=4, in=2048, out=128: +25% out-tok/s, TPOT 11.53 vs 15.31 ms)
- For moderate batch sizes and mixed workloads, differences are within ±5%
Since there is no universally better choice , I preserve the existing default (per-token)
There was a problem hiding this comment.
Our current tests don't really use small inputs or outputs, so maybe per-tensor is better choice.
@jikunshang what do you think?
There was a problem hiding this comment.
updated and use default per-tensor
| @@ -308,7 +308,7 @@ def __init__(self, quant_config: Fp8Config): | |||
| # Use per-token quantization for better perf if dynamic and cutlass | |||
| if self.act_q_static: | |||
| self.activation_quant_key = kFp8StaticTensorSym | |||
| elif cutlass_fp8_supported(): | |||
| elif cutlass_fp8_supported() or current_platform.is_xpu(): | |||
There was a problem hiding this comment.
Same with above, compare performance then choose better one
|
Is reasonable for the accuracy result of : |
Yes, the accuracy results are reasonable. Validated with GSM8k (5-shot, 250 samples) for
|
842f839 to
9c63545
Compare
zufangzhu
left a comment
There was a problem hiding this comment.
Just one small suggestion; LGTM
| self._check_scheme_supported( | ||
| CompressedTensorsW8A8Fp8.get_min_capability(), error=False | ||
| ) | ||
| or current_platform.is_xpu() |
There was a problem hiding this comment.
Since this function _check_scheme_supported is to check capability, can we directly return true inner it.
9c63545 to
e461379
Compare
| @@ -335,6 +335,9 @@ def get_config_filenames(cls) -> list[str]: | |||
| def _check_scheme_supported( | |||
| min_capability: int, error: bool = True, match_exact: bool = False | |||
| ) -> bool: | |||
| if current_platform.is_xpu(): | |||
There was a problem hiding this comment.
we should be careful about adding such change...
There was a problem hiding this comment.
So I was using _check_scheme_supported or current_platform.is_xpu() at the external call site for this check — do you think that works?
| _SUPPORTED_ACT_QUANT_KEYS = { | ||
| kFp8DynamicTensorSym, | ||
| kFp8DynamicTokenSym, | ||
| kFp8StaticTensorSym, | ||
| kFp8StaticTokenSym, | ||
| } | ||
| _SUPPORTED_WEIGHT_QUANT_KEYS = { | ||
| kFp8StaticChannelSym, | ||
| kFp8StaticTensorSym, | ||
| } |
There was a problem hiding this comment.
do we support all _SUPPORTED_WEIGHT_QUANT_KEYS x _SUPPORTED_ACT_QUANT_KEYS ?
There was a problem hiding this comment.
Yes, all 8 combinations (2 weight types × 4 activation types) are supported
|
This pull request has merge conflicts that must be resolved before it can be |
9c5b346 to
09807e7
Compare
|
Documentation preview: https://vllm--43645.org.readthedocs.build/en/43645/ |
| commands: | ||
| - >- | ||
| bash .buildkite/scripts/hardware_ci/run-intel-test.sh | ||
| 'python3 examples/basic/offline_inference/generate.py --linear-backend xpu --model RedHatAI/Meta-Llama-3.1-8B-Instruct-FP8 --dtype bfloat16 --block-size 64 --gpu-memory-utilization 0.9 --enforce-eager --max-model-len 4096 && |
There was a problem hiding this comment.
we can remove --dtype bfloat16 --block-size 64 --gpu-memory-utilization 0.9
| @@ -316,28 +319,51 @@ def check_model(model): | |||
| assert output | |||
|
|
|||
|
|
|||
| def test_compressed_tensors_fp8(vllm_runner): | |||
| @pytest.mark.parametrize( | |||
There was a problem hiding this comment.
emmm, necessary to change these uts?
223eb3a to
ae1419b
Compare
| config = get_current_vllm_config_or_none() | ||
| is_fp8_w8a8_supported = ( | ||
| config is not None | ||
| and config.kernel_config.linear_backend == "xpu" |
There was a problem hiding this comment.
--linear-backend xpu-w8a8 ?
There was a problem hiding this comment.
offline talked with @jikunshang , we will add two linear-backend options ('xpu' and 'xpu_woq' ).
- --linear-backend xpu: Use the default XPU kernels. (e.g. w8a8 kernel for fp8) .
- --linear-backend xpu_woq: Use weight only quant kernels
b454531 to
945bbad
Compare
Signed-off-by: Chaojun,Zhang <chaojun.zhang@intel.com>
Signed-off-by: Chaojun Zhang <chaojun.zhang@intel.com>
Replace the xpu_w8a8_fp8 linear backend with two clearer options: - xpu: W8A8 and block-scaled quantization kernels - xpu_woq: weight-only quantization kernels (W8A16) Update CI commands, documentation, and kernel selection logic. Signed-off-by: Chaojun Zhang <chaojun.zhang@intel.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
13ac077 to
64bb275
Compare
…vllm-project#43645) Signed-off-by: Chaojun,Zhang <chaojun.zhang@intel.com> Signed-off-by: Chaojun Zhang <chaojun.zhang@intel.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…vllm-project#43645) Signed-off-by: Chaojun,Zhang <chaojun.zhang@intel.com> Signed-off-by: Chaojun Zhang <chaojun.zhang@intel.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…vllm-project#43645) Signed-off-by: Chaojun,Zhang <chaojun.zhang@intel.com> Signed-off-by: Chaojun Zhang <chaojun.zhang@intel.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Purpose
This PR adds
XPUW8A8FP8LinearKernelandXPUW8A16FP8LinearKernelfor XPU,replacing the previous
XPUFP8ScaledMMLinearKernelwith proper support formultiple weight/activation quantization granularities.
Changes
XPUW8A8FP8LinearKernelandXPUW8A16FP8LinearKernel, replacingXPUFP8ScaledMMLinearKernelkFp8DynamicTokenSym) on XPUAccuracy Tests
Benchmark: gsm8k, 250 samples, 5-shot, eager mode
Test script (
acc.py):Case Definitions
kFp8StaticTensorSymkFp8StaticTensorSymRedHatAI/Meta-Llama-3.1-8B-Instruct-FP8kFp8StaticChannelSymkFp8DynamicTokenSymRedHatAI/Qwen3-32B-FP8-dynamic--max-model-len 30720kFp8StaticTensorSymkFp8DynamicTokenSymneuralmagic/Llama-3.2-1B-Instruct-FP8-dynamickFp8StaticTensorSymkFp8DynamicTensorSymmeta-llama/Llama-3.2-1B-Instruct--quantization fp8Each case is run with:
VLLM_USE_V1=1 python3 acc.py \ --model <model> --dtype bfloat16 --tp <tp> \ --block-size 64 --gpu-memory-util 0.9 --eager [extra args]Results