|
4 | 4 |
|
5 | 5 | import operator |
6 | 6 | from functools import cache |
| 7 | +from pathlib import Path |
7 | 8 |
|
8 | 9 | import torch |
| 10 | +import typer |
9 | 11 |
|
10 | 12 | import cutlass |
11 | 13 | import cutlass.cute as cute |
|
15 | 17 |
|
16 | 18 | from transformer_nuggets.cute.base import CuteOp |
17 | 19 | from transformer_nuggets.cute.cache import compile_tvm_ffi_and_cache |
| 20 | +from transformer_nuggets.cute.profiler import group_by_unit, profile_session |
18 | 21 | from transformer_nuggets.cute.profiler.ops import profile_region |
19 | 22 | from transformer_nuggets.cute.utils import fake_stream, make_fake_compact_tensor |
20 | 23 |
|
@@ -582,3 +585,86 @@ def mxfp8_tma_gemv( |
582 | 585 | output, |
583 | 586 | profile_buffer, |
584 | 587 | ) |
| 588 | + |
| 589 | + |
| 590 | +def quantize_mxfp8_tensor(value: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: |
| 591 | + """Create E4M3 values and raw E8M0 block scales for profiling inputs.""" |
| 592 | + blocks = value.float().reshape(value.shape[0], -1, 32) |
| 593 | + max_abs = blocks.abs().amax(dim=-1).clamp_min(torch.finfo(torch.float32).tiny) |
| 594 | + exponent = torch.ceil(torch.log2(max_abs / 448.0)).clamp(-126, 127) |
| 595 | + scale = torch.exp2(exponent).unsqueeze(-1) |
| 596 | + quantized = (blocks / scale).clamp(-448, 448).to(torch.float8_e4m3fn) |
| 597 | + return quantized.reshape_as(value), (exponent + 127).to(torch.uint8) |
| 598 | + |
| 599 | + |
| 600 | +app = typer.Typer(help="Run the MXFP8 TMA GEMV with labeled intra-kernel profiling.") |
| 601 | + |
| 602 | + |
| 603 | +@app.command() |
| 604 | +def profile_mxfp8_tma( |
| 605 | + n: int = 4096, |
| 606 | + k: int = 8192, |
| 607 | + block_n: int = 4, |
| 608 | + num_stages: int = 2, |
| 609 | + output: Path = Path("mxfp8_tma.pftrace"), |
| 610 | + seed: int = 0, |
| 611 | + warmups: int = 1, |
| 612 | + device: str = "cuda", |
| 613 | +) -> None: |
| 614 | + """Generate a Perfetto trace for one warm MXFP8 TMA GEMV launch.""" |
| 615 | + torch_device = torch.device(device) |
| 616 | + if torch_device.type != "cuda" or not torch.cuda.is_available(): |
| 617 | + raise typer.BadParameter("device must name an available CUDA device") |
| 618 | + if warmups < 0: |
| 619 | + raise typer.BadParameter("warmups must be non-negative") |
| 620 | + |
| 621 | + torch.manual_seed(seed) |
| 622 | + q_input, input_scale = quantize_mxfp8_tensor( |
| 623 | + torch.randn((1, k), dtype=torch.bfloat16, device=torch_device) |
| 624 | + ) |
| 625 | + weight, weight_scale = quantize_mxfp8_tensor( |
| 626 | + torch.randn((n, k), dtype=torch.bfloat16, device=torch_device) |
| 627 | + ) |
| 628 | + op = get_mxfp8_tma_gemv( |
| 629 | + n, |
| 630 | + k, |
| 631 | + block_n, |
| 632 | + num_stages, |
| 633 | + enable_profiling=True, |
| 634 | + ) |
| 635 | + output.parent.mkdir(parents=True, exist_ok=True) |
| 636 | + |
| 637 | + with profile_session( |
| 638 | + max_events_per_unit=op.max_profile_events_per_cta, |
| 639 | + num_units=(op.num_profile_units, "CTA"), |
| 640 | + tag_names=list(op.profile_tags), |
| 641 | + trace_path=str(output), |
| 642 | + device=torch_device, |
| 643 | + post_process_events=group_by_unit, |
| 644 | + ) as (prof, _): |
| 645 | + result = torch.empty((1, n), dtype=torch.bfloat16, device=torch_device) |
| 646 | + for _ in range(warmups): |
| 647 | + op.interface( |
| 648 | + q_input, |
| 649 | + weight, |
| 650 | + input_scale, |
| 651 | + weight_scale, |
| 652 | + output=result, |
| 653 | + profile_buffer=prof.tensor, |
| 654 | + ) |
| 655 | + torch.cuda.synchronize(torch_device) |
| 656 | + prof.tensor.zero_() |
| 657 | + op.interface( |
| 658 | + q_input, |
| 659 | + weight, |
| 660 | + input_scale, |
| 661 | + weight_scale, |
| 662 | + output=result, |
| 663 | + profile_buffer=prof.tensor, |
| 664 | + ) |
| 665 | + |
| 666 | + typer.echo(f"Wrote {output.resolve()}") |
| 667 | + |
| 668 | + |
| 669 | +if __name__ == "__main__": |
| 670 | + app() |
0 commit comments