Skip to content

Commit 9e5ca92

Browse files
Improve fused RoPE backend tests
1 parent 5ecc0e5 commit 9e5ca92

1 file changed

Lines changed: 219 additions & 22 deletions

File tree

transformer_engine/plugin/tests/test_fused_rope.py

Lines changed: 219 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -252,30 +252,155 @@ def _reference_qkv_backward(
252252
return out
253253

254254

255+
class _TorchRoPEBackend:
256+
@staticmethod
257+
def fused_rope_forward(
258+
input,
259+
freqs,
260+
start_positions,
261+
qkv_format,
262+
interleaved,
263+
cu_seqlens,
264+
cp_size,
265+
cp_rank,
266+
):
267+
return _reference_rope(
268+
input,
269+
freqs,
270+
qkv_format,
271+
interleaved,
272+
cu_seqlens,
273+
start_positions,
274+
cp_size,
275+
cp_rank,
276+
False,
277+
)
278+
279+
@staticmethod
280+
def fused_rope_backward(
281+
output_grads,
282+
freqs,
283+
start_positions,
284+
qkv_format,
285+
interleaved,
286+
cu_seqlens,
287+
cp_size,
288+
cp_rank,
289+
):
290+
return _reference_rope(
291+
output_grads,
292+
freqs,
293+
qkv_format,
294+
interleaved,
295+
cu_seqlens,
296+
start_positions,
297+
cp_size,
298+
cp_rank,
299+
True,
300+
)
301+
302+
@staticmethod
303+
def fused_qkv_rope_forward(
304+
qkv_input,
305+
q_freqs,
306+
k_freqs,
307+
start_positions,
308+
qkv_split_arg_list,
309+
qkv_format,
310+
interleaved,
311+
cp_size,
312+
cp_rank,
313+
):
314+
return _reference_qkv_forward(
315+
qkv_input,
316+
q_freqs,
317+
k_freqs,
318+
start_positions,
319+
qkv_split_arg_list,
320+
qkv_format,
321+
interleaved,
322+
cp_size,
323+
cp_rank,
324+
)
325+
326+
@staticmethod
327+
def fused_qkv_rope_backward(
328+
q_grad_out,
329+
k_grad_out,
330+
v_grad_out,
331+
q_freqs,
332+
k_freqs,
333+
qkv_split_arg_list,
334+
qkv_format,
335+
interleaved,
336+
cp_size,
337+
cp_rank,
338+
):
339+
return _reference_qkv_backward(
340+
q_grad_out,
341+
k_grad_out,
342+
v_grad_out,
343+
q_freqs,
344+
k_freqs,
345+
qkv_split_arg_list,
346+
qkv_format,
347+
interleaved,
348+
cp_size,
349+
cp_rank,
350+
)
351+
352+
255353
class FusedRoPETests(TestCase):
256354
def __init__(self, device="cpu"):
257355
super().__init__(
258356
"Fused RoPE",
259-
"Test FlagOS Triton fused RoPE and fused QKV RoPE implementations",
357+
"Test fused RoPE and fused QKV RoPE across CUDA, FlagOS, and torch reference",
260358
)
261-
self.backends = [backend for backend in get_available_backends() if backend == "flagos"]
359+
self.backends = get_available_backends()
360+
if "torch" not in self.backends:
361+
self.backends.append("torch")
362+
self.backends = [
363+
backend for backend in self.backends if backend in ("cuda", "flagos", "torch")
364+
]
262365
self.device = device
263366

264-
def _iter_runnable_backends(self):
367+
def _get_backend(self, backend_name):
368+
if backend_name == "torch":
369+
return _TorchRoPEBackend()
370+
if self.device == "cpu":
371+
raise NotImplementedError("fused RoPE requires a GPU device")
372+
if backend_name == "flagos" and not _triton_available():
373+
raise NotImplementedError("Triton is not installed")
374+
return get_backend(backend_name)
375+
376+
def _iter_backends(self):
265377
if not self.backends:
266378
self.skipped += 1
267-
print(" skip: flagos backend is not registered")
379+
print(" ⊘ no tested backend is registered")
268380
return
269381
for backend_name in self.backends:
270-
if self.device == "cpu":
271-
self.skipped += 1
272-
print(f" skip {backend_name}: fused RoPE requires a GPU device")
273-
continue
274-
if not _triton_available():
382+
try:
383+
yield backend_name, self._get_backend(backend_name)
384+
except NotImplementedError as exc:
275385
self.skipped += 1
276-
print(f" skip {backend_name}: Triton is not installed")
277-
continue
278-
yield backend_name, get_backend(backend_name)
386+
print(f" ⊘ {backend_name} ({exc})")
387+
388+
def _compare_to_cuda(self, outputs, backend_name, labels, msg):
389+
if "cuda" not in outputs or backend_name not in outputs:
390+
return
391+
392+
try:
393+
for actual, expected, label in zip(outputs[backend_name], outputs["cuda"], labels):
394+
self.assert_close(
395+
actual.float(),
396+
expected.float(),
397+
rtol=1e-4,
398+
atol=1e-4,
399+
msg=f"{msg} {label} mismatch between {backend_name} and cuda",
400+
)
401+
print(f" ✓ {backend_name} matches cuda")
402+
except AssertionError as exc:
403+
print(f" ✗ {backend_name} vs cuda: {exc}")
279404

280405
def test_rope_sbhd_bshd_forward_backward(self):
281406
print("\n Testing fused_rope_forward/backward for SBHD and BSHD")
@@ -286,6 +411,11 @@ def test_rope_sbhd_bshd_forward_backward(self):
286411
]
287412

