Skip to content

Commit 301bd9f

Browse files
optim speed
1 parent de6e892 commit 301bd9f

1 file changed

Lines changed: 144 additions & 107 deletions

File tree

vllm_fl/dispatch/backends/vendor/gcu/impl/per_token_group_quant_fp8.py

Lines changed: 144 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
# Copyright (c) 2026 BAAI. All rights reserved.
22

3-
"""GCU fix for _per_token_group_quant_fp8 / _per_token_group_quant_fp8_colmajor.
3+
"""GCU fix for per_token_group_quant_fp8 / per_token_group_quant_fp8_colmajor.
44
5-
GCU hardware limits grid.x to 65535 and grid.y/grid.z to 255. The upstream
6-
launcher uses ``grid = (M,)`` where ``M = numel // group_size``, which exceeds
7-
the limit for large tensors (e.g. 8192 × 4096 ÷ 128 = 262144).
5+
GCU (L600/Libra) hardware limits:
6+
- Grid (SP 数量): <= 48 (1-D only, no 2-D grid)
7+
- DSM (片上共享存储): <= 448 KB
8+
- num_warps: <= 4
9+
- 禁止 int64 索引,统一使用 int32
810
9-
This patch converts the 1-D grid into a 2-D grid that respects GCU limits,
10-
following the same pattern as ``fused_recurrent_packed_decode.py``.
11+
The upstream launcher uses ``grid = (M,)`` where ``M = numel // group_size``,
12+
which exceeds the limit for large tensors (e.g. 8192 × 4096 ÷ 128 = 262144).
13+
14+
This patch converts the launch to GCU Fixed-Grid + strided-loop pattern:
15+
- grid = (min(M, GCU_NUM_GRID),) — 1-D only
16+
- kernel 内跨步循环 for g_id in range(pid, M, GCU_NUM_GRID)
1117
"""
1218

1319
from __future__ import annotations
@@ -20,36 +26,43 @@
2026

2127
logger = logging.getLogger(__name__)
2228

23-
GCU_MAX_GRID_X = 65535
24-
GCU_MAX_GRID_YZ = 255
29+
# ---------------------------------------------------------------------------
30+
# GCU (L600 / Libra) hardware constants
31+
# ---------------------------------------------------------------------------
32+
# S60 板卡: GCU_NUM_GRID = 24, GCU_MAX_DSM_MEMORY = int(1.5 * 1024 * 1024)
33+
# Libra 板卡: GCU_NUM_GRID = 48, GCU_MAX_DSM_MEMORY = 917504 // 2
34+
GCU_NUM_GRID = 48
35+
GCU_MAX_DSM_MEMORY = 917504 // 2 # 448 KB
36+
2537
_patched = False
2638

2739

2840
# ---------------------------------------------------------------------------
2941
# Grid helpers
3042
# ---------------------------------------------------------------------------
3143

32-
def _gcu_grid(total: int) -> tuple[int, int]:
33-
"""Split *total* work-items into a 2-D grid respecting GCU hardware limits."""
34-
grid_x = min(total, GCU_MAX_GRID_X)
35-
grid_y = triton.cdiv(total, grid_x)
36-
if grid_y > GCU_MAX_GRID_YZ:
37-
grid_y = GCU_MAX_GRID_YZ
38-
grid_x = triton.cdiv(total, grid_y)
39-
grid_x = min(grid_x, GCU_MAX_GRID_X)
40-
return grid_x, grid_y
44+
def _gcu_grid(total: int) -> tuple[int, ...]:
45+
"""Return a 1-D grid tuple respecting GCU hardware limits (grid <= GCU_NUM_GRID).
46+
47+
GCU only supports 1-D grid; the kernel uses a strided loop internally
48+
to cover all *total* work-items.
49+
"""
50+
return (min(total, GCU_NUM_GRID),)
4151

4252

4353
# ---------------------------------------------------------------------------
44-
# GCU-compatible Triton kernels
54+
# GCU-compatible Triton kernels (Fixed-Grid + strided loop)
4555
# ---------------------------------------------------------------------------
4656

57+
4758
@triton.jit
4859
def _per_token_group_quant_fp8_gcu(
4960
# Pointers to inputs and output
5061
y_ptr,
5162
y_q_ptr,
5263
y_s_ptr,
64+
# Total number of groups (M = numel // group_size)
65+
total_groups,
5366
group_size,
5467
# Num columns of y
5568
y_num_columns,
@@ -62,46 +75,49 @@ def _per_token_group_quant_fp8_gcu(
6275
use_ue8m0: tl.constexpr,
6376
# Meta-parameters
6477
BLOCK: tl.constexpr,
78+
NUM_SPC: tl.constexpr,
6579
):
66-
"""A Triton-accelerated function to perform per-token-group
67-
quantization on a tensor.
80+
"""GCU Fixed-Grid per-token-group FP8 quantization (row-major scales).
6881
69-
GCU variant: uses 2-D grid (program_id 0 + 1) so that grid.x never
70-
exceeds the hardware limit of 65535.
82+
Uses 1-D Fixed Grid + strided loop so that grid never exceeds
83+
GCU_NUM_GRID (48 for L600). Each program loops over multiple groups
84+
via ``for g_id in range(pid, total_groups, NUM_SPC)``.
7185
"""
7286
groups_per_row = y_num_columns // group_size
87+
pid = tl.program_id(0)
7388

