Skip to content

Commit 9dded24

Browse files
EF-TYcyber-pioneerphysics31415926Copilotliuyancong-enflame-tech
authored
Support GCU(Enflame) (#208)
### PR Category Vendor ### PR Type New Features ### Description support enflame backend ### Changes add enflame backend ### Testing Test on qwen3.6-27b & qwen3.6-35b-a3b **TODO:** fix precision problem in qwen3.6-27b. --------- Signed-off-by: liuyancong-enflame-tech <sp.yancong.liu@enflame-tech.com> Co-authored-by: cyber-pioneer <116002591+cyber-pioneer@users.noreply.github.com> Co-authored-by: Physics <3150105638@zju.edu.cn> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: liuyancong-enflame-tech <sp.yancong.liu@enflame-tech.com>
1 parent 9b9d21d commit 9dded24

17 files changed

Lines changed: 1300 additions & 4 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ In theory, vllm-plugin-FL can support all models available in vLLM, as long as n
3232
| Moore Threads | Supported | - |
3333
| Hygon | Supported | - |
3434
| Sunrise | Supported | - |
35+
| Enflame | Supported | - |
3536

3637
## Quick Start
3738

vllm_fl/compilation/graph.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class Graph:
4949
graph = torch.musa.MUSAGraph
5050
elif current_platform.device_type == "ptpu":
5151
graph = torch.ptpu.PTPUGraph
52+
elif current_platform.device_type == "gcu":
53+
graph = torch.gcu.GCUGraph
5254
elif current_platform.device_type == "txda":
5355
graph = None
5456
else:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
"""
4+
GCU (Enflame) backend for vllm-plugin-FL dispatch.
5+
"""
6+
7+
from .gcu import GCUBackend
8+
from .patch import apply_gcu_patches
9+
10+
apply_gcu_patches()
11+
12+
__all__ = ["GCUBackend"]
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
"""
4+
GCU backend implementation.
5+
"""
6+
7+
from __future__ import annotations
8+
from typing import Optional, Union
9+
import sys
10+
import torch
11+
from vllm_fl.dispatch.backends.base import Backend
12+
13+
14+
class GCUBackend(Backend):
15+
"""GCU vendor backend (``torch.gcu`` / torch_gcu runtime)."""
16+
17+
_available: Optional[bool] = None
18+
19+
@property
20+
def name(self) -> str:
21+
return "gcu"
22+
23+
@property
24+
def vendor(self) -> Optional[str]:
25+
return "gcu"
26+
27+
def is_available(self) -> bool:
28+
if GCUBackend._available is None:
29+
gcu = getattr(torch, "gcu", None)
30+
if gcu is not None and gcu.is_available() and gcu.device_count() > 0:
31+
GCUBackend._available = True
32+
else:
33+
GCUBackend._available = False
34+
return GCUBackend._available
35+
36+
def silu_and_mul(self, obj, x: torch.Tensor) -> torch.Tensor:
37+
from .impl.activation import silu_and_mul_gcu
38+
39+
return silu_and_mul_gcu(obj, x)
40+
41+
def rms_norm(
42+
self,
43+
obj,
44+
x: torch.Tensor,
45+
residual: Optional[torch.Tensor] = None,
46+
) -> Union[torch.Tensor, tuple[torch.Tensor, torch.Tensor]]:
47+
from .impl.normalization import rms_norm_gcu
48+
49+
return rms_norm_gcu(obj, x, residual)
50+
51+
def rotary_embedding(
52+
self,
53+
obj,
54+
query: torch.Tensor,
55+
key: torch.Tensor,
56+
cos: torch.Tensor,
57+
sin: torch.Tensor,
58+
position_ids: torch.Tensor,
59+
rotary_interleaved: bool = False,
60+
inplace: bool = True,
61+
) -> tuple[torch.Tensor, torch.Tensor]:
62+
from .impl.rotary import rotary_embedding_gcu
63+
64+
return rotary_embedding_gcu(
65+
obj,
66+
query,
67+
key,
68+
cos,
69+
sin,
70+
position_ids,
71+
rotary_interleaved=rotary_interleaved,
72+
inplace=inplace,
73+
)
74+
75+
def attention_backend(self, use_mla: bool = False, use_sparse: bool = False) -> str:
76+
from vllm.v1.attention.backends.registry import AttentionBackendEnum
77+
78+
if use_mla:
79+
if use_sparse:
80+
raise NotImplementedError("GCU does not support sparse attention yet")
81+
raise NotImplementedError("GCU does not support MLA yet")
82+
83+
import flash_attn.vllm_flash_attn
84+
85+
sys.modules["vllm.vllm_flash_attn"] = flash_attn.vllm_flash_attn
86+
87+
return AttentionBackendEnum.FLASH_ATTN.get_path()

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

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
from __future__ import annotations
4+
5+
import torch
6+
import torch.nn.functional as F
7+
8+
9+
def silu_and_mul_gcu(obj, x: torch.Tensor) -> torch.Tensor:
10+
d = x.shape[-1] // 2
11+
x1, x2 = x[..., :d], x[..., d:]
12+
return F.silu(x1) * x2
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
"""GCU fix for Qwen3-VL bilinear position-embedding Triton kernel.
4+
5+
GCU hardware limits grid.x to 65535. The upstream kernel launches one CTA per
6+
output token with ``grid=(total_out,)``, which fails when ``t * h * w > 65535``
7+
(e.g. 2048 dummy video frames in warmup).
8+
"""
9+
10+
from __future__ import annotations
11+
12+
import logging
13+
14+
import torch
15+
import triton
16+
import triton.language as tl
17+
18+
logger = logging.getLogger(__name__)
19+
20+
GCU_MAX_GRID_X = 65535
21+
_patched = False
22+
23+
24+
@triton.jit
25+
def _bilinear_pos_embed_kernel_gcu(
26+
embed_ptr,
27+
output_ptr,
28+
H,
29+
W,
30+
h_scale,
31+
w_scale,
32+
NUM_GRID: tl.constexpr,
33+
M_SIZE: tl.constexpr,
34+
HIDDEN_DIM: tl.constexpr,
35+
BLOCK_D: tl.constexpr,
36+
TOTAL_OUT,
37+
):
38+
"""Fused bilinear pos-embed interpolation with spatial-merge reorder."""
39+
pid = tl.program_id(0) + tl.program_id(1) * tl.num_programs(0)
40+
if pid >= TOTAL_OUT:
41+
return
42+
43+
total_spatial = H * W
44+
spatial_idx = pid % total_spatial
45+
46+
num_blocks_w = W // M_SIZE
47+
block_idx = spatial_idx // (M_SIZE * M_SIZE)
48+
local_idx = spatial_idx % (M_SIZE * M_SIZE)
49+
br = block_idx // num_blocks_w
50+
bc = block_idx % num_blocks_w
51+
lr = local_idx // M_SIZE
52+
lc = local_idx % M_SIZE
53+
row = br * M_SIZE + lr
54+
col = bc * M_SIZE + lc
55+
56+
h_frac = row.to(tl.float32) * h_scale
57+
w_frac = col.to(tl.float32) * w_scale
58+
59+
hf = tl.math.floor(h_frac).to(tl.int32)
60+
wf = tl.math.floor(w_frac).to(tl.int32)
61+
hc = tl.minimum(hf + 1, NUM_GRID - 1)
62+
wc = tl.minimum(wf + 1, NUM_GRID - 1)
63+
64+
dh = h_frac - hf.to(tl.float32)
65+
dw = w_frac - wf.to(tl.float32)
66+
w11 = dh * dw
67+
w10 = dh - w11
68+
w01 = dw - w11
69+
w00 = 1.0 - dh - w01
70+
71+
off00 = (hf * NUM_GRID + wf) * HIDDEN_DIM
72+
off01 = (hf * NUM_GRID + wc) * HIDDEN_DIM
73+
off10 = (hc * NUM_GRID + wf) * HIDDEN_DIM
74+
off11 = (hc * NUM_GRID + wc) * HIDDEN_DIM
75+
out_off = pid * HIDDEN_DIM
76+
77+
out_dtype = output_ptr.dtype.element_ty
78+
w00_c = w00.to(out_dtype)
79+
w01_c = w01.to(out_dtype)
80+
w10_c = w10.to(out_dtype)
81+
w11_c = w11.to(out_dtype)
82+
83+
for d in tl.range(0, HIDDEN_DIM, BLOCK_D):
84+
cols = d + tl.arange(0, BLOCK_D)
85+
mask = cols < HIDDEN_DIM
86+
87+
e00 = tl.load(embed_ptr + off00 + cols, mask=mask)
88+
e01 = tl.load(embed_ptr + off01 + cols, mask=mask)
89+
e10 = tl.load(embed_ptr + off10 + cols, mask=mask)
90+
e11 = tl.load(embed_ptr + off11 + cols, mask=mask)
91+
92+
val = w00_c * e00 + w01_c * e01 + w10_c * e10 + w11_c * e11
93+
94+
tl.store(output_ptr + out_off + cols, val, mask=mask)
95+
96+
97+
def triton_pos_embed_interpolate_gcu(
98+
embed_weight: torch.Tensor,
99+
t: int,
100+
h: int,
101+
w: int,
102+
num_grid_per_side: int,
103+
m_size: int,
104+
dtype: torch.dtype,
105+
) -> torch.Tensor:
106+
"""GCU-safe launcher: split grid across (x, y) when total_out exceeds 65535."""
107+
assert h % m_size == 0 and w % m_size == 0, (
108+
f"h={h} and w={w} must be divisible by m_size={m_size}"
109+
)
110+
hidden_dim = embed_weight.shape[1]
111+
total_out = t * h * w
112+
output = torch.empty(
113+
total_out,
114+
hidden_dim,
115+
device=embed_weight.device,
116+
dtype=dtype,
117+
)
118+
119+
h_scale = float(num_grid_per_side - 1) / float(h - 1) if h > 1 else 0.0
120+
w_scale = float(num_grid_per_side - 1) / float(w - 1) if w > 1 else 0.0
121+
122+
block_d = triton.next_power_of_2(hidden_dim)
123+
124+
grid_x = min(total_out, GCU_MAX_GRID_X)
125+
grid_y = triton.cdiv(total_out, grid_x)
126+
127+
_bilinear_pos_embed_kernel_gcu[(grid_x, grid_y)](
128+
embed_weight,
129+
output,
130+
h,
131+
w,
132+
h_scale,
133+
w_scale,
134+
num_grid_per_side,
135+
m_size,
136+
hidden_dim,
137+
block_d,
138+
total_out,
139+
)
140+
return output
141+
142+
143+
def apply_bilinear_pos_embed_gcu_patch() -> None:
144+
"""Replace upstream Triton launcher with the GCU grid-safe version."""
145+
global _patched
146+
if _patched:
147+
return
148+
149+
gcu = getattr(torch, "gcu", None)
150+
if gcu is None or not gcu.is_available():
151+
return
152+
153+
try:
154+
import vllm.model_executor.models.qwen3_vl as qwen3_vl
155+
156+
if not getattr(qwen3_vl, "HAS_TRITON", False):
157+
return
158+
159+
qwen3_vl.triton_pos_embed_interpolate = triton_pos_embed_interpolate_gcu
160+
qwen3_vl._bilinear_pos_embed_kernel = _bilinear_pos_embed_kernel_gcu
161+
_patched = True
162+
logger.info(
163+
"Patched Qwen3-VL bilinear pos embed for GCU (grid.x <= %d)",
164+
GCU_MAX_GRID_X,
165+
)
166+
except Exception as exc:
167+
logger.warning("Failed to patch bilinear pos embed for GCU: %s", exc)

0 commit comments

Comments
 (0)