288413
for qkv_format, shape, interleaved, cp_size, cp_rank, use_start in cases:
414+
print(
415+
f"\n Testing fused_rope_forward/backward with {qkv_format.name}, "
416+
f"interleaved={interleaved}, cp_size={cp_size}, "
417+
f"start_positions={use_start}"
418+
)
289419
d2 = 6
290420
freq_len = shape[0] if qkv_format == NVTE_QKV_Format.NVTE_SBHD else shape[1]
291421
freq_len = max(freq_len * cp_size + 3, 12)
@@ -313,7 +443,8 @@ def test_rope_sbhd_bshd_forward_backward(self):
313443
grad, freqs, qkv_format, interleaved, None, start_positions, cp_size, cp_rank, True
314444
)
315445

316-
for backend_name, backend in self._iter_runnable_backends():
446+
outputs = {}
447+
for backend_name, backend in self._iter_backends():
317448
try:
318449
out = backend.fused_rope_forward(
319450
tensor,
@@ -349,10 +480,28 @@ def test_rope_sbhd_bshd_forward_backward(self):
349480
atol=1e-4,
350481
msg=f"fused_rope_backward mismatch for {backend_name}",
351482
)
352-
print(f" ok {backend_name}: {qkv_format.name}, interleaved={interleaved}")
483+
outputs[backend_name] = (out, dx)
484+
print(f" ✓ {backend_name}")
485+
except NotImplementedError as exc:
486+
self.skipped += 1
487+
print(f" ⊘ {backend_name} ({exc})")
488+
except RuntimeError as exc:
489+
if "is not available" in str(exc):
490+
self.skipped += 1
491+
print(f" ⊘ {backend_name} ({exc})")
492+
else:
493+
self.failed += 1
494+
print(f" ✗ {backend_name}: {exc}")
353495
except Exception as exc:
354496
self.failed += 1
355-
print(f" fail {backend_name}: {exc}")
497+
print(f" ✗ {backend_name}: {exc}")
498+
499+
self._compare_to_cuda(
500+
outputs,
501+
"flagos",
502+
("forward", "backward"),
503+
f"{qkv_format.name} fused_rope",
504+
)
356505

357506
def test_rope_thd_forward_backward(self):
358507
print("\n Testing fused_rope_forward/backward for THD")
@@ -362,6 +511,11 @@ def test_rope_thd_forward_backward(self):
362511
]
363512

364513
for cu_cpu, interleaved, cp_size, cp_rank, use_start in cases:
514+
print(
515+
"\n Testing fused_rope_forward/backward with NVTE_THD, "
516+
f"interleaved={interleaved}, cp_size={cp_size}, "
517+
f"start_positions={use_start}"
518+
)
365519
cu_seqlens = cu_cpu.to(self.device)
366520
local_cu = cu_cpu // cp_size
367521
total_t = int(local_cu[-1].item())
@@ -397,7 +551,8 @@ def test_rope_thd_forward_backward(self):
397551
True,
398552
)
399553