74-
# Map the 2-D program id to a flat group id.
75-
g_id = tl.program_id(0) + tl.program_id(1) * tl.num_programs(0)
76-
row = g_id // groups_per_row
77-
row_g_id = g_id % groups_per_row
89+
# Strided loop: each SP handles work items pid, pid+NUM_SPC, pid+2*NUM_SPC, ...
90+
for g_id in range(pid, total_groups, NUM_SPC):
91+
row = g_id // groups_per_row
92+
row_g_id = g_id % groups_per_row
7893

79-
# Ensure offset calculations use int64 to prevent overflow
80-
y_ptr_offset = (row.to(tl.int64) * y_row_stride) + (
81-
row_g_id.to(tl.int64) * group_size
82-
)
83-
y_ptr += y_ptr_offset
94+
# Offset calculations use int32 (GCU constraint: no int64 indexing)
95+
y_ptr_offset = (
96+
row.to(tl.int32) * y_row_stride.to(tl.int32)
97+
+ row_g_id.to(tl.int32) * group_size.to(tl.int32)
98+
)
99+
y_cur = y_ptr + y_ptr_offset
100+
101+
y_q_cur = y_q_ptr + g_id.to(tl.int32) * group_size.to(tl.int32)
102+
y_s_cur = y_s_ptr + g_id
84103

85-
y_q_ptr_offset = g_id.to(tl.int64) * group_size
86-
y_q_ptr += y_q_ptr_offset
87-
y_s_ptr += g_id
104+
cols = tl.arange(0, BLOCK) # group_size <= BLOCK
105+
mask = cols < group_size
88106

89-
cols = tl.arange(0, BLOCK) # N <= BLOCK
90-
mask = cols < group_size
107+
y = tl.load(y_cur + cols, mask=mask, other=0.0).to(tl.float32)
91108

92-
y = tl.load(y_ptr + cols, mask=mask, other=0.0).to(tl.float32)
93-
# Quant
94-
# Use multiply-by-reciprocal instead of division to match PyTorch's
95-
# tensor/scalar division precision (GPU fast-division for constexpr
96-
# divisors can introduce 1-ULP error that flips FP8 quantization at
97-
# representable-value boundaries).
98-
_absmax = tl.maximum(tl.max(tl.abs(y)), eps)
99-
scale_raw = _absmax * (1.0 / fp8_max)
100-
y_s = tl.math.exp2(tl.ceil(tl.log2(scale_raw))) if use_ue8m0 else scale_raw
101-
y_q = tl.clamp(y / y_s, fp8_min, fp8_max).to(y_q_ptr.dtype.element_ty)
109+
# Quant — multiply-by-reciprocal avoids GPU fast-division 1-ULP error
110+
_absmax = tl.maximum(tl.max(tl.abs(y)), eps)
111+
scale_raw = _absmax * (1.0 / fp8_max)
112+
y_s = (
113+
tl.math.exp2(tl.ceil(tl.log2(scale_raw)))
114+
if use_ue8m0
115+
else scale_raw
116+
)
117+
y_q = tl.clamp(y / y_s, fp8_min, fp8_max).to(y_q_ptr.dtype.element_ty)
102118

103-
tl.store(y_q_ptr + cols, y_q, mask=mask)
104-
tl.store(y_s_ptr, y_s)
119+
tl.store(y_q_cur + cols, y_q, mask=mask)
120+
tl.store(y_s_cur, y_s)
105121

106122

