Skip to content

Commit 7047001

Browse files
committed
fix(quantization): address INT8 review feedback
1 parent bccee4c commit 7047001

10 files changed

Lines changed: 127 additions & 360 deletions

File tree

tests/unit_tests/quantization/test_packed_int8.py

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@
1919
QuantizationType,
2020
)
2121

22-
from vllm_fl.quantization.w8a8 import packed
23-
from vllm_fl.quantization.w8a8.int8_mode import (
24-
INT8_MODE_ENV,
25-
should_use_packed_w8a8,
22+
from vllm.model_executor.layers.quantization.compressed_tensors.schemes import (
23+
CompressedTensorsW8A8Int8,
2624
)
2725

26+
from vllm_fl.quantization.w8a8 import packed
27+
from vllm_fl.quantization.w8a8.int8_mode import should_use_packed_w8a8
28+
2829

2930
def _weight_args(strategy: QuantizationStrategy) -> QuantizationArgs:
3031
return QuantizationArgs(
@@ -37,50 +38,66 @@ def _weight_args(strategy: QuantizationStrategy) -> QuantizationArgs:
3738
)
3839

3940

40-
def test_auto_mode_maps_channelwise_packed_int8_to_w8a8(monkeypatch):
41-
monkeypatch.delenv(INT8_MODE_ENV, raising=False)
41+
def _activation_args(
42+
*,
43+
strategy: QuantizationStrategy = QuantizationStrategy.TOKEN,
44+
dynamic: bool = True,
45+
) -> QuantizationArgs:
46+
return QuantizationArgs(
47+
num_bits=8,
48+
type=QuantizationType.INT,
49+
strategy=strategy,
50+
symmetric=True,
51+
dynamic=dynamic,
52+
)
53+
54+
55+
def test_model_config_maps_packed_channelwise_w8a8():
4256
assert should_use_packed_w8a8(
4357
_weight_args(QuantizationStrategy.CHANNEL),
44-
None,
45-
"pack-quantized",
46-
)
47-
assert not should_use_packed_w8a8(
48-
_weight_args(QuantizationStrategy.GROUP),
49-
None,
58+
_activation_args(),
5059
"pack-quantized",
5160
)
5261

5362

54-
def test_w8a16_mode_keeps_channelwise_checkpoint_weight_only(monkeypatch):
55-
monkeypatch.setenv(INT8_MODE_ENV, "w8a16")
63+
def test_missing_activation_config_keeps_weight_only_scheme():
5664
assert not should_use_packed_w8a8(
5765
_weight_args(QuantizationStrategy.CHANNEL),
5866
None,
5967
"pack-quantized",
6068
)
6169

6270

63-
def test_w8a8_mode_rejects_groupwise_checkpoint(monkeypatch):
64-
monkeypatch.setenv(INT8_MODE_ENV, "w8a8")
65-
with pytest.raises(ValueError, match="--strategy channel"):
71+
def test_packed_w8a8_rejects_groupwise_weights():
72+
with pytest.raises(ValueError, match="per-channel"):
6673
should_use_packed_w8a8(
6774
_weight_args(QuantizationStrategy.GROUP),
68-
None,
75+
_activation_args(),
6976
"pack-quantized",
7077
)
7178

7279

73-
def test_packed_scheme_uses_native_compatible_layer_contract(monkeypatch):
80+
def test_static_activation_config_does_not_select_dynamic_w8a8():
81+
assert not should_use_packed_w8a8(
82+
_weight_args(QuantizationStrategy.CHANNEL),
83+
_activation_args(dynamic=False),
84+
"pack-quantized",
85+
)
86+
87+
88+
def test_packed_scheme_reuses_native_w8a8_execution(monkeypatch):
89+
processed_layers = []
90+
7491
class FakeKernel:
7592
def process_weights_after_loading(self, layer):
76-
pass
93+
processed_layers.append(layer)
7794

7895
def apply_weights(self, layer, x, bias):
7996
raise AssertionError("not used")
8097

8198
monkeypatch.setattr(
82-
packed,
83-
"create_w8a8_linear_kernel",
99+
"vllm.model_executor.layers.quantization.compressed_tensors.schemes."
100+
"compressed_tensors_w8a8_int8.init_int8_linear_kernel",
84101
lambda *args, **kwargs: FakeKernel(),
85102
)
86103
monkeypatch.setattr(
@@ -91,7 +108,10 @@ def apply_weights(self, layer, x, bias):
91108
"vllm.model_executor.parameter.get_tensor_model_parallel_world_size",
92109
lambda: 1,
93110
)
94-
scheme = packed.FLPackedW8A8Scheme(layer_name="model.linear")
111+
112+
scheme = packed.CompressedTensorsPackedW8A8Int8(layer_name="model.linear")
113+
assert isinstance(scheme, CompressedTensorsW8A8Int8)
114+
95115
layer = torch.nn.Module()
96116
scheme.create_weights(
97117
layer,
@@ -102,6 +122,19 @@ def apply_weights(self, layer, x, bias):
102122
)
103123

104124
assert layer.logical_widths == [4, 4]
125+
assert not hasattr(layer, "weight")
105126
assert layer.weight_packed.shape == (8, 2)
106127
assert layer.weight_scale.shape == (8, 1)
107128
assert layer.weight_scale.dtype == torch.float32
129+
130+
unpacked = torch.ones((8, 8), dtype=torch.int8)
131+
monkeypatch.setattr(
132+
packed,
133+
"unpack_uint8b128_int32",
134+
lambda *args, **kwargs: unpacked,
135+
)
136+
scheme.process_weights_after_loading(layer)
137+
138+
assert not hasattr(layer, "weight_packed")
139+
assert torch.equal(layer.weight, unpacked)
140+
assert processed_layers == [layer]

tests/unit_tests/quantization/test_w8a8_linear.py

Lines changed: 0 additions & 135 deletions
This file was deleted.

tests/unit_tests/quantization/test_w8a8_moe.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,10 @@ def upstream_selector(*args, **kwargs):
137137
activation_key=None,
138138
)
139139

