@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 53a30845be
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 14, 05:30 UTC

Llamas 3.1 405B fp4 changes upstreaming from 355_wip (#25135)

Signed-off-by: Aleksandr Malyshev <maleksan@amd.com> Co-authored-by: Aleksandr Malyshev <maleksan@amd.com> Co-authored-by: Doug Lehr <douglehr@amd.com>

Aleksandr Malyshev committed Sep 25, 2025 at 18:16 UTC 53a30845bef29f62140dad2113a2dc901fd959cf
6 files changed +301 -38
vllm/envs.py
+16
@@ -106,6 +106,8 @@ if TYPE_CHECKING:
106 VLLM_ROCM_USE_AITER_RMSNORM: bool = True
107 VLLM_ROCM_USE_AITER_MLA: bool = True
108 VLLM_ROCM_USE_AITER_MHA: bool = True
109 + VLLM_ROCM_USE_AITER_FP4_ASM_GEMM: bool = False
110 + VLLM_ROCM_USE_TRITON_ROPE: bool = False
111 VLLM_ROCM_USE_AITER_FP8BMM: bool = True
112 VLLM_ROCM_USE_SKINNY_GEMM: bool = True
113 VLLM_ROCM_FP8_PADDING: bool = True
@@ -934,6 +936,18 @@ environment_variables: dict[str, Callable[[], Any]] = {
936 lambda: (os.getenv("VLLM_ROCM_USE_AITER_MHA", "True").lower() in
937 ("true", "1")),
938
939 + # Whether to use aiter fp4 gemm asm.
940 + # By default is disabled.
941 + "VLLM_ROCM_USE_AITER_FP4_ASM_GEMM":
942 + lambda: (os.getenv("VLLM_ROCM_USE_AITER_FP4_ASM_GEMM", "False").lower() in
943 + ("true", "1")),
944 +
945 + # Whether to use aiter rope.
946 + # By default is disabled.
947 + "VLLM_ROCM_USE_TRITON_ROPE":
948 + lambda: (os.getenv("VLLM_ROCM_USE_TRITON_ROPE", "False").lower() in
949 + ("true", "1")),
950 +
951 # Whether to use aiter triton fp8 bmm kernel
952 # By default is enabled.
953 "VLLM_ROCM_USE_AITER_FP8BMM":
@@ -1539,6 +1553,8 @@ def compute_hash() -> str:
1553 "VLLM_ROCM_USE_AITER_RMSNORM",
1554 "VLLM_ROCM_USE_AITER_MLA",
1555 "VLLM_ROCM_USE_AITER_MHA",
1556 + "VLLM_ROCM_USE_AITER_FP4_ASM_GEMM",
1557 + "VLLM_ROCM_USE_TRITON_ROPE",
1558 "VLLM_ROCM_USE_AITER_FP8BMM",
1559 "VLLM_ROCM_USE_SKINNY_GEMM",
1560 "VLLM_ROCM_FP8_PADDING",
vllm/model_executor/layers/linear.py
+20 -8
@@ -323,6 +323,12 @@ class ReplicatedLinear(LinearBase):
323 return_bias: bool = True,
324 disable_tp: bool = False,
325 ):
326 + # If MergedReplicatedLinear, use output size of each partition.
327 + if hasattr(self, "output_sizes"):
328 + self.output_partition_sizes = self.output_sizes
329 + else:
330 + self.output_partition_sizes = [output_size]
331 +
332 super().__init__(input_size,
333 output_size,
334 skip_bias_add,
@@ -335,7 +341,8 @@ class ReplicatedLinear(LinearBase):
341 # All the linear layer supports quant method.
342 assert self.quant_method is not None
343 self.quant_method.create_weights(self,
338 - self.input_size, [self.output_size],
344 + self.input_size,
345 + self.output_partition_sizes,
346 self.input_size,
347 self.output_size,
348 self.params_dtype,
@@ -374,12 +381,15 @@ class ReplicatedLinear(LinearBase):
381 param.data.copy_(loaded_weight)
382
383 def forward(
377 - self, x: torch.Tensor
384 + self,
385 + x: torch.Tensor,
386 ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[Parameter]]]:
387 bias = self.bias if not self.skip_bias_add else None
388 assert self.quant_method is not None
389 +
390 output = self.quant_method.apply(self, x, bias)
391 output_bias = self.bias if self.skip_bias_add else None
392 +
393 if not self.return_bias:
394 return output
395 return output, output_bias
@@ -413,7 +423,7 @@ class ColumnParallelLinear(LinearBase):
423 output_sizes: list of output sizes packed into one output, like for QKV
424 the list would be size 3.
425 prefix: The name of the layer in the state dict, including all parents
416 - (e.g. model.layers.0.qkv_proj)
426 + (e.g. model.layers.0.qkv_proj)
427 return_bias: If true, return bias together with outputs in forward pass.
428 disable_tp: If true, weights matrix won't be sharded through tp rank.
429 """
@@ -535,13 +545,15 @@ class ColumnParallelLinear(LinearBase):
545 param.load_column_parallel_weight(loaded_weight=loaded_weight)
546
547 def forward(
538 - self, input_
548 + self,
549 + input_,
550 ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[Parameter]]]:
551 bias = self.bias if not self.skip_bias_add else None
552
553 # Matrix multiply.
554 assert self.quant_method is not None
555 output_parallel = self.quant_method.apply(self, input_, bias)
556 +
557 if self.gather_output and self.tp_size > 1:
558 # All-gather across the partitions.
559 output = tensor_model_parallel_all_gather(output_parallel)
@@ -1326,7 +1338,8 @@ class RowParallelLinear(LinearBase):
1338 param.load_row_parallel_weight(loaded_weight=loaded_weight)
1339
1340 def forward(
1329 - self, input_
1341 + self,
1342 + input_,
1343 ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[Parameter]]]:
1344 if self.input_is_parallel:
1345 input_parallel = input_
@@ -1340,9 +1353,8 @@ class RowParallelLinear(LinearBase):
1353 # Only fuse bias add into GEMM for rank 0 (this ensures that
1354 # bias will not get added more than once in TP>1 case)
1355 bias_ = None if (self.tp_rank > 0 or self.skip_bias_add) else self.bias
1343 - output_parallel = self.quant_method.apply(self,
1344 - input_parallel,
1345 - bias=bias_)
1356 + output_parallel = self.quant_method.apply(self, input_parallel, bias_)
1357 +
1358 if self.reduce_results and self.tp_size > 1:
1359 output = tensor_model_parallel_all_reduce(output_parallel)
1360 else:
vllm/model_executor/layers/quantization/quark/quark.py
+1
@@ -395,6 +395,7 @@ class QuarkLinearMethod(LinearMethodBase):
395 scheme = layer.scheme
396 if scheme is None:
397 raise ValueError("A scheme must be defined for each layer")
398 +
399 return scheme.apply_weights(layer, x, bias=bias)
400
401
vllm/model_executor/layers/quantization/quark/schemes/quark_w4a4_mxfp4.py
+156 -29
@@ -1,12 +1,13 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 +from functools import cache
5 from typing import Any, Callable, Optional
6
7 import torch
8 import torch.nn.functional as F
9
9 -from vllm.logger import init_logger
10 +from vllm import envs
11 from vllm.model_executor.layers.quantization.quark.schemes import QuarkScheme
12 from vllm.model_executor.layers.quantization.utils.mxfp4_utils import (
13 OCP_MX_BLOCK_SIZE, dequant_mxfp4, quant_dequant_mxfp4)
@@ -14,7 +15,90 @@ from vllm.model_executor.parameter import (GroupQuantScaleParameter,
15 PackedvLLMParameter)
16 from vllm.platforms import current_platform
17
17 -logger = init_logger(__name__)
18 +
19 +@cache
20 +def is_rocm_aiter_fp4_asm_gemm_enabled() -> bool:
21 + return current_platform.is_rocm() \
22 + and envs.VLLM_ROCM_USE_AITER_FP4_ASM_GEMM \
23 + and envs.VLLM_ROCM_USE_AITER
24 +
25 +
26 +try:
27 + from aiter.ops.shuffle import shuffle_weight
28 + from aiter.ops.triton.gemm_afp4wfp4 import gemm_afp4wfp4
29 + from aiter.ops.triton.quant import dynamic_mxfp4_quant
30 +
31 + from vllm.utils import direct_register_custom_op
32 + if is_rocm_aiter_fp4_asm_gemm_enabled():
33 + from aiter import gemm_a4w4, per_1x32_f4_quant_hip
34 +
35 + def gemm_with_dynamic_quant(
36 + x: torch.Tensor,
37 + weight: torch.Tensor,
38 + weight_scale: torch.Tensor,
39 + rocm_use_aiter_fp4_asm_gemm: bool = False,
40 + out_dtype: Optional[torch.dtype] = torch.bfloat16,
41 + x_scales: Optional[torch.Tensor] = None,
42 + ) -> torch.Tensor:
43 + M = x.shape[0]
44 + if rocm_use_aiter_fp4_asm_gemm:
45 + if x_scales is None:
46 + # use hip quant kernel for performance
47 + x_q, x_s = per_1x32_f4_quant_hip(x, shuffle=True)
48 + else:
49 + x_q = x
50 + x_s = x_scales
51 +
52 + # 32 alignment is enough for dim0 padding of output for
53 + # gemm_a4w4 kernel
54 + y = torch.empty((M + 31) // 32 * 32,
55 + weight.shape[0],
56 + device=x_q.device,
57 + dtype=out_dtype)
58 +
59 + gemm_a4w4(x_q,
60 + weight,
61 + x_s,
62 + weight_scale.view(x_s.dtype),
63 + y,
64 + bpreshuffle=True)
65 + return y[:M]
66 + else:
67 + if x_scales is None:
68 + x_q, x_s = dynamic_mxfp4_quant(x)
69 + else:
70 + x_q = x
71 + x_s = x_scales
72 + y = torch.empty(x_q.shape[0],
73 + weight.shape[0],
74 + device=x_q.device,
75 + dtype=out_dtype)
76 +
77 + gemm_afp4wfp4(x_q, weight, x_s, weight_scale.T, out_dtype, y)
78 + return y
79 +
80 + def gemm_with_dynamic_quant_fake(
81 + x: torch.Tensor,
82 + weight: torch.Tensor,
83 + weight_scale: torch.Tensor,
84 + x_scales: torch.Tensor = None,
85 + rocm_use_aiter_fp4_asm_gemm: bool = False,
86 + out_dtype: Optional[torch.dtype] = torch.bfloat16,
87 + ) -> torch.Tensor:
88 + return torch.empty((*x.shape[:-1], weight.shape[0]),
89 + dtype=out_dtype,
90 + device=x.device)
91 +
92 + direct_register_custom_op(
93 + op_name="gemm_with_dynamic_quant",
94 + op_func=gemm_with_dynamic_quant,
95 + mutates_args=[],
96 + fake_impl=gemm_with_dynamic_quant_fake,
97 + dispatch_key=current_platform.dispatch_key,
98 + )
99 +
100 +except ImportError:
101 + dynamic_mxfp4_quant = gemm_afp4wfp4 = None
102
103 __all__ = ["QuarkW4A4MXFP4"]
104
@@ -27,29 +111,15 @@ class QuarkW4A4MXFP4(QuarkScheme):
111 self.qscheme = "per_group"
112 self.weight_quant_spec = weight_quant_spec
113 self.input_quant_spec = input_quant_spec
30 -
31 - self.static_input_scales = not input_quant_spec.get("is_dynamic")
32 -
33 - if self.static_input_scales:
114 + self.emulate = not current_platform.supports_mx()
115 + self.rocm_use_aiter_fp4_asm_gemm = is_rocm_aiter_fp4_asm_gemm_enabled()
116 + if not self.emulate and (dynamic_mxfp4_quant is None
117 + or gemm_afp4wfp4 is None):
118 + # Currently need these kernels if not emulating
119 raise NotImplementedError(
35 - "QuarkW4A4MXFP4 with static input scales is currently not "
36 - "implemented. Please open an issue.")
37 -
38 - if not current_platform.supports_mx():
39 - self.emulate = True
40 - logger.warning_once(
41 - "The current platform does not support native MXFP4 "
42 - "computation. Simulated weight dequantization and activation "
43 - "QDQ (quantize and dequantize) will be used, with the linear "
44 - "layers computed in high precision.")
45 - else:
46 - self.emulate = True
47 - logger.warning_once(
48 - "The current platform supports native MXFP4 "
49 - "computation, but kernels are not yet integrated in vLLM. "
50 - "Simulated weight dequantization and activation "
51 - "QDQ (quantize and dequantize) will be used, with the linear "
52 - "layers computed in high precision.")
120 + f"{self.__class__.__name__} requires AITER to be installed "
121 + "for non-emulation mode! Please refer to "
122 + "https://github.com/ROCm/aiter for installation details.")
123
124 @classmethod
125 def get_min_capability(cls) -> int:
@@ -58,8 +128,65 @@ class QuarkW4A4MXFP4(QuarkScheme):
128 def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
129 layer.weight = torch.nn.Parameter(layer.weight.data,
130 requires_grad=False)
61 - layer.weight_scale = torch.nn.Parameter(layer.weight_scale.data,
62 - requires_grad=False)
131 +
132 + if self.emulate:
133 + layer.weight_scale = torch.nn.Parameter(layer.weight_scale.data,
134 + requires_grad=False)
135 + try:
136 + from quark.torch.export.nn.modules import realquantizer
137 + from quark.torch.quantization.config.config import (
138 + QuantizationSpec)
139 + except ImportError as err:
140 + raise ImportError(
141 + "The package `amd-quark` is required to use AMD Quark "
142 + "MX-FP4 models. Please install it with `pip install "
143 + "amd-quark`.") from err
144 +
145 + weight_quant_spec = QuantizationSpec.from_dict(
146 + self.weight_quant_spec)
147 +
148 + weight_quantizer = realquantizer.get_real_quantizer(
149 + qspec=weight_quant_spec,
150 + quantizer=None,
151 + real_quantized=True,
152 + reorder=False,
153 + float_dtype=self.out_dtype,
154 + scale_shape=layer.weight_scale.shape,
155 + zero_point_shape=None,
156 + )
157 + weight_quantizer.scale.data = layer.weight_scale.data
158 +
159 + layer.weight = torch.nn.Parameter(
160 + weight_quantizer(layer.weight.data).to(self.out_dtype),
161 + requires_grad=False,
162 + )
163 + layer.weight_scale = None
164 +
165 + # This call is necessary to release the scales memory.
166 + torch.cuda.empty_cache()
167 + else:
168 + if self.rocm_use_aiter_fp4_asm_gemm:
169 + # shuffle weight scale
170 + weight_scale_shuffle = layer.weight_scale.data
171 + sm, sn = weight_scale_shuffle.shape
172 + weight_scale_shuffle = weight_scale_shuffle.view(
173 + sm // 32, 2, 16, sn // 8, 2, 4, 1)
174 + weight_scale_shuffle = weight_scale_shuffle.permute(
175 + 0, 3, 5, 2, 4, 1, 6).contiguous()
176 + weight_scale_shuffle = weight_scale_shuffle.view(sm, sn)
177 + layer.weight_scale = torch.nn.Parameter(weight_scale_shuffle,
178 + requires_grad=False)
179 +
180 + # shuffle weight
181 + weight_shuffle = layer.weight.data
182 + weight_shuffle = shuffle_weight(weight_shuffle,
183 + layout=(16, 16))
184 + layer.weight = torch.nn.Parameter(weight_shuffle,
185 + requires_grad=False)
186 + else:
187 + layer.weight_scale = torch.nn.Parameter(
188 + layer.weight_scale.data.T.contiguous(),
189 + requires_grad=False)
190
191 def create_weights(self, layer: torch.nn.Module,
192 output_partition_sizes: list[int],
@@ -104,9 +231,9 @@ class QuarkW4A4MXFP4(QuarkScheme):
231
232 if self.emulate:
233 dq_w = dequant_mxfp4(layer.weight, layer.weight_scale, x.dtype)
107 -
234 x = quant_dequant_mxfp4(x)
109 -
235 return F.linear(x, dq_w, bias)
236 else:
112 - raise NotImplementedError()
237 + return torch.ops.vllm.gemm_with_dynamic_quant(
238 + x, layer.weight, layer.weight_scale,
239 + self.rocm_use_aiter_fp4_asm_gemm, self.out_dtype)
vllm/model_executor/layers/rotary_embedding/base.py
+22 -1
@@ -8,6 +8,8 @@ import torch
8 from vllm.model_executor.custom_op import CustomOp
9
10 from .common import apply_rotary_emb_torch
11 +from .rocm_aiter_rope_ops import (is_rocm_triton_rotary_embedding_enabled,
12 + rocm_aiter_rotary_emb)
13
14
15 @CustomOp.register("rotary_embedding")
@@ -45,6 +47,8 @@ class RotaryEmbedding(CustomOp):
47 cache = cache.to(dtype)
48 self.cos_sin_cache: torch.Tensor
49 self.register_buffer("cos_sin_cache", cache, persistent=False)
50 + self.is_rocm_triton_rotary_embedding_enabled = \
51 + is_rocm_triton_rotary_embedding_enabled()
52
53 def _compute_inv_freq(self, base: float) -> torch.Tensor:
54 """Compute the inverse frequency."""
@@ -120,14 +124,31 @@ class RotaryEmbedding(CustomOp):
124 return query, key
125
126 from vllm import _custom_ops as ops
123 -
127 self._match_cos_sin_cache_dtype(query)
128 +
129 # ops.rotary_embedding() is an in-place operation
130 # that updates the query and key tensors.
131 ops.rotary_embedding(positions, query, key, self.head_size,
132 self.cos_sin_cache, self.is_neox_style)
133 return query, key
134
135 + def forward_hip(
136 + self,
137 + positions: torch.Tensor,
138 + query: torch.Tensor,
139 + key: Optional[torch.Tensor] = None,
140 + ) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
141 + if self.is_rocm_triton_rotary_embedding_enabled:
142 + self._match_cos_sin_cache_dtype(query)
143 + rocm_aiter_rotary_emb(positions, query, key, self.cos_sin_cache,
144 + self.head_size, self.rotary_dim,
145 + self.is_neox_style)
146 + else:
147 + # ops.rotary_embedding() is an in-place operation
148 + # that updates the query and key tensors.
149 + self.forward_cuda(positions, query, key)
150 + return query, key
151 +
152 def forward_xpu(
153 self,
154 positions: torch.Tensor,
vllm/model_executor/layers/rotary_embedding/rocm_aiter_rope_ops.py new
+86
@@ -0,0 +1,86 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +
4 +import torch
5 +
6 +import vllm.envs as envs
7 +from vllm.platforms import current_platform
8 +from vllm.utils import direct_register_custom_op
9 +
10 +
11 +def is_rocm_triton_rotary_embedding_enabled() -> bool:
12 + return (current_platform.is_rocm() and envs.VLLM_ROCM_USE_AITER
13 + and envs.VLLM_ROCM_USE_TRITON_ROPE)
14 +
15 +
16 +def rocm_aiter_rotary_emb_with_key_forward_triton_impl(
17 + positions: torch.Tensor,
18 + sin: torch.Tensor,
19 + cos: torch.Tensor,
20 + query: torch.Tensor,
21 + key: torch.Tensor,
22 + rotate_style: int = 0,
23 + is_nope_first: bool = False,
24 +) -> None:
25 + import aiter.ops.triton.rope as ops
26 + ops.rope_cached_thd_positions_2c_fwd_inplace(
27 + query,
28 + key,
29 + cos,
30 + sin,
31 + positions,
32 + rotate_style,
33 + reuse_freqs_front_part=True,
34 + nope_first=is_nope_first,
35 + )
36 +
37 +
38 +def rocm_aiter_rotary_emb_with_key_forward_triton_fake(
39 + positions: torch.Tensor,
40 + sin: torch.Tensor,
41 + cos: torch.Tensor,
42 + query: torch.Tensor,
43 + key: torch.Tensor,
44 + rotate_style: int = 0,
45 + is_nope_first: bool = False,
46 +) -> None:
47 + pass
48 +
49 +
50 +if is_rocm_triton_rotary_embedding_enabled():
51 +
52 + direct_register_custom_op(
53 + op_name="rocm_aiter_rotary_emb_with_key_forward_triton",
54 + op_func=rocm_aiter_rotary_emb_with_key_forward_triton_impl,
55 + mutates_args=["key", "query"],
56 + fake_impl=rocm_aiter_rotary_emb_with_key_forward_triton_fake,
57 + dispatch_key=current_platform.dispatch_key,
58 + )
59 +
60 +
61 +def rocm_aiter_rotary_emb(positions: torch.Tensor, query: torch.Tensor,
62 + key: torch.Tensor, cos_sin_cache: torch.Tensor,
63 + head_size: int, rotary_dim: int,
64 + is_neox_style: bool):
65 + num_tokens = positions.numel()
66 + cos, sin = cos_sin_cache.chunk(2, dim=-1)
67 + query_shape = query.shape
68 + key_shape = key.shape
69 + rotate_style = 0 if is_neox_style else 1
70 +
71 + query = query.view(num_tokens, -1, head_size)
72 + key = key.view(num_tokens, -1, head_size)
73 + query_ = query[..., :rotary_dim]
74 + key_ = key[..., :rotary_dim]
75 + positions = positions.view(*query.shape[:1])
76 + torch.ops.vllm.rocm_aiter_rotary_emb_with_key_forward_triton(
77 + positions,
78 + sin,
79 + cos,
80 + query_,
81 + key_,
82 + rotate_style,
83 + False,
84 + )
85 + query = query.view(query_shape)
86 + key = key.view(key_shape)