107123
@triton.jit
@@ -110,6 +126,8 @@ def _per_token_group_quant_fp8_colmajor_gcu(
110126
y_ptr,
111127
y_q_ptr,
112128
y_s_ptr,
129+
# Total number of groups (M = numel // group_size)
130+
total_groups,
113131
group_size,
114132
# Num columns of y
115133
y_num_columns,
@@ -124,50 +142,55 @@ def _per_token_group_quant_fp8_colmajor_gcu(
124142
use_ue8m0: tl.constexpr,
125143
# Meta-parameters
126144
BLOCK: tl.constexpr,
145+
NUM_SPC: tl.constexpr,
127146
):
128-
"""A Triton-accelerated function to perform per-token-group
129-
quantization on a tensor (column-major scales).
147+
"""GCU Fixed-Grid per-token-group FP8 quantization (column-major scales).
130148
131-
GCU variant: uses 2-D grid (program_id 0 + 1) so that grid.x never
132-
exceeds the hardware limit of 65535.
149+
Uses 1-D Fixed Grid + strided loop. Scale tensor is written in
150+
column-major order (shape [M, sf_k], stride [1, tma_aligned_m]).
133151
"""
134152
groups_per_row = y_num_columns // group_size
153+
pid = tl.program_id(0)
135154

136-
# Map the 2-D program id to a flat group id.
137-
g_id = tl.program_id(0) + tl.program_id(1) * tl.num_programs(0)
138-
row = g_id // groups_per_row
139-
row_g_id = g_id % groups_per_row
155+
for g_id in range(pid, total_groups, NUM_SPC):
156+
row = g_id // groups_per_row
157+
row_g_id = g_id % groups_per_row
140158

141-
# Ensure offset calculations use int64 to prevent overflow
142-
y_ptr_offset = (row.to(tl.int64) * y_row_stride) + (
143-
row_g_id.to(tl.int64) * group_size
144-
)
145-
y_ptr += y_ptr_offset
159+
y_ptr_offset = (
160+
row.to(tl.int32) * y_row_stride.to(tl.int32)
161+
+ row_g_id.to(tl.int32) * group_size.to(tl.int32)
162+
)
163+
y_cur = y_ptr + y_ptr_offset
164+
165+
y_q_cur = y_q_ptr + g_id.to(tl.int32) * group_size.to(tl.int32)
146166

147-
y_q_ptr_offset = g_id.to(tl.int64) * group_size
148-
y_q_ptr += y_q_ptr_offset
167+
# Column-major scale indexing
168+
blocks_per_row = groups_per_row
169+
scale_col = g_id % blocks_per_row
170+
scale_row = g_id // blocks_per_row
171+
y_s_offset = (
172+
scale_col.to(tl.int32) * y_s_col_stride.to(tl.int32)
173+
+ scale_row.to(tl.int32)
174+
)
175+
y_s_cur = y_s_ptr + y_s_offset
149176

150-
# Convert g_id the flattened block coordinate to 2D so we can index
151-
# into the output y_scales matrix
152-
blocks_per_row = y_num_columns // group_size
153-
scale_col = g_id % blocks_per_row
154-
scale_row = g_id // blocks_per_row
155-
# Ensure offset calculation uses int64 for y_s_ptr
156-
y_s_ptr_offset = (scale_col.to(tl.int64) * y_s_col_stride) + scale_row.to(tl.int64)
157-
y_s_ptr += y_s_ptr_offset
177+
cols = tl.arange(0, BLOCK)
178+
mask = cols < group_size
158179

159-
cols = tl.arange(0, BLOCK) # group_size <= BLOCK
160-
mask = cols < group_size
180+
y = tl.load(y_cur + cols, mask=mask, other=0.0).to(tl.float32)
161181

162-
y = tl.load(y_ptr + cols, mask=mask, other=0.0).to(tl.float32)
163-
# Quant
164-
_absmax = tl.maximum(tl.max(tl.abs(y)), eps)
165-
scale_raw = _absmax * (1.0 / fp8_max)
166-
y_s = tl.math.exp2(tl.ceil(tl.log2(scale_raw))) if use_ue8m0 else scale_raw
167-
y_q = tl.clamp(y / y_s, fp8_min, fp8_max).to(y_q_ptr.dtype.element_ty)
182+
# Quant
183+
_absmax = tl.maximum(tl.max(tl.abs(y)), eps)
184+
scale_raw = _absmax * (1.0 / fp8_max)
185+
y_s = (
186+
tl.math.exp2(tl.ceil(tl.log2(scale_raw)))
187+
if use_ue8m0
188+
else scale_raw
189+
)
190+
y_q = tl.clamp(y / y_s, fp8_min, fp8_max).to(y_q_ptr.dtype.element_ty)
168191

169-
tl.store(y_q_ptr + cols, y_q, mask=mask)
170-
tl.store(y_s_ptr, y_s)
192+
tl.store(y_q_cur + cols, y_q, mask=mask)
193+
tl.store(y_s_cur, y_s)
171194

172195

173196
# ---------------------------------------------------------------------------
@@ -187,8 +210,8 @@ def per_token_group_quant_fp8_gcu(
187210
"""GCU-compatible version of ``per_token_group_quant_fp8``.
188211
189212
Identical semantics to the upstream function, but launches Triton kernels
190-
with a 2-D grid that respects GCU hardware limits
191-
(grid.x ≤ 65535, grid.y ≤ 255).
213+
with GCU Fixed-Grid (1-D, ≤48) + strided-loop pattern instead of a
214+
potentially oversized 1-D grid.
192215
"""
193216
from vllm.model_executor.layers.quantization.utils.quant_utils import (
194217
get_fp8_min_max,
@@ -255,46 +278,60 @@ def per_token_group_quant_fp8_gcu(
255278
)
256279
return x_q, x_s
257280

258-
# TRITON FALLBACK – use 2-D grid for GCU
259-
M = x.numel() // group_size
281+
# --- GCU Triton fallback: Fixed-Grid + strided loop ---
282+
M = x.numel() // group_size # total number of groups
260283
N = group_size
261284
BLOCK = triton.next_power_of_2(N)
262-
# heuristics for number of warps
263-
num_warps = min(max(BLOCK // 256, 1), 8)
285+
286+
# Heuristics for number of warps (GCU: must be <= 4)
287+
num_warps = min(max(BLOCK // 256, 1), 4)
264288
num_stages = 1
265289

290+
# DSM sanity check (GCU L600: ≤ 448 KB)
291+
# Peak vectors per iteration: y (input) + y_q (output) ≈ 2
292+
# For BLOCK=128, fp16: 2 * 128 * 2 = 512 bytes — well within 448 KB
293+
_dsm_bytes = 2 * BLOCK * x.element_size()
294+
assert _dsm_bytes <= GCU_MAX_DSM_MEMORY, (
295+
f"DSM estimate {_dsm_bytes} bytes exceeds GCU limit "
296+
f"{GCU_MAX_DSM_MEMORY} bytes. Reduce group_size or BLOCK."
297+
)
298+
299+
# 1-D Fixed Grid (GCU only supports 1-D grid)
266300
grid = _gcu_grid(M)
301+
assert grid[0] <= GCU_NUM_GRID, (
302+
f"Grid {grid[0]} exceeds GCU_NUM_GRID {GCU_NUM_GRID}"
303+
)
267304

268305
if column_major_scales:
269306
_per_token_group_quant_fp8_colmajor_gcu[grid](
270-
x,
271-
x_q,
272-
x_s,
273-
group_size,
274-
x.shape[1],
275-
x.stride(0),
276-
x_s.stride(1),
277-
eps,
307+
x, x_q, x_s,
308+
total_groups=M,
309+
group_size=group_size,
310+
y_num_columns=x.shape[1],
311+
y_row_stride=x.stride(0),
312+
y_s_col_stride=x_s.stride(1),
313+
eps=eps,
278314
fp8_min=fp8_min,
279315
fp8_max=fp8_max,
280316
use_ue8m0=use_ue8m0,
281317
BLOCK=BLOCK,
318+
NUM_SPC=GCU_NUM_GRID,
282319
num_warps=num_warps,
283320
num_stages=num_stages,
284321
)
285322
else:
286323
_per_token_group_quant_fp8_gcu[grid](
287-
x,
288-
x_q,
289-
x_s,
290-
group_size,
291-
x.shape[1],
292-
x.stride(0),
293-
eps,
324+
x, x_q, x_s,
325+
total_groups=M,
326+
group_size=group_size,
327+
y_num_columns=x.shape[1],
328+
y_row_stride=x.stride(0),
329+
eps=eps,
294330
fp8_min=fp8_min,
295331
fp8_max=fp8_max,
296332
use_ue8m0=use_ue8m0,
297333
BLOCK=BLOCK,
334+
NUM_SPC=GCU_NUM_GRID,
298335
num_warps=num_warps,
299336
num_stages=num_stages,
300337
)
@@ -493,15 +530,15 @@ def apply_per_token_group_quant_fp8_gcu_patch() -> None:
493530
except ImportError:
494531
continue
495532
if hasattr(mod, "per_token_group_quant_fp8"):
496-
# mod.per_token_group_quant_fp8 = per_token_group_quant_fp8_gcu
497-
mod.per_token_group_quant_fp8 = per_token_group_quant_fp8_torch
533+
mod.per_token_group_quant_fp8 = per_token_group_quant_fp8_gcu
498534

499535
_patched = True
500536
logger.info(
501537
"Patched per_token_group_quant_fp8 for GCU "
502-
"(grid.x <= %d, grid.y <= %d) in %d modules",
503-
GCU_MAX_GRID_X,
504-
GCU_MAX_GRID_YZ,
538+
"(grid <= %d, 1-D only, NUM_SPC=%d, DSM <= %d KB) in %d modules",
539+
GCU_NUM_GRID,
540+
GCU_NUM_GRID,
541+
GCU_MAX_DSM_MEMORY // 1024,
505542
len(_IMPORTERS),
506543
)
507544
except Exception as exc:

0 commit comments

Comments
 (0)