|
10 | 10 |
|
11 | 11 | try: |
12 | 12 | from transformer_nuggets.cute import ( |
| 13 | + BlockScaleLayout, |
13 | 14 | GridScheduler, |
14 | 15 | MXFP8_TMA_PROFILE_TAGS, |
15 | 16 | get_mxfp8_tma_gemv, |
16 | 17 | mxfp8_tma_gemv, |
| 18 | + mxfp8_tma_scaled_mm, |
17 | 19 | select_mxfp8_tma_compute_warps, |
18 | 20 | ) |
19 | 21 | from transformer_nuggets.cute.mxfp8_tma import app |
@@ -42,6 +44,27 @@ def test_select_mxfp8_tma_compute_warps(k, block_n, expected_sm100): |
42 | 44 | assert select_mxfp8_tma_compute_warps(k, block_n) == expected |
43 | 45 |
|
44 | 46 |
|
| 47 | +def swizzle_mxfp8_scales(scales: torch.Tensor) -> torch.Tensor: |
| 48 | + """Convert natural E8M0 scales to canonical SWIZZLE_32_4_4 storage.""" |
| 49 | + rows, cols = scales.shape |
| 50 | + padded_rows = ((rows + 127) // 128) * 128 |
| 51 | + padded_cols = ((cols + 3) // 4) * 4 |
| 52 | + padded = torch.zeros( |
| 53 | + (padded_rows, padded_cols), |
| 54 | + dtype=scales.dtype, |
| 55 | + device=scales.device, |
| 56 | + ) |
| 57 | + padded[:rows, :cols] = scales |
| 58 | + return ( |
| 59 | + padded.view(padded_rows // 128, 128, padded_cols // 4, 4) |
| 60 | + .permute(0, 2, 1, 3) |
| 61 | + .reshape(-1, 4, 32, 4) |
| 62 | + .transpose(1, 2) |
| 63 | + .reshape(-1) |
| 64 | + .contiguous() |
| 65 | + ) |
| 66 | + |
| 67 | + |
45 | 68 | def dequantize_mxfp8(value: torch.Tensor, scale: torch.Tensor) -> torch.Tensor: |
46 | 69 | """Dequantize raw MXFP8 storage to float32.""" |
47 | 70 | expanded_scale = scale.view(torch.float8_e8m0fnu).float().repeat_interleave(32, dim=1) |
@@ -81,6 +104,65 @@ def test_mxfp8_tma_gemv_matches_reference(k, block_n, num_stages, num_compute_wa |
81 | 104 | torch.testing.assert_close(actual, expected, atol=1.0, rtol=0.05) |
82 | 105 |
|
83 | 106 |
|
| 107 | +@pytest.mark.parametrize(("block_n", "num_compute_warps"), [(8, 4), (6, 2)]) |
| 108 | +def test_mxfp8_tma_swizzled_scales_match_raw(block_n, num_compute_warps): |
| 109 | + """Read canonical blocked E8M0 scales across row-layout boundaries.""" |
| 110 | + n, k = 264, 2048 |
| 111 | + q_input, input_scale = quantize_mxfp8(torch.randn((1, k), dtype=torch.bfloat16, device="cuda")) |
| 112 | + weight, weight_scale = quantize_mxfp8(torch.randn((n, k), dtype=torch.bfloat16, device="cuda")) |
| 113 | + expected = mxfp8_tma_gemv( |
| 114 | + q_input, |
| 115 | + weight, |
| 116 | + input_scale, |
| 117 | + weight_scale, |
| 118 | + block_n=block_n, |
| 119 | + num_compute_warps=num_compute_warps, |
| 120 | + ) |
| 121 | + actual = mxfp8_tma_gemv( |
| 122 | + q_input, |
| 123 | + weight, |
| 124 | + swizzle_mxfp8_scales(input_scale), |
| 125 | + swizzle_mxfp8_scales(weight_scale), |
| 126 | + block_n=block_n, |
| 127 | + num_compute_warps=num_compute_warps, |
| 128 | + block_scale_layout=BlockScaleLayout.SWIZZLE_32_4_4, |
| 129 | + ) |
| 130 | + torch.cuda.synchronize() |
| 131 | + torch.testing.assert_close(actual, expected, atol=1.0, rtol=0.05) |
| 132 | + |
| 133 | + |
| 134 | +def test_mxfp8_tma_scaled_mm_adapter_matches_scaled_mm(): |
| 135 | + """Match scaled_mm's blocked E8M0 scale and transposed-weight contract.""" |
| 136 | + from torch.nn.functional import ScalingType, SwizzleType, scaled_mm |
| 137 | + |
| 138 | + n, k = 128, 2048 |
| 139 | + q_input, input_scale = quantize_mxfp8(torch.randn((1, k), dtype=torch.bfloat16, device="cuda")) |
| 140 | + weight, weight_scale = quantize_mxfp8(torch.randn((n, k), dtype=torch.bfloat16, device="cuda")) |
| 141 | + input_scale = swizzle_mxfp8_scales(input_scale).view(torch.float8_e8m0fnu) |
| 142 | + weight_scale = swizzle_mxfp8_scales(weight_scale).view(torch.float8_e8m0fnu) |
| 143 | + actual = mxfp8_tma_scaled_mm( |
| 144 | + q_input, |
| 145 | + weight.t(), |
| 146 | + input_scale, |
| 147 | + weight_scale, |
| 148 | + block_n=8, |
| 149 | + num_compute_warps=4, |
| 150 | + ) |
| 151 | + expected = scaled_mm( |
| 152 | + q_input, |
| 153 | + weight.t(), |
| 154 | + scale_a=[input_scale], |
| 155 | + scale_recipe_a=[ScalingType.BlockWise1x32], |
| 156 | + swizzle_a=[SwizzleType.SWIZZLE_32_4_4], |
| 157 | + scale_b=[weight_scale], |
| 158 | + scale_recipe_b=[ScalingType.BlockWise1x32], |
| 159 | + swizzle_b=[SwizzleType.SWIZZLE_32_4_4], |
| 160 | + output_dtype=torch.bfloat16, |
| 161 | + ) |
| 162 | + torch.cuda.synchronize() |
| 163 | + torch.testing.assert_close(actual, expected, atol=1.0, rtol=0.05) |
| 164 | + |
| 165 | + |
84 | 166 | def test_mxfp8_tma_gemv_persistent_grid_matches_reference(): |
85 | 167 | """Reuse a bounded physical CTA grid across all logical output tiles.""" |
86 | 168 | k = 2048 |
|
0 commit comments