400-
for backend_name, backend in self._iter_runnable_backends():
554+
outputs = {}
555+
for backend_name, backend in self._iter_backends():
401556
try:
402557
out = backend.fused_rope_forward(
403558
tensor,
@@ -433,10 +588,28 @@ def test_rope_thd_forward_backward(self):
433588
atol=1e-4,
434589
msg=f"THD fused_rope_backward mismatch for {backend_name}",
435590
)
436-
print(f" ok {backend_name}: THD, interleaved={interleaved}")
591+
outputs[backend_name] = (out, dx)
592+
print(f" ✓ {backend_name}")
593+
except NotImplementedError as exc:
594+
self.skipped += 1
595+
print(f" ⊘ {backend_name} ({exc})")
596+
except RuntimeError as exc:
597+
if "is not available" in str(exc):
598+
self.skipped += 1
599+
print(f" ⊘ {backend_name} ({exc})")
600+
else:
601+
self.failed += 1
602+
print(f" ✗ {backend_name}: {exc}")
437603
except Exception as exc:
438604
self.failed += 1
439-
print(f" fail {backend_name}: {exc}")
605+
print(f" ✗ {backend_name}: {exc}")
606+
607+
self._compare_to_cuda(
608+
outputs,
609+
"flagos",
610+
("forward", "backward"),
611+
"THD fused_rope",
612+
)
440613

441614
def test_qkv_rope_forward_backward(self):
442615
print("\n Testing fused_qkv_rope_forward/backward")
@@ -447,6 +620,11 @@ def test_qkv_rope_forward_backward(self):
447620
qkv_split_arg_list = [16, 8, 8]
448621

449622
for qkv_format, shape, interleaved, cp_size, cp_rank, use_start in cases:
623+
print(
624+
f"\n Testing fused_qkv_rope_forward/backward with {qkv_format.name}, "
625+
f"interleaved={interleaved}, cp_size={cp_size}, "
626+
f"start_positions={use_start}"
627+
)
450628
d2 = 6
451629
seq_len = shape[0] if qkv_format == NVTE_QKV_Format.NVTE_SBHD else shape[1]
452630
freq_len = max(seq_len * cp_size + 3, 12)
@@ -486,7 +664,8 @@ def test_qkv_rope_forward_backward(self):
486664
cp_rank,
487665
)
488666

489-
for backend_name, backend in self._iter_runnable_backends():
667+
outputs = {}
668+
for backend_name, backend in self._iter_backends():
490669
try:
491670
q_out, k_out, v_out = backend.fused_qkv_rope_forward(
492671
qkv,
@@ -539,16 +718,34 @@ def test_qkv_rope_forward_backward(self):
539718
atol=1e-4,
540719
msg=f"fused_qkv_rope_backward mismatch for {backend_name}",
541720
)
542-
print(f" ok {backend_name}: {qkv_format.name}, interleaved={interleaved}")
721+
outputs[backend_name] = (q_out, k_out, v_out, dqkv)
722+
print(f" ✓ {backend_name}")
723+
except NotImplementedError as exc:
724+
self.skipped += 1
725+
print(f" ⊘ {backend_name} ({exc})")
726+
except RuntimeError as exc:
727+
if "is not available" in str(exc):
728+
self.skipped += 1
729+
print(f" ⊘ {backend_name} ({exc})")
730+
else:
731+
self.failed += 1
732+
print(f" ✗ {backend_name}: {exc}")
543733
except Exception as exc:
544734
self.failed += 1
545-
print(f" fail {backend_name}: {exc}")
735+
print(f" ✗ {backend_name}: {exc}")
736+
737+
self._compare_to_cuda(
738+
outputs,
739+
"flagos",
740+
("Q forward", "K forward", "V forward", "backward"),
741+
f"{qkv_format.name} fused_qkv_rope",
742+
)
546743

547744
def run_all_tests(self):
548745
print("\n" + "=" * 60)
549746
print("Testing Fused RoPE")
550747
print("=" * 60)
551-
print(f"Available tested backends: {', '.join(self.backends) or 'none'}")
748+
print(f"Available backends: {', '.join(self.backends) or 'none'}")
552749

553750
self.test_rope_sbhd_bshd_forward_backward()
554751
self.test_rope_thd_forward_backward()

0 commit comments

Comments
 (0)