Skip to content

Commit e400a5b

Browse files
committed
Add TP3 lowbit and profiling probes
1 parent aac884c commit e400a5b

5 files changed

Lines changed: 363 additions & 2 deletions

File tree

vllm/distributed/device_communicators/lowbit_all_reduce.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
logger = init_logger(__name__)
2323
_INT8_WORKSPACES: dict[tuple[int, int, int, int], tuple[torch.Tensor, torch.Tensor]] = {}
24+
_INT8_BF16PACK_WORKSPACES: dict[
25+
tuple[int, int, int, int], tuple[torch.Tensor, torch.Tensor, int]
26+
] = {}
2427
_FP4_WORKSPACES: dict[tuple[int, int, int, int], tuple[torch.Tensor, torch.Tensor]] = {}
2528
_SHARED_ROW_WORKSPACES: dict[tuple[int, int, int], tuple[torch.Tensor, torch.Tensor]] = {}
2629

@@ -287,6 +290,7 @@ def should_use_tp3_lowbit_hidden_reduce(
287290
"fp4_block16",
288291
"fp4_block16_packed",
289292
"int8_shared_row",
293+
"int8_bf16pack",
290294
)
291295
and should_use_tp3_int8_hidden_reduce(input_, world_size, min_tokens)
292296
)
@@ -328,6 +332,29 @@ def _int8_workspace(
328332
return send, gathered
329333

330334

335+
def _int8_bf16pack_workspace(
336+
device: torch.device,
337+
world_size: int,
338+
rows: int,
339+
cols: int,
340+
) -> tuple[torch.Tensor, torch.Tensor, int]:
341+
device_index = device.index if device.index is not None else torch.cuda.current_device()
342+
q_nbytes = rows * cols
343+
s_nbytes = rows * 4
344+
total_nbytes = q_nbytes + s_nbytes
345+
packed_numel = (total_nbytes + 1) // 2
346+
key = (device_index, world_size, rows, cols)
347+
cached = _INT8_BF16PACK_WORKSPACES.get(key)
348+
if cached is not None and cached[0].numel() == packed_numel:
349+
return cached
350+
send = torch.empty(packed_numel, device=device, dtype=torch.bfloat16)
351+
gathered = torch.empty((world_size, packed_numel), device=device,
352+
dtype=torch.bfloat16)
353+
cached = (send, gathered, total_nbytes)
354+
_INT8_BF16PACK_WORKSPACES[key] = cached
355+
return cached
356+
357+
331358
def _fp4_workspace(
332359
device: torch.device,
333360
world_size: int,
@@ -403,6 +430,58 @@ def tp3_int8_hidden_all_reduce(
403430
return out
404431

405432

433+
def tp3_int8_bf16pack_hidden_all_reduce(
434+
input_: torch.Tensor,
435+
group: ProcessGroup,
436+
) -> torch.Tensor:
437+
"""Approximate SUM all-reduce with int8 bytes transported as BF16.
438+
439+
This does not use BF16 arithmetic. The BF16 tensor is only a 16-bit NCCL
440+
transport container for the existing int8 codes plus fp32 row scales.
441+
"""
442+
assert triton is not None
443+
world_size = dist.get_world_size(group)
444+
assert world_size == 3
445+
rows, cols = input_.shape
446+
trace, trace_sync = _trace_enabled(input_)
447+
t0 = _mark_trace(trace, trace_sync, input_.device)
448+
q_nbytes = rows * cols
449+
send_bf16, gathered_bf16, total_nbytes = _int8_bf16pack_workspace(
450+
input_.device, world_size, rows, cols
451+
)
452+
send = send_bf16.view(torch.uint8)[:total_nbytes]
453+
gathered = gathered_bf16.view(torch.uint8).view(world_size, -1)[:, :total_nbytes]
454+
q_local = send[:q_nbytes].view(rows, cols)
455+
s_local = send[q_nbytes:].view(torch.float32)
456+
block = triton.next_power_of_2(cols)
457+
_quantize_i8_row_kernel[(rows,)](input_, q_local, s_local, cols, block)
458+
t1 = _mark_trace(trace, trace_sync, input_.device)
459+
dist.all_gather_into_tensor(gathered_bf16, send_bf16, group=group)
460+
t2 = _mark_trace(trace, trace_sync, input_.device)
461+
out = torch.empty_like(input_)
462+
q_gathered = gathered[:, :q_nbytes].view(world_size, rows, cols)
463+
s_gathered = gathered[:, q_nbytes:].view(torch.float32).view(world_size, rows)
464+
_dequant_sum_i8_row_kernel[(rows,)](
465+
q_gathered[0], q_gathered[1], q_gathered[2],
466+
s_gathered[0], s_gathered[1], s_gathered[2],
467+
out, cols, block,
468+
)
469+
t3 = _mark_trace(trace, trace_sync, input_.device)
470+
if trace:
471+
logger.warning(
472+
"AG2_LOWBIT_REDUCE_TRACE mode=int8_bf16pack tokens=%d hidden=%d "
473+
"quant=%.6f gather=%.6f dequant=%.6f total=%.6f payload_mib=%.3f",
474+
rows,
475+
cols,
476+
t1 - t0,
477+
t2 - t1,
478+
t3 - t2,
479+
t3 - t0,
480+
send_bf16.numel() * send_bf16.element_size() / 2**20,
481+
)
482+
return out
483+
484+
406485
def tp3_int8_tensor_hidden_all_reduce(
407486
input_: torch.Tensor,
408487
group: ProcessGroup,
@@ -588,6 +667,8 @@ def tp3_lowbit_hidden_all_reduce(
588667
group: ProcessGroup,
589668
mode: str,
590669
) -> torch.Tensor:
670+
if mode == "int8_bf16pack":
671+
return tp3_int8_bf16pack_hidden_all_reduce(input_, group)
591672
if mode == "int8_shared_row":
592673
return tp3_int8_shared_row_hidden_all_reduce(input_, group)
593674
if mode == "int8_tensor":

vllm/model_executor/layers/linear.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
33

44
import itertools
5+
import os
6+
import time
57
from abc import abstractmethod
68
from collections.abc import Iterable
79
from typing import Any
@@ -45,6 +47,33 @@
4547

4648
logger = init_logger(__name__)
4749

50+
51+
def _ag2_row_linear_trace_bool(name: str, default: str = "0") -> bool:
52+
return os.environ.get(name, default).strip().lower() in ("1", "true", "yes", "on")
53+
54+
55+
def _ag2_row_linear_trace_enabled(prefix: str, input_: torch.Tensor) -> bool:
56+
if not _ag2_row_linear_trace_bool("AG2_VLLM_ROW_LINEAR_TRACE"):
57+
return False
58+
min_tokens = int(os.environ.get("AG2_VLLM_ROW_LINEAR_TRACE_MIN_TOKENS", "8192"))
59+
if input_.dim() == 0 or int(input_.shape[0]) < min_tokens:
60+
return False
61+
prefix_filter = os.environ.get("AG2_VLLM_ROW_LINEAR_TRACE_PREFIX", "")
62+
if prefix_filter and prefix_filter not in prefix:
63+
return False
64+
if _ag2_row_linear_trace_bool("AG2_VLLM_ROW_LINEAR_TRACE_RANK0_ONLY", "1"):
65+
if os.environ.get("LOCAL_RANK") is not None:
66+
return os.environ.get("LOCAL_RANK") == "0"
67+
if torch.cuda.is_available():
68+
return torch.cuda.current_device() == 0
69+
return True
70+
71+
72+
def _ag2_row_linear_mark(enabled: bool, device: torch.device) -> float:
73+
if enabled and _ag2_row_linear_trace_bool("AG2_VLLM_ROW_LINEAR_TRACE_SYNC"):
74+
torch.cuda.synchronize(device)
75+
return time.perf_counter()
76+
4877
WEIGHT_LOADER_V2_SUPPORTED = [
4978
"UnquantizedLinearMethod",
5079
"CompressedTensorsLinearMethod",
@@ -2203,24 +2232,47 @@ def forward(
22032232
self,
22042233
input_,
22052234
) -> torch.Tensor | tuple[torch.Tensor, Parameter | None]:
2235+
trace_enabled = _ag2_row_linear_trace_enabled(self.prefix, input_)
2236+
device = input_.device
2237+
t0 = _ag2_row_linear_mark(trace_enabled, device) if trace_enabled else 0.0
22062238
if self.input_is_parallel:
22072239
input_parallel = input_
22082240
else:
22092241
split_input = split_tensor_along_last_dim(
22102242
input_, num_partitions=self.tp_size
22112243
)
22122244
input_parallel = split_input[self.tp_rank].contiguous()
2245+
t1 = _ag2_row_linear_mark(trace_enabled, device) if trace_enabled else 0.0
22132246

22142247
# Matrix multiply.
22152248
# Only fuse bias add into GEMM for rank 0 (this ensures that
22162249
# bias will not get added more than once in TP>1 case)
22172250
bias_ = None if (self.tp_rank > 0 or self.skip_bias_add) else self.bias
22182251
output_parallel = self.quant_method.apply(self, input_parallel, bias_)
2252+
t2 = _ag2_row_linear_mark(trace_enabled, device) if trace_enabled else 0.0
22192253

22202254
if self.reduce_results and self.tp_size > 1:
22212255
output = tensor_model_parallel_all_reduce(output_parallel)
22222256
else:
22232257
output = output_parallel
2258+
t3 = _ag2_row_linear_mark(trace_enabled, device) if trace_enabled else 0.0
2259+
if trace_enabled:
2260+
logger.warning(
2261+
"AG2_ROW_LINEAR_TRACE prefix=%s tokens=%d input_shape=%s "
2262+
"output_shape=%s tp_size=%d reduce=%s quant=%s split=%.6f "
2263+
"gemm=%.6f all_reduce=%.6f total=%.6f",
2264+
self.prefix,
2265+
int(input_.shape[0]) if input_.dim() > 0 else 0,
2266+
tuple(input_.shape),
2267+
tuple(output.shape),
2268+
self.tp_size,
2269+
self.reduce_results and self.tp_size > 1,
2270+
self.quant_method.__class__.__name__,
2271+
t1 - t0,
2272+
t2 - t1,
2273+
t3 - t2,
2274+
t3 - t0,
2275+
)
22242276

22252277
if not self.return_bias:
22262278
return output

0 commit comments

Comments
 (0)