@@ -70,6 +70,7 @@ def __init__(
7070 block_n : int ,
7171 num_stages : int ,
7272 enable_profiling : bool = False ,
73+ num_compute_warps : int = 1 ,
7374 ):
7475 super ().__init__ ()
7576 m = 1
@@ -82,6 +83,8 @@ def __init__(
8283 )
8384 if num_stages not in (2 , 3 ):
8485 raise ValueError ("num_stages must be 2 or 3" )
86+ if num_compute_warps not in (1 , 2 , 4 ) or block_n % num_compute_warps != 0 :
87+ raise ValueError ("num_compute_warps must be 1, 2, or 4 and divide block_n" )
8588 if sf_k < num_stages * 32 :
8689 raise ValueError ("TMA staging requires at least one K tile per stage" )
8790 self .m = m
@@ -92,6 +95,8 @@ def __init__(
9295 self .tile_k_u32 = 256
9396 self .num_k_tiles = sf_k // 32
9497 self .enable_profiling = enable_profiling
98+ self .num_compute_warps = num_compute_warps
99+ self .rows_per_warp = block_n // num_compute_warps
95100 self .max_profile_events_per_cta = 2 + 3 * self .num_k_tiles
96101 self .num_profile_units = n // block_n
97102
@@ -166,7 +171,9 @@ def kernel(
166171 producer , consumer = pipeline .PipelineTmaAsync .create (
167172 num_stages = self .num_stages ,
168173 producer_group = pipeline .CooperativeGroup (pipeline .Agent .Thread ),
169- consumer_group = pipeline .CooperativeGroup (pipeline .Agent .Thread , self .m ),
174+ consumer_group = pipeline .CooperativeGroup (
175+ pipeline .Agent .Thread , self .num_compute_warps
176+ ),
170177 tx_count = (self .block_n + self .m ) * self .tile_k_u32 * 4 ,
171178 barrier_storage = barriers ,
172179 tidx = lane ,
@@ -218,7 +225,7 @@ def kernel(
218225 )
219226 stage .commit ()
220227
221- acc = [cutlass .Float32 (0.0 ) for _ in range (self .block_n )]
228+ acc = [cutlass .Float32 (0.0 ) for _ in range (self .rows_per_warp )]
222229 for k_tile in cutlass .range_constexpr (self .num_k_tiles ):
223230 if warp == TMA_PRODUCER_WARP and k_tile < self .num_k_tiles - 1 :
224231 with profile_region (
@@ -269,15 +276,15 @@ def kernel(
269276 cute .copy (
270277 smem_atom ,
271278 cute .make_tensor (
272- sX .iterator + cute .assume (sX .layout ((warp , col_a , full .index )), divby = 4 ),
279+ sX .iterator + cute .assume (sX .layout ((0 , col_a , full .index )), divby = 4 ),
273280 chunk_layout ,
274281 ),
275282 cute .make_tensor (x_frag .iterator , chunk_layout ),
276283 )
277284 cute .copy (
278285 smem_atom ,
279286 cute .make_tensor (
280- sX .iterator + cute .assume (sX .layout ((warp , col_b , full .index )), divby = 4 ),
287+ sX .iterator + cute .assume (sX .layout ((0 , col_b , full .index )), divby = 4 ),
281288 chunk_layout ,
282289 ),
283290 cute .make_tensor (x_frag .iterator + 4 , chunk_layout ),
@@ -291,12 +298,13 @@ def kernel(
291298 input_scale = cute .make_rmem_tensor (1 , cutlass .Uint8 )
292299 cute .copy (
293300 input_scale_atom ,
294- cute .make_tensor (mSFX .iterator + mSFX .layout ((warp , scale_k )), scale_layout ),
301+ cute .make_tensor (mSFX .iterator + mSFX .layout ((0 , scale_k )), scale_layout ),
295302 input_scale ,
296303 )
297304 sx = input_scale [0 ]
298305
299- for row in cutlass .range_constexpr (self .block_n ):
306+ for local_row in cutlass .range_constexpr (self .rows_per_warp ):
307+ row = warp * self .rows_per_warp + local_row
300308 w_frag = cute .make_rmem_tensor ((1 , 8 ), cutlass .Uint32 )
301309 cute .copy (
302310 smem_atom ,
@@ -334,7 +342,7 @@ def kernel(
334342 product = (x_values * w_values ).reduce (
335343 cute .ReductionOp .ADD , cutlass .Float32 (0.0 ), (None , 1 )
336344 )
337- acc [row ] += (product [0 ] + product [1 ]) * combined_e8m0_to_f32 (
345+ acc [local_row ] += (product [0 ] + product [1 ]) * combined_e8m0_to_f32 (
338346 sx , weight_scale [0 ]
339347 )
340348 cute .arch .fence_view_async_shared ()
@@ -352,11 +360,12 @@ def kernel(
352360 ):
353361 if warp == TMA_PRODUCER_WARP :
354362 producer .tail ()
355- for row in cutlass .range_constexpr (self .block_n ):
356- acc [row ] = cute .arch .warp_reduction (acc [row ], operator .add )
363+ for local_row in cutlass .range_constexpr (self .rows_per_warp ):
364+ acc [local_row ] = cute .arch .warp_reduction (acc [local_row ], operator .add )
357365 if lane == 0 :
358- for row in cutlass .range_constexpr (self .block_n ):
359- mO [warp , n0 + row ] = acc [row ].to (cutlass .BFloat16 )
366+ for local_row in cutlass .range_constexpr (self .rows_per_warp ):
367+ row = warp * self .rows_per_warp + local_row
368+ mO [0 , n0 + row ] = acc [local_row ].to (cutlass .BFloat16 )
360369
361370 @cute .jit
362371 def __call__ (
@@ -399,23 +408,24 @@ def __call__(
399408 _name_prefix = name ,
400409 ).launch (
401410 grid = [self .n // self .block_n , 1 , 1 ],
402- block = [self .m * 32 , 1 , 1 ],
411+ block = [self .num_compute_warps * 32 , 1 , 1 ],
403412 stream = stream ,
404413 )
405414
406415 def get_key (self ) -> str :
407416 """Return the static kernel specialization key."""
408417 return (
409418 f"{ self .n } _{ self .sf_k } _{ self .block_n } _{ self .num_stages } "
410- f"_profile={ self .enable_profiling } "
419+ f"_cw= { self . num_compute_warps } _profile={ self .enable_profiling } "
411420 )
412421
413422 def get_name (self ) -> str :
414423 """Return the compiled kernel name."""
415424 profile_suffix = "_profiled" if self .enable_profiling else ""
416425 return (
417426 f"mxfp8_tma_gemv_n{ self .n } _k{ self .sf_k * 32 } "
418- f"_bn{ self .block_n } _s{ self .num_stages } { profile_suffix } "
427+ f"_bn{ self .block_n } _s{ self .num_stages } _cw{ self .num_compute_warps } "
428+ f"{ profile_suffix } "
419429 )
420430
421431 def interface (
@@ -536,9 +546,17 @@ def get_mxfp8_tma_gemv(
536546 block_n : int ,
537547 num_stages : int = 2 ,
538548 enable_profiling : bool = False ,
549+ num_compute_warps : int = 1 ,
539550) -> Mxfp8TmaGemv :
540551 """Return a cached MXFP8 TMA GEMV specialization."""
541- return Mxfp8TmaGemv (n , k , block_n , num_stages , enable_profiling )
552+ return Mxfp8TmaGemv (
553+ n ,
554+ k ,
555+ block_n ,
556+ num_stages ,
557+ enable_profiling ,
558+ num_compute_warps ,
559+ )
542560
543561
544562def mxfp8_tma_gemv (
@@ -552,6 +570,7 @@ def mxfp8_tma_gemv(
552570 output : torch .Tensor | None = None ,
553571 enable_profiling : bool = False ,
554572 profile_buffer : torch .Tensor | None = None ,
573+ num_compute_warps : int = 1 ,
555574) -> torch .Tensor :
556575 """Compute raw-layout MXFP8 GEMV on prequantized inputs.
557576
@@ -565,6 +584,7 @@ def mxfp8_tma_gemv(
565584 output: Optional caller-owned contiguous ``[1, N]`` BF16 output.
566585 enable_profiling: Compile a separate specialization with labeled region timing.
567586 profile_buffer: Buffer from ``profile_session`` for the profiled specialization.
587+ num_compute_warps: Consumer warps sharing each CTA's output-row tile.
568588
569589 Returns:
570590 The provided or newly allocated output tensor.
@@ -577,6 +597,7 @@ def mxfp8_tma_gemv(
577597 block_n ,
578598 num_stages ,
579599 enable_profiling ,
600+ num_compute_warps ,
580601 ).interface (
581602 q_input ,
582603 weight ,
@@ -606,6 +627,7 @@ def profile_mxfp8_tma(
606627 k : int = 8192 ,
607628 block_n : int = 4 ,
608629 num_stages : int = 2 ,
630+ num_compute_warps : int = 1 ,
609631 output : Path = Path ("mxfp8_tma.pftrace" ),
610632 seed : int = 0 ,
611633 warmups : int = 1 ,
@@ -631,6 +653,7 @@ def profile_mxfp8_tma(
631653 block_n ,
632654 num_stages ,
633655 enable_profiling = True ,
656+ num_compute_warps = num_compute_warps ,
634657 )
635658 output .parent .mkdir (parents = True , exist_ok = True )
636659
0 commit comments