Skip to content

Commit 2188137

Browse files
authored
apply flagos te_groups_gemm op (#55)
- add ```te_general_grouped_gemm``` op for flagos backend, base on flag_gems - support both forward and backward computation, distinguished by ```grad```
1 parent 1f98511 commit 2188137

4 files changed

Lines changed: 323 additions & 0 deletions

File tree

transformer_engine/plugin/core/backends/flagos/flagos.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
generic_gemm_fl,
2020
scaled_masked_softmax_forward_fl,
2121
scaled_masked_softmax_backward_fl,
22+
te_general_grouped_gemm_fl,
2223
)
2324

2425

@@ -118,6 +119,46 @@ def generic_gemm(
118119
beta,
119120
)
120121

122+
def te_general_grouped_gemm(
123+
self,
124+
A: List[Any],
125+
transa: bool,
126+
B: List[Any],
127+
transb: bool,
128+
D: Optional[List[torch.Tensor]],
129+
D_type: DType,
130+
m_splits: List[int],
131+
bias: List[torch.Tensor],
132+
bias_type: DType,
133+
single_output: bool,
134+
pre_gelu_out: List[torch.Tensor],
135+
grad: bool,
136+
workspace: List[torch.Tensor],
137+
workspaceSizes: int,
138+
accumulate: bool,
139+
use_split_accumulator: bool,
140+
math_sm_count: int,
141+
) -> Optional[List[torch.Tensor]]:
142+
return te_general_grouped_gemm_fl(
143+
A,
144+
transa,
145+
B,
146+
transb,
147+
D,
148+
D_type,
149+
m_splits,
150+
bias,
151+
bias_type,
152+
single_output,
153+
pre_gelu_out,
154+
grad,
155+
workspace,
156+
workspaceSizes,
157+
accumulate,
158+
use_split_accumulator,
159+
math_sm_count,
160+
)
161+
121162
# Other granular functions
122163
def rmsnorm_fwd(
123164
self,

transformer_engine/plugin/core/backends/flagos/impl/gemm.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
__all__ = [
1212
"generic_gemm_fl",
13+
"te_general_grouped_gemm_fl",
1314
]
1415

1516
_DTYPE_TO_TORCH = {
@@ -115,3 +116,107 @@ def generic_gemm_fl(
115116
return D, bias_grad, gelu_input, extra_output_ret
116117
else:
117118
return out1, bias_grad, gelu_input, extra_output_ret
119+
120+
121+
# This function can represent both forward and backward computations.
122+
# When grad is False (forward computation), the 'bias' is bias;
123+
# When grad is True (backward computation/gradient calculation), the 'bias' is grad_bias;
124+
def te_general_grouped_gemm_fl(
125+
B: List[torch.Tensor],
126+
transb: bool,
127+
A: List[torch.Tensor],
128+
transa: bool,
129+
D: Optional[List[torch.Tensor]],
130+
D_type: Any,
131+
m_splits: List[int],
132+
bias: List[torch.Tensor], # bias or grad_bias
133+
bias_type: Any,
134+
single_output: bool,
135+
pre_gelu_out: List[torch.Tensor],
136+
grad: bool,
137+
workspace: List[torch.Tensor],
138+
workspaceSize: int,
139+
accumulate: bool,
140+
use_split_accumulator: bool,
141+
math_sm_count: int,
142+
) -> Optional[List[torch.Tensor]]:
143+
if single_output and D is None:
144+
raise ValueError("not implemented, D should be allocated for single output case.")
145+
146+
num_gemms = len(A)
147+
if D is None:
148+
D = []
149+
for i in range(num_gemms):
150+
m = A[i].shape[1] if transa else A[i].shape[0]
151+
n = B[i].shape[0] if transb else B[i].shape[1]
152+
D.append(torch.empty((m, n), dtype=D[i].dtype, device=A[0].device))
153+
154+
temp_D = []
155+
for i in range(num_gemms):
156+
# Handle the special case of zero-element inputs
157+
if A[i].numel() == 0 or B[i].numel() == 0:
158+
if not single_output:
159+
if D[i].numel() != 0 and not accumulate:
160+
flag_gems.copy_(D[i], flag_gems.zeros(D[i].shape))
161+
else:
162+
out = flag_gems.zeros((A[i].shape[0], B[i].shape[1]))
163+
if grad and len(bias) > i and bias[i] is not None and bias[i].numel() != 0:
164+
flag_gems.copy_(bias[i], flag_gems.zeros(bias[i].shape))
165+
if (
166+
len(pre_gelu_out) > i
167+
and pre_gelu_out[i] is not None
168+
and pre_gelu_out[i].numel() != 0
169+
):
170+
flag_gems.copy_(pre_gelu_out[i], flag_gems.zeros(pre_gelu_out[i].shape))
171+
continue
172+
173+
a = A[i].t() if transa else A[i]
174+
b = B[i].t() if transb else B[i]
175+
# Determine presence of epilogue tensors
176+
has_bias = len(bias) > i and bias[i] is not None and bias[i].numel() > 0
177+
has_pre_gelu = (
178+
len(pre_gelu_out) > i and pre_gelu_out[i] is not None and pre_gelu_out[i].numel() > 0
179+
)
180+
181+
# Forward Pass calculation
182+
if not grad:
183+
if has_bias:
184+
# Fused matrix multiplication and bias addition
185+
out = flag_gems.addmm(bias[i], a, b)
186+
else:
187+
out = flag_gems.mm(a, b)
188+
189+
# Apply GELU epilogue if pre_gelu_out is provided
190+
if has_pre_gelu:
191+
flag_gems.copy_(pre_gelu_out[i], out)
192+
out = flag_gems.gelu(out)
193+
else:
194+
out = flag_gems.mm(a, b)
195+
196+
# Apply dGELU epilogue if requested
197+
if has_pre_gelu:
198+
out = flag_gems.gelu_backward(out, pre_gelu_out[i])
199+
200+
# Compute bias gradients if requested
201+
if has_bias:
202+
bias_grad = flag_gems.sum_dim(out, dim=[0])
203+
if accumulate:
204+
flag_gems.add_(bias[i], bias_grad)
205+
else:
206+
flag_gems.copy_(bias[i], bias_grad)
207+
208+
if not single_output:
209+
# Store output
210+
if accumulate:
211+
flag_gems.add_(D[i], out.to(D[i].dtype))
212+
else:
213+
flag_gems.copy_(D[i], out.to(D[i].dtype))
214+
else:
215+
temp_D.append(out.to(D[0].dtype))
216+
217+
if single_output:
218+
if temp_D:
219+
temp = flag_gems.cat(temp_D, dim=0)
220+
flag_gems.copy_(D[0], temp)
221+
222+
return bias

transformer_engine/plugin/core/backends/flagos/register_ops.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ def register_builtins(registry) -> None:
6666
vendor=None,
6767
priority=150,
6868
),
69+
OpImpl(
70+
op_name="te_general_grouped_gemm",
71+
impl_id="default.flagos",
72+
kind=BackendImplKind.DEFAULT,
73+
fn=_bind_is_available(backend.te_general_grouped_gemm, is_avail),
74+
vendor=None,
75+
priority=150,
76+
),
6977
OpImpl(
7078
op_name="multi_tensor_scale",
7179
impl_id="default.flagos",
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import torch
2+
3+
from transformer_engine.plugin.test_utils import (
4+
get_available_backends,
5+
get_backend,
6+
TestCase,
7+
generate_random_tensor,
8+
)
9+
10+
11+
class grouped_gemmTests(TestCase):
12+
def __init__(self, device="cpu"):
13+
super().__init__(
14+
"Moe permute Operations",
15+
"Test correctness of all moe permute operations across backends",
16+
)
17+
self.backends = get_available_backends()
18+
self.device = device
19+
20+
def test_grouped_gemm_equivalence(self, grad, has_bias, has_pre_gelu, single_output):
21+
print(
22+
"\n test te_general_grouped_gemm"
23+
f" grad:{grad} has_bias:{has_bias},has_pre_gelu:{has_pre_gelu},single_output:{single_output}"
24+
)
25+
import transformer_engine_torch_nv as tex
26+
27+
num_gemms = 2
28+
m, k, n = 128, 32, 64
29+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
30+
dtype = torch.float16
31+
32+
if dtype == torch.float16:
33+
te_dtype = tex.DType.kFloat16
34+
elif dtype == torch.float32:
35+
te_dtype = tex.DType.kFloat32
36+
elif dtype == torch.bfloat16:
37+
te_dtype = tex.DType.kBFloat16
38+
else:
39+
raise ValueError(f"不支持的 dtype: {torch_dtype}")
40+
41+
torch.manual_seed(42)
42+
43+
A_list = [torch.randn((k, n), device=device, dtype=dtype) for _ in range(num_gemms)]
44+
B_list = [torch.randn((m, k), device=device, dtype=dtype) for _ in range(num_gemms)]
45+
46+
bias_list_py_bias = [
47+
(
48+
torch.randn(n, device=device, dtype=dtype)
49+
if has_bias
50+
else torch.empty(0, device=device, dtype=dtype)
51+
)
52+
for _ in range(num_gemms)
53+
]
54+
bias_list_te = [b.clone() for b in bias_list_py_bias]
55+
56+
pre_gelu_list_py = [
57+
(
58+
torch.randn(m, n, device=device, dtype=dtype)
59+
if has_pre_gelu
60+
else torch.empty(0, device=device, dtype=dtype)
61+
)
62+
for _ in range(num_gemms)
63+
]
64+
pre_gelu_list_te = [p.clone() for p in pre_gelu_list_py]
65+
66+
if single_output:
67+
D_list_py = [torch.empty(m * num_gemms, n, device=device, dtype=dtype)]
68+
D_list_te = [torch.empty(m * num_gemms, n, device=device, dtype=dtype)]
69+
else:
70+
D_list_py = [torch.empty(m, n, device=device, dtype=dtype) for _ in range(num_gemms)]
71+
D_list_te = [torch.empty(m, n, device=device, dtype=dtype) for _ in range(num_gemms)]
72+
workspace_py = [torch.empty(1024 * 1024, device=device, dtype=torch.uint8)]
73+
workspace_te = [torch.empty(1024 * 1024, device=device, dtype=torch.uint8)]
74+
75+
tex.te_general_grouped_gemm(
76+
A_list,
77+
False,
78+
B_list,
79+
False,
80+
D_list_te,
81+
te_dtype,
82+
[],
83+
bias_list_te,
84+
te_dtype,
85+
single_output,
86+
pre_gelu_list_te,
87+
grad,
88+
workspace_te,
89+
1024 * 1024,
90+
False,
91+
False,
92+
0,
93+
)
94+
95+
for backend_name in self.backends:
96+
backend = get_backend(backend_name)
97+
print("backend:", backend)
98+
try:
99+
bias_list_py = [b.clone() for b in bias_list_py_bias]
100+
backend.te_general_grouped_gemm(
101+
A_list,
102+
False,
103+
B_list,
104+
False,
105+
D_list_py,
106+
te_dtype,
107+
[],
108+
bias_list_py,
109+
te_dtype,
110+
single_output,
111+
pre_gelu_list_py,
112+
grad,
113+
workspace_py,
114+
1024 * 1024,
115+
False,
116+
False,
117+
0,
118+
)
119+
120+
for py_d, te_d in zip(D_list_py, D_list_te):
121+
self.assert_close(
122+
py_d, te_d, rtol=1e-3, atol=1e-3, msg="Output D tensors mismatch!"
123+
)
124+
125+
if not grad and has_pre_gelu:
126+
for py_p, te_p in zip(pre_gelu_list_py, pre_gelu_list_te):
127+
self.assert_close(
128+
py_p, te_p, rtol=1e-3, atol=1e-3, msg="Pre-GELU out tensors mismatch!"
129+
)
130+
131+
if grad or has_bias:
132+
for py_b, te_b in zip(bias_list_py, bias_list_te):
133+
self.assert_close(
134+
py_b, te_b, rtol=1e-3, atol=1e-3, msg="Bias gradient tensors mismatch!"
135+
)
136+
print(f" ✓ {backend_name}")
137+
except NotImplementedError:
138+
self.skipped += 1
139+
print(f" ⊘ {backend_name} (not implemented)")
140+
except Exception as e:
141+
self.failed += 1
142+
print(f" ✗ Test failed: {e}")
143+
144+
def run_all_tests(self):
145+
print("\n" + "=" * 60)
146+
print("=" * 60)
147+
print(f"Available backends: {', '.join(self.backends)}")
148+
149+
# gemm tests
150+
self.test_grouped_gemm_equivalence(False, False, False, False)
151+
self.test_grouped_gemm_equivalence(False, True, False, False)
152+
self.test_grouped_gemm_equivalence(False, False, True, False)
153+
154+
self.test_grouped_gemm_equivalence(False, False, False, True)
155+
self.test_grouped_gemm_equivalence(False, True, False, True)
156+
self.test_grouped_gemm_equivalence(False, False, True, True)
157+
return self.report()
158+
159+
160+
def main():
161+
device = "cuda" if torch.cuda.is_available() else "cpu"
162+
print(f"Using device: {device}")
163+
test_suite = grouped_gemmTests(device=device)
164+
success = test_suite.run_all_tests()
165+
return 0 if success else 1
166+
167+
168+
if __name__ == "__main__":
169+
exit(main())

0 commit comments

Comments
 (0)