1919 select_nvfp4_tma_compute_warps ,
2020 select_nvfp4_tma_config ,
2121 select_nvfp4_tma_split_k ,
22+ select_nvfp4_tma_stage_weight_scales ,
2223 )
2324 from transformer_nuggets .cute .profiler import profile_session
2425 from transformer_nuggets .cute .profiler .host import decode_events
@@ -54,6 +55,7 @@ def test_select_nvfp4_tma_compute_warps(k, block_n, expected):
5455 (14336 , 16384 , (8 , 3 , 4 )),
5556 (24576 , 24576 , (8 , 2 , 4 )),
5657 (32768 , 32768 , (8 , 3 , 4 )),
58+ (16384 , 8192 , (8 , 2 , 4 )),
5759 ],
5860)
5961def test_select_nvfp4_tma_config (n , k , expected ):
@@ -78,6 +80,38 @@ def test_select_nvfp4_tma_split_k(n, k, expected):
7880 assert select_nvfp4_tma_split_k (n , k ) == expected
7981
8082
83+ @pytest .mark .parametrize (
84+ ("n" , "k" , "block_n" , "num_compute_warps" , "grid_scheduler" , "split_k" , "expected" ),
85+ [
86+ (16384 , 6144 , 8 , 4 , GridScheduler .STATIC , 1 , True ),
87+ (32768 , 8192 , 8 , 4 , GridScheduler .STATIC , 1 , True ),
88+ (14336 , 8192 , 8 , 4 , GridScheduler .STATIC , 1 , False ),
89+ (16384 , 12288 , 8 , 4 , GridScheduler .STATIC , 1 , False ),
90+ (16384 , 8192 , 16 , 4 , GridScheduler .STATIC , 1 , False ),
91+ (16384 , 8192 , 8 , 2 , GridScheduler .STATIC , 1 , False ),
92+ (16384 , 8192 , 8 , 4 , GridScheduler .PERSISTENT , 1 , False ),
93+ (16384 , 8192 , 8 , 4 , GridScheduler .STATIC , 2 , False ),
94+ ],
95+ )
96+ def test_select_nvfp4_tma_stage_weight_scales (
97+ n , k , block_n , num_compute_warps , grid_scheduler , split_k , expected
98+ ):
99+ """Stage physical scale subsets only in the measured B200 regime."""
100+ if torch .cuda .get_device_capability () != (10 , 0 ):
101+ expected = False
102+ assert (
103+ select_nvfp4_tma_stage_weight_scales (
104+ n ,
105+ k ,
106+ block_n ,
107+ num_compute_warps ,
108+ grid_scheduler ,
109+ split_k ,
110+ )
111+ is expected
112+ )
113+
114+
81115def pack_fp4 (codes : torch .Tensor ) -> torch .Tensor :
82116 """Pack low-nibble-first E2M1 codes into the PyTorch FP4 shell dtype."""
83117 packed = codes [:, 0 ::2 ] | (codes [:, 1 ::2 ] << 4 )
@@ -178,6 +212,23 @@ def test_nvfp4_tma_scale_layout_and_paired_loads(block_n, num_compute_warps):
178212 torch .testing .assert_close (actual , expected , atol = 2.0 , rtol = 0.05 )
179213
180214
215+ def test_nvfp4_tma_compact_scale_tma_matches_blocked_layout ():
216+ """Match exact scales across physical 128-row atoms with compact TMA staging."""
217+ q_input , weight , input_scale , weight_scale , expected , _ = make_case (264 , 6144 )
218+ actual = nvfp4_tma_gemv (
219+ q_input ,
220+ weight ,
221+ input_scale ,
222+ weight_scale ,
223+ block_n = 8 ,
224+ num_stages = 3 ,
225+ num_compute_warps = 4 ,
226+ stage_weight_scales = True ,
227+ )
228+ torch .cuda .synchronize ()
229+ torch .testing .assert_close (actual , expected , atol = 0 , rtol = 0 )
230+
231+
181232def test_nvfp4_tma_fp16_partial_reduction_preserves_extreme_values ():
182233 """Keep exact FP16 partial sums before accumulating them in FP32."""
183234 n , k = 128 , 2048
@@ -374,8 +425,11 @@ def test_nvfp4_tma_split_k_accepts_scalar_aligned_workspace():
374425 torch .testing .assert_close (output , expected , atol = 2.0 , rtol = 0.05 )
375426
376427
377- @pytest .mark .parametrize (("k" , "split_k" , "num_stages" ), [(8192 , 2 , 2 ), (16384 , 4 , 3 )])
378- def test_nvfp4_tma_split_k_cuda_graph (k , split_k , num_stages ):
428+ @pytest .mark .parametrize (
429+ ("k" , "split_k" , "num_stages" , "stage_weight_scales" ),
430+ [(8192 , 2 , 2 , True ), (16384 , 4 , 3 , False )],
431+ )
432+ def test_nvfp4_tma_split_k_cuda_graph (k , split_k , num_stages , stage_weight_scales ):
379433 """Reduce parallel K partitions in FP32 before the final BF16 conversion."""
380434 n = 128
381435 q_input , weight , input_scale , weight_scale , expected , _ = make_case (n , k )
@@ -386,6 +440,7 @@ def test_nvfp4_tma_split_k_cuda_graph(k, split_k, num_stages):
386440 "num_compute_warps" : 4 ,
387441 "num_stages" : num_stages ,
388442 "split_k" : split_k ,
443+ "stage_weight_scales" : stage_weight_scales ,
389444 "output" : output ,
390445 "partial_output" : partial_output ,
391446 }
@@ -398,6 +453,24 @@ def test_nvfp4_tma_split_k_cuda_graph(k, split_k, num_stages):
398453 torch .testing .assert_close (output , expected , atol = 2.0 , rtol = 0.05 )
399454
400455
456+ def test_nvfp4_tma_compact_scale_tma_persistent_reuse ():
457+ """Reuse compact scale stages across persistent output tiles."""
458+ q_input , weight , input_scale , weight_scale , expected , _ = make_case (256 , 2048 )
459+ actual = nvfp4_tma_gemv (
460+ q_input ,
461+ weight ,
462+ input_scale ,
463+ weight_scale ,
464+ block_n = 8 ,
465+ num_compute_warps = 4 ,
466+ grid_scheduler = GridScheduler .PERSISTENT ,
467+ num_persistent_ctas = 4 ,
468+ stage_weight_scales = True ,
469+ )
470+ torch .cuda .synchronize ()
471+ torch .testing .assert_close (actual , expected , atol = 2.0 , rtol = 0.05 )
472+
473+
401474def test_nvfp4_tma_persistent_cuda_graph ():
402475 """Replay persistent NVFP4 GEMV into caller-owned output."""
403476 q_input , weight , input_scale , weight_scale , expected , _ = make_case (256 , 2048 )
0 commit comments