140-
from vllm_fl.quantization.w8a8.moe_experts import (
141-
VllmFunctionalW8A8Experts,
142-
)
140+
from vllm_fl.quantization.w8a8.moe_experts import TritonW8A8Experts
143141

144142
assert backend == "triton"
145-
assert experts_cls is VllmFunctionalW8A8Experts
143+
assert experts_cls is TritonW8A8Experts
146144
assert upstream_calls == []
147145

148146

tests/unit_tests/quantization/test_w8a8_moe_experts.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ def _apply_arguments():
4646
def test_functional_experts_defer_activation_quantization():
4747
instance = SimpleNamespace()
4848
assert (
49-
moe_experts.VllmFunctionalW8A8Experts.expects_unquantized_inputs.fget(instance)
50-
is True
49+
moe_experts.TritonW8A8Experts.expects_unquantized_inputs.fget(instance) is True
5150
)
5251

5352

@@ -114,7 +113,7 @@ def fake_fused_experts(
114113
instance = SimpleNamespace(quant_config=quant_config)
115114
arguments = _apply_arguments()
116115

117-
moe_experts.VllmFunctionalW8A8Experts.apply(instance, **arguments)
116+
moe_experts.TritonW8A8Experts.apply(instance, **arguments)
118117

119118
assert calls[0]["hidden_states"].dtype == torch.bfloat16
120119
assert calls[0]["quant_config"] is quant_config
@@ -140,4 +139,4 @@ def test_functional_experts_rejects_prequantized_input(monkeypatch):
140139
arguments["a1q_scale"] = torch.ones((2, 1), dtype=torch.float32)
141140

142141
with pytest.raises(ValueError, match="quantized before"):
143-
moe_experts.VllmFunctionalW8A8Experts.apply(instance, **arguments)
142+
moe_experts.TritonW8A8Experts.apply(instance, **arguments)

vllm_fl/ops/fused_moe/fused_moe_utils.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -312,19 +312,17 @@ def apply(
312312
expert_tokens_meta: mk.ExpertTokensMetadata | None,
313313
apply_router_weight_on_input: bool,
314314
):
315-
# Dynamic W8A8 is handled by VllmFunctionalW8A8Experts so vLLM owns
315+
# Dynamic W8A8 is handled by TritonW8A8Experts so vLLM owns
316316
# activation quantization. Do not allow it to fall back into the
317317
# FlagGems contract, which expects floating-point input here.
318318
if self.quant_config.use_int8_w8a8:
319319
raise RuntimeError(
320-
"W8A8 MoE must use VllmFunctionalW8A8Experts, not TritonExpertsFL"
320+
"W8A8 MoE must use TritonW8A8Experts, not TritonExpertsFL"
321321
)
322322

323-
# Fast path (no LoRA): let FlagGems own both expert GEMMs for
324-
# unquantized and W8A16 inputs.
325-
from vllm_fl.utils import use_flaggems_op
326-
327-
if self._lora_context is None and use_flaggems_op("fused_moe"):
323+
# Fast path (no LoRA, NVIDIA only): let FlagGems own both expert GEMMs
324+
# for unquantized and W8A16 inputs.
325+
if self._lora_context is None and current_platform.is_cuda():
328326
import flag_gems
329327

330328
output.copy_(

0 commit comments

Comments
 (0)