main
py 212 lines 6.43 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import importlib
5 from types import SimpleNamespace
6
7 import pytest
8 import torch
9
10 from vllm.model_executor.kernels.linear import (
11 B12xFp8BlockScaledMMKernel,
12 B12xMxFp4LinearKernel,
13 B12xMxfp8LinearKernel,
14 B12xNvFp4LinearKernel,
15 B12xTensorFP8ScaledMMLinearKernel,
16 )
17 from vllm.model_executor.warmup.b12x_warmup import b12x_warmup
18 from vllm.utils.b12x import B12xWarmupUnit, b12x_warmup_token_counts
19
20
21 def test_b12x_warmup_token_counts_cover_serving_regimes() -> None:
22 assert b12x_warmup_token_counts(
23 max_tokens=2048,
24 cudagraph_capture_sizes=[1, 2, 8, 128],
25 ) == (1, 2, 8, 128, 2048)
26
27
28 @pytest.mark.parametrize(
29 ("kernel_cls", "module_name", "call_name", "layer", "name"),
30 [
31 (
32 B12xMxFp4LinearKernel,
33 "vllm.model_executor.kernels.linear.mxfp4.b12x",
34 "_apply_b12x_mxfp4_linear",
35 SimpleNamespace(
36 weight=torch.empty((48, 64), dtype=torch.uint8),
37 weight_scale=torch.empty((128, 4), dtype=torch.uint8),
38 ),
39 "MXFP4",
40 ),
41 (
42 B12xNvFp4LinearKernel,
43 "vllm.model_executor.kernels.linear.nvfp4.b12x",
44 "_apply_b12x_nvfp4_linear",
45 SimpleNamespace(
46 weight=torch.empty((48, 64), dtype=torch.uint8),
47 weight_scale=torch.empty((128, 8), dtype=torch.float8_e4m3fn),
48 input_global_scale_inv=torch.tensor(2.0),
49 alpha=torch.tensor(0.25),
50 ),
51 "NVFP4",
52 ),
53 (
54 B12xFp8BlockScaledMMKernel,
55 "vllm.model_executor.kernels.linear.scaled_mm.b12x",
56 "_run_b12x_fp8_block_scaled_mm",
57 SimpleNamespace(
58 weight=torch.empty((256, 128), dtype=torch.float8_e4m3fn),
59 weight_scale_inv=torch.empty((2, 1), dtype=torch.float32),
60 ),
61 "block-FP8",
62 ),
63 ],
64 )
65 def test_b12x_warmup_units_cover_token_counts(
66 monkeypatch,
67 kernel_cls,
68 module_name: str,
69 call_name: str,
70 layer,
71 name: str,
72 ) -> None:
73 calls = []
74 monkeypatch.setattr(
75 importlib.import_module(module_name),
76 call_name,
77 lambda *args: calls.append(args),
78 )
79 kernel = object.__new__(kernel_cls)
80
81 unit = kernel.get_b12x_warmup_unit(layer, (1, 8), torch.bfloat16)
82 unit.compile()
83
84 assert unit.name == name
85 assert [args[0].shape[0] for args in calls] == [1, 8]
86 assert unit.key[-1] == torch.bfloat16
87
88
89 def test_b12x_mxfp8_warmup_unit(monkeypatch) -> None:
90 import vllm.model_executor.kernels.linear.mxfp8.b12x as b12x_mod
91
92 calls = []
93 monkeypatch.setattr(
94 b12x_mod,
95 "_import_b12x_mxfp8",
96 lambda: SimpleNamespace(
97 mm=lambda *args, **kwargs: calls.append((args, kwargs))
98 ),
99 )
100 monkeypatch.setattr(
101 b12x_mod,
102 "current_stream",
103 lambda: SimpleNamespace(cuda_stream=object()),
104 )
105 packed_weight = SimpleNamespace(
106 in_features=128,
107 padded_in_features=128,
108 out_features=256,
109 weight=SimpleNamespace(values=torch.empty(1)),
110 )
111 layer = SimpleNamespace(b12x_mxfp8_packed_weight=packed_weight)
112 kernel = object.__new__(B12xMxfp8LinearKernel)
113
114 unit = kernel.get_b12x_warmup_unit(layer, (1, 8), torch.float16)
115 unit.compile()
116
117 assert [args[0].shape for args, _ in calls] == [(1, 128), (8, 128)]
118 assert [kwargs["expected_m"] for _, kwargs in calls] == [1, 8]
119
120
121 def test_b12x_tensor_fp8_warmup_unit(monkeypatch) -> None:
122 import vllm.model_executor.kernels.linear.scaled_mm.b12x as b12x_mod
123
124 calls = []
125 monkeypatch.setattr(
126 b12x_mod,
127 "_import_b12x_tensor_fp8",
128 lambda: SimpleNamespace(
129 prewarm=lambda *args, **kwargs: calls.append((args, kwargs))
130 ),
131 )
132 monkeypatch.setattr(
133 b12x_mod,
134 "current_stream",
135 lambda: SimpleNamespace(cuda_stream=object()),
136 )
137 packed_weight = SimpleNamespace(
138 in_features=128,
139 padded_in_features=128,
140 out_features=256,
141 values=torch.empty(1),
142 )
143 layer = SimpleNamespace(b12x_tensor_fp8_packed_weight=packed_weight)
144 kernel = object.__new__(B12xTensorFP8ScaledMMLinearKernel)
145
146 unit = kernel.get_b12x_warmup_unit(layer, (1, 8), torch.bfloat16)
147 unit.compile()
148
149 assert calls[0][0] == (packed_weight, (1, 8))
150 assert calls[0][1]["out_dtype"] == torch.bfloat16
151
152
153 def test_b12x_warmup_deduplicates_registered_signatures(monkeypatch) -> None:
154 import vllm.model_executor.warmup.b12x_warmup as warmup_mod
155
156 calls: list[tuple[str, tuple[int, ...], torch.dtype]] = []
157
158 class Provider:
159 def get_b12x_warmup_unit(self, layer, token_counts, output_dtype):
160 return B12xWarmupUnit(
161 name="fake",
162 key=(type(self), layer.shape, output_dtype),
163 compile=lambda: calls.append((layer.name, token_counts, output_dtype)),
164 )
165
166 provider = Provider()
167 layers = [
168 SimpleNamespace(name="first", shape=(128, 256), b12x_warmup_provider=provider),
169 SimpleNamespace(
170 name="duplicate", shape=(128, 256), b12x_warmup_provider=provider
171 ),
172 SimpleNamespace(name="second", shape=(256, 256), b12x_warmup_provider=provider),
173 SimpleNamespace(),
174 ]
175 scans = 0
176
177 def modules():
178 nonlocal scans
179 scans += 1
180 return iter(layers)
181
182 worker = SimpleNamespace(
183 get_model=lambda: SimpleNamespace(modules=modules),
184 scheduler_config=SimpleNamespace(
185 max_num_batched_tokens=8,
186 max_num_scheduled_tokens=32,
187 ),
188 model_config=SimpleNamespace(dtype=torch.float32),
189 vllm_config=SimpleNamespace(
190 compilation_config=SimpleNamespace(compile_sizes=[4, 16])
191 ),
192 )
193 platform = SimpleNamespace(
194 is_cuda=lambda: True,
195 is_device_capability_family=lambda family: family == 120,
196 )
197 synchronized = []
198 monkeypatch.setattr(warmup_mod, "current_platform", platform)
199 monkeypatch.setattr(
200 warmup_mod.torch.accelerator,
201 "synchronize",
202 lambda: synchronized.append(True),
203 )
204
205 b12x_warmup(worker, [1, 2])
206
207 assert scans == 1
208 assert calls == [
209 ("first", (1, 2, 4, 8, 16, 32), torch.bfloat16),
210 ("second", (1, 2, 4, 8, 16, 32), torch.bfloat16),
211 ]
212 assert synchronized == [True]