@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 1f1b1bc03b
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 15, 08:30 UTC

[V1][Quantization] Add CUDA graph compatible v1 GGUF support (#18646)

Signed-off-by: Isotr0py <mozf@mail2.sysu.edu.cn> Signed-off-by: Isotr0py <2037008807@qq.com>

Isotr0py committed May 27, 2025 at 12:40 UTC 1f1b1bc03bfa36dbd0d884cede6f4dcdf0937e25
5 files changed +188 -59
tests/kernels/quantization/test_gguf.py
+1 -3
@@ -8,7 +8,6 @@ from gguf import GGMLQuantizationType, GGUFReader, ReaderTensor, dequantize
8 from huggingface_hub import snapshot_download
9
10 import vllm._custom_ops as ops
11 -from vllm.model_executor.layers.activation import SiluAndMul
11 from vllm.model_executor.layers.fused_moe import fused_experts
12 from vllm.model_executor.layers.quantization.gguf import _fused_moe_gguf
13 from vllm.platforms import current_platform
@@ -176,12 +175,11 @@ def test_moe(num_tokens: int, hidden_size: int, dtype: torch.dtype,
175
176 w2_dequant = torch.tensor(dequantize(w2.data, quant_type),
177 device="cuda").to(dtype)
179 - act = SiluAndMul()
178
179 output = _fused_moe_gguf(x, torch.tensor(w13.data, device="cuda"),
180 torch.tensor(w2.data,
181 device="cuda"), topk_weights,
184 - topk_ids, quant_type, quant_type, act)
182 + topk_ids, quant_type, quant_type, "silu")
183
184 ref_output = fused_experts(x, w13_dequant, w2_dequant, topk_weights,
185 topk_ids).reshape(output.shape)
tests/models/quantization/test_gguf.py
+6 -2
@@ -78,8 +78,12 @@ DOLPHIN_CONFIG = GGUFTestConfig(
78 )
79
80 MODELS = [
81 - LLAMA_CONFIG, QWEN2_CONFIG, PHI3_CONFIG, GPT2_CONFIG, STABLELM_CONFIG,
82 - DOLPHIN_CONFIG
81 + LLAMA_CONFIG,
82 + QWEN2_CONFIG,
83 + PHI3_CONFIG,
84 + GPT2_CONFIG,
85 + # STABLELM_CONFIG, # enable this when v1 support head_size=80
86 + DOLPHIN_CONFIG,
87 # STARCODER_CONFIG, # broken
88 ]
89
vllm/engine/arg_utils.py
-8
@@ -1291,14 +1291,6 @@ class EngineArgs:
1291 recommend_to_remove=False)
1292 return False
1293
1294 - # Some quantization is not compatible with torch.compile.
1295 - V1_UNSUPPORTED_QUANT = ["gguf"]
1296 - if model_config.quantization in V1_UNSUPPORTED_QUANT:
1297 - _raise_or_fallback(
1298 - feature_name=f"--quantization {model_config.quantization}",
1299 - recommend_to_remove=False)
1300 - return False
1301 -
1294 # No Embedding Models so far.
1295 if model_config.task not in ["generate"]:
1296 _raise_or_fallback(feature_name=f"--task {model_config.task}",
vllm/model_executor/layers/linear.py
-4
@@ -587,8 +587,6 @@ class MergedColumnParallelLinear(ColumnParallelLinear):
587 param.shard_id.append(loaded_shard_id)
588 param.shard_id_map[loaded_shard_id] = len(param.data_container)
589 param.data_container.append(loaded_weight)
590 - if len(param.data_container) == 2:
591 - self.qweight = param.materialize_nested()
590 return
591
592 param_data = param.data
@@ -982,8 +980,6 @@ class QKVParallelLinear(ColumnParallelLinear):
980 param.shard_id.append(loaded_shard_id)
981 param.shard_id_map[loaded_shard_id] = len(param.data_container)
982 param.data_container.append(loaded_weight)
985 - if len(param.data_container) == 3:
986 - self.qweight = param.materialize_nested()
983 return
984
985 param_data = param.data
vllm/model_executor/layers/quantization/gguf.py
+181 -42
@@ -9,7 +9,6 @@ from torch.nn.parameter import Parameter, UninitializedParameter
9
10 from vllm import _custom_ops as ops
11 from vllm.logger import init_logger
12 -from vllm.model_executor.layers.activation import SiluAndMul
12 from vllm.model_executor.layers.fused_moe.layer import (FusedMoE,
13 FusedMoEMethodBase)
14 from vllm.model_executor.layers.linear import LinearBase, LinearMethodBase
@@ -19,6 +18,7 @@ from vllm.model_executor.layers.quantization.base_config import (
18 from vllm.model_executor.layers.vocab_parallel_embedding import (
19 VocabParallelEmbedding)
20 from vllm.model_executor.utils import set_weight_attrs
21 +from vllm.utils import direct_register_custom_op
22
23 logger = init_logger(__name__)
24
@@ -96,8 +96,8 @@ MMVQ_QUANT_TYPES = STANDARD_QUANT_TYPES | KQUANT_TYPES | IMATRIX_QUANT_TYPES
96 MMQ_QUANT_TYPES = STANDARD_QUANT_TYPES | KQUANT_TYPES
97
98
99 -def _fuse_mul_mat(x: torch.Tensor, qweight: torch.Tensor,
100 - qweight_type: int) -> torch.Tensor:
99 +def _fused_mul_mat_gguf(x: torch.Tensor, qweight: torch.Tensor,
100 + qweight_type: int) -> torch.Tensor:
101 # HACK: when doing chunked prefill we don't generate output tokens
102 # so input to logits generator is empty which causes invalid parameter
103 if x.shape[0] == 0:
@@ -130,6 +130,30 @@ def _fuse_mul_mat(x: torch.Tensor, qweight: torch.Tensor,
130 return y
131
132
133 +def _fused_mul_mat_gguf_fake(
134 + x: torch.Tensor,
135 + qweight: torch.Tensor,
136 + qweight_type: int,
137 +) -> torch.Tensor:
138 + return torch.empty(x.shape[0],
139 + qweight.shape[0],
140 + dtype=x.dtype,
141 + device=x.device)
142 +
143 +
144 +try:
145 + direct_register_custom_op(
146 + op_name="_fused_mul_mat_gguf",
147 + op_func=_fused_mul_mat_gguf,
148 + mutates_args=[],
149 + fake_impl=_fused_mul_mat_gguf_fake,
150 + )
151 + fused_mul_mat_gguf = torch.ops.vllm._fused_mul_mat_gguf
152 +
153 +except AttributeError as error:
154 + raise error
155 +
156 +
157 def _fused_moe_gguf(
158 x: torch.Tensor,
159 w1: torch.Tensor,
@@ -138,8 +162,21 @@ def _fused_moe_gguf(
162 topk_ids: torch.Tensor,
163 qweight_type: int,
164 qweight_type2: int,
141 - act,
165 + activation: str,
166 ) -> torch.Tensor:
167 +
168 + def act(x: torch.Tensor):
169 + d = x.shape[-1] // 2
170 + output_shape = (x.shape[:-1] + (d, ))
171 + out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
172 + if activation == "silu":
173 + torch.ops._C.silu_and_mul(out, x)
174 + elif activation == "gelu":
175 + torch.ops._C.gelu_and_mul(out, x)
176 + else:
177 + raise ValueError(f"Unsupported activation: {activation}")
178 + return out
179 +
180 # lazy import to avoid triggering triton import in CPU backend
181 from vllm.model_executor.layers.fused_moe.fused_moe import (
182 moe_align_block_size)
@@ -189,12 +226,12 @@ def _fused_moe_gguf(
226 for ww, ii in zip(w, idx):
227 expert_up = w1[ii]
228
192 - out = _fuse_mul_mat(inp, expert_up, qweight_type)
229 + out = fused_mul_mat_gguf(inp, expert_up, qweight_type)
230 out = act(out)
231
232 expert_down = w2[ii]
196 - current_state = _fuse_mul_mat(out, expert_down,
197 - qweight_type2).mul_(ww)
233 + current_state = fused_mul_mat_gguf(out, expert_down,
234 + qweight_type2).mul_(ww)
235 if current_hidden_state is None:
236 current_hidden_state = current_state
237 else:
@@ -203,6 +240,78 @@ def _fused_moe_gguf(
240 return out_hidden_states
241
242
243 +def _fused_moe_gguf_fake(
244 + x: torch.Tensor,
245 + w1: torch.Tensor,
246 + w2: torch.Tensor,
247 + topk_weights: torch.Tensor,
248 + topk_ids: torch.Tensor,
249 + qweight_type: int,
250 + qweight_type2: int,
251 + activation: str,
252 +) -> torch.Tensor:
253 + return torch.empty_like(x)
254 +
255 +
256 +try:
257 + direct_register_custom_op(
258 + op_name="_fused_moe_gguf",
259 + op_func=_fused_moe_gguf,
260 + mutates_args=[],
261 + fake_impl=_fused_moe_gguf_fake,
262 + )
263 + fused_moe_gguf = torch.ops.vllm._fused_moe_gguf
264 +
265 +except AttributeError as error:
266 + raise error
267 +
268 +
269 +def _apply_gguf_embedding(
270 + x: torch.Tensor,
271 + qweight: torch.Tensor,
272 + qweight_type: int,
273 + hidden_size: int,
274 + dtype: Optional[torch.dtype] = None,
275 +) -> torch.Tensor:
276 + if qweight_type in UNQUANTIZED_TYPES:
277 + return torch.embedding(qweight, x)
278 + elif qweight_type in DEQUANT_TYPES:
279 + block_size, type_size = gguf.GGML_QUANT_SIZES[qweight_type]
280 + x_flat = x.flatten()
281 + assert (hidden_size == qweight.shape[1] // type_size * block_size)
282 + quant = torch.index_select(qweight, dim=0, index=x_flat)
283 + dequant = ops.ggml_dequantize(quant, qweight_type, hidden_size,
284 + x_flat.shape[0], dtype)
285 + return dequant.view(*x.shape, hidden_size)
286 + else:
287 + qweight_type = WeightType(qweight_type)
288 + raise NotImplementedError(
289 + f"Unsupported GGUF quantization type: {qweight_type}")
290 +
291 +
292 +def _apply_gguf_embedding_fake(
293 + x: torch.Tensor,
294 + qweight: torch.Tensor,
295 + qweight_type: int,
296 + hidden_size: int,
297 + dtype: Optional[torch.dtype] = None,
298 +) -> torch.Tensor:
299 + return torch.empty(x.shape[0], hidden_size, dtype=dtype, device=x.device)
300 +
301 +
302 +try:
303 + direct_register_custom_op(
304 + op_name="_apply_gguf_embedding",
305 + op_func=_apply_gguf_embedding,
306 + mutates_args=[],
307 + fake_impl=_apply_gguf_embedding_fake,
308 + )
309 + apply_gguf_embedding = torch.ops.vllm._apply_gguf_embedding
310 +
311 +except AttributeError as error:
312 + raise error
313 +
314 +
315 class GGUFLinearMethod(LinearMethodBase):
316 """Linear method for GGUF.
317
@@ -249,26 +358,76 @@ class GGUFLinearMethod(LinearMethodBase):
358 set_weight_attrs(qweight_type, extra_weight_attrs)
359 layer.register_parameter("qweight_type", qweight_type)
360
361 + def process_weights_after_loading(self, layer: torch.nn.Module):
362 + qweight_type = layer.qweight_type.weight_type
363 + if not (qweight_type in UNQUANTIZED_TYPES
364 + or qweight_type in DEQUANT_TYPES):
365 + qweight_type = WeightType(qweight_type)
366 + raise ValueError(
367 + f"Unsupported GGUF quantization type {qweight_type} in "
368 + f"layer {layer}.")
369 + # For MergedColumnParallelLinear and QKVParallelLinear, we need to
370 + # materialize the padded weight parameter for CUDA Graph compatibility.
371 + self._create_padded_weight_param(layer)
372 +
373 + def _create_padded_weight_param(self, layer: torch.nn.Module):
374 + """Create padded weight parameter for GGUF MergedLinear layer."""
375 + qweight = layer.qweight
376 + shard_id_map = qweight.shard_id_map
377 + shard_id = qweight.shard_id
378 + if len(data_container := qweight.data_container) > 1:
379 + dtype = {data.dtype for data in data_container}
380 + assert len(dtype) == 1, ValueError(
381 + f"Data container has mixed dtypes: {dtype}")
382 + dtype = next(iter(dtype))
383 + # concat dim0 and pad dim1
384 + padded_side = max(x.size(1) for x in data_container)
385 + concat_side = sum(x.size(0) for x in data_container)
386 + # Pad the quantized weights to dense tensor, and create a map
387 + # with the location of each shard in the padded tensor.
388 + padded_data = torch.zeros((concat_side, padded_side),
389 + dtype=dtype,
390 + device=qweight.device)
391 + # (dim0_start, dim0_end, dim1_size)
392 + shard_offset_map = dict[str, tuple[int, int, int]]()
393 + for idx in shard_id:
394 + id_in_container = shard_id_map[idx]
395 + start = sum(
396 + x.size(0) for x in data_container[:id_in_container])
397 + end = start + data_container[id_in_container].size(0)
398 + size = data_container[id_in_container].size(1)
399 + padded_data[start:end, :size] = data_container[id_in_container]
400 + shard_offset_map[idx] = (start, end, size)
401 + qweight.data_container.clear()
402 + padded_param = Parameter(padded_data, requires_grad=False)
403 + set_weight_attrs(padded_param, vars(qweight))
404 + set_weight_attrs(padded_param,
405 + {"shard_offset_map": shard_offset_map})
406 + layer.register_parameter("qweight", padded_param)
407 +
408 def apply(self,
409 layer: torch.nn.Module,
410 x: torch.Tensor,
411 bias: Optional[torch.Tensor] = None) -> torch.Tensor:
256 - shard_id = getattr(layer.qweight, "shard_id", None)
412 + shard_id = layer.qweight.shard_id
413
414 if shard_id:
415 # dequantize shard weights respectively
416 shard_id = ["q", "k", "v"] if "q" in shard_id else shard_id
261 - qweight = layer.qweight.unbind(0)
417 + qweight = layer.qweight
418 result = []
419 for idx in shard_id:
264 - q_idx = layer.qweight.shard_id_map[idx]
420 + start, end, offset = layer.qweight.shard_offset_map[idx]
421 qweight_type = layer.qweight_type.shard_weight_type[idx]
266 - result.append(_fuse_mul_mat(x, qweight[q_idx], qweight_type))
422 + result.append(
423 + fused_mul_mat_gguf(
424 + x, qweight[start:end, :offset].contiguous(),
425 + qweight_type))
426 out = torch.cat(result, axis=1)
427 else:
428 qweight = layer.qweight
429 qweight_type = layer.qweight_type.weight_type
271 - out = _fuse_mul_mat(x, qweight, qweight_type)
430 + out = fused_mul_mat_gguf(x, qweight, qweight_type)
431 if bias is not None:
432 out.add_(bias)
433 return out
@@ -338,7 +497,6 @@ class GGUFMoEMethod(FusedMoEMethodBase):
497
498 set_weight_attrs(w2_qweight_type, extra_weight_attrs)
499 layer.register_parameter("w2_qweight_type", w2_qweight_type)
341 - self.act = SiluAndMul()
500
501 def apply(
502 self,
@@ -375,10 +533,10 @@ class GGUFMoEMethod(FusedMoEMethodBase):
533 custom_routing_function=custom_routing_function,
534 scoring_func=scoring_func,
535 e_score_correction_bias=e_score_correction_bias)
378 - return _fused_moe_gguf(x, layer.w13_qweight, layer.w2_qweight,
379 - topk_weights, topk_ids,
380 - layer.w13_qweight_type.weight_type,
381 - layer.w2_qweight_type.weight_type, self.act)
536 + return fused_moe_gguf(x, layer.w13_qweight, layer.w2_qweight,
537 + topk_weights, topk_ids,
538 + layer.w13_qweight_type.weight_type,
539 + layer.w2_qweight_type.weight_type, activation)
540
541
542 class GGUFEmbeddingMethod(GGUFLinearMethod):
@@ -392,34 +550,15 @@ class GGUFEmbeddingMethod(GGUFLinearMethod):
550 x: torch.Tensor) -> torch.Tensor:
551 qweight = layer.qweight
552 qweight_type = layer.qweight_type.weight_type
553 + hidden_size = qweight.tensor_shape[1]
554
396 - block_size, type_size = gguf.GGML_QUANT_SIZES[qweight_type]
397 - hidden_size = qweight.shape[1] // type_size * block_size
398 - if qweight_type < 2:
399 - return torch.embedding(qweight, x)
400 - x_flat = x.flatten()
401 - quant = torch.index_select(qweight, dim=0, index=x_flat)
402 - dequant = ops.ggml_dequantize(quant, qweight_type, hidden_size,
403 - x_flat.shape[0], self.params_dtype)
404 - return dequant.view(*x.shape, hidden_size)
555 + return apply_gguf_embedding(x,
556 + qweight,
557 + qweight_type,
558 + hidden_size,
559 + dtype=self.params_dtype)
560
561
562 class GGUFUninitializedParameter(UninitializedParameter):
563 cls_to_become = Parameter
564 data_container: list[torch.Tensor]
410 -
411 - def materialize_nested(self) -> Parameter:
412 - dtype = {data.dtype for data in self.data_container}
413 - assert len(dtype) == 1, ValueError(
414 - f"Data container has mixed dtypes: {dtype}")
415 - dtype = next(iter(dtype))
416 - nested_data = torch.nested.nested_tensor(self.data_container,
417 - device=self.device,
418 - dtype=dtype)
419 - self.data_container.clear()
420 - param = torch.Tensor._make_subclass(self.cls_to_become,
421 - nested_data,
422 - require_grad=False)
423 - for k, v in self.__dict__.items():
424 - setattr(param, k, v)
425 - return param