2121from flash_attn .cute .mask import AttentionMask
2222from flash_attn .cute .softmax import call_score_mod , call_score_mod_bwd
2323from flash_attn .cute .seqlen_info import SeqlenInfoQK
24+ from flash_attn .cute .block_info import BlockInfo
2425from quack .cute_dsl_utils import ParamsBase
2526from flash_attn .cute .tile_scheduler import SingleTileScheduler , SingleTileVarlenScheduler , TileSchedulerArguments
2627from flash_attn .cute .block_sparsity import BlockSparseTensors
@@ -41,6 +42,7 @@ def __init__(
4142 num_threads : int = 256 ,
4243 pack_gqa : bool = False ,
4344 is_causal : bool = False ,
45+ is_local : bool = False ,
4446 SdP_swapAB : bool = False ,
4547 dKV_swapAB : bool = False ,
4648 dQ_swapAB : bool = False ,
@@ -83,6 +85,7 @@ def __init__(
8385 self .num_threads = num_threads
8486 self .pack_gqa = pack_gqa
8587 self .is_causal = is_causal
88+ self .is_local = is_local
8689 self .num_stages_Q = num_stages_Q
8790 self .num_stages_dO = num_stages_dO
8891 self .SdP_swapAB = SdP_swapAB
@@ -436,7 +439,9 @@ def __call__(
436439 tile_sched_params = TileScheduler .to_underlying_arguments (tile_sched_args )
437440 grid_dim = TileScheduler .get_grid_shape (tile_sched_params )
438441
439- softmax_scale_log2 , softmax_scale = utils .compute_softmax_scale_log2 (softmax_scale , self .score_mod )
442+ softmax_scale_log2 , _ = utils .compute_softmax_scale_log2 (
443+ softmax_scale , self .score_mod
444+ )
440445 self .kernel (
441446 mQ ,
442447 mK ,
@@ -453,6 +458,8 @@ def __call__(
453458 mSeqUsedK ,
454459 softmax_scale ,
455460 softmax_scale_log2 ,
461+ window_size_left ,
462+ window_size_right ,
456463 self .sQ_layout ,
457464 self .sK_layout ,
458465 self .sV_layout ,
@@ -498,6 +505,8 @@ def kernel(
498505 mSeqUsedK : Optional [cute .Tensor ],
499506 softmax_scale : cutlass .Float32 ,
500507 softmax_scale_log2 : cutlass .Float32 ,
508+ window_size_left : Optional [Int32 ],
509+ window_size_right : Optional [Int32 ],
501510 sQ_layout : cute .ComposedLayout ,
502511 sK_layout : cute .ComposedLayout ,
503512 sV_layout : cute .ComposedLayout ,
@@ -540,13 +549,16 @@ def kernel(
540549 tile_n = self .n_block_size ,
541550 )
542551
543- m_block_max = cute .ceil_div (seqlen .seqlen_q , self .m_block_size )
544- m_block_min = 0
545- if cutlass .const_expr (self .is_causal ):
546- m_block_min = max (
547- (n_block * self .n_block_size + seqlen .seqlen_q - seqlen .seqlen_k ) // self .m_block_size ,
548- m_block_min ,
549- )
552+ block_info = BlockInfo (
553+ self .m_block_size ,
554+ self .n_block_size ,
555+ self .is_causal ,
556+ self .is_local ,
557+ False ,
558+ window_size_left ,
559+ window_size_right ,
560+ )
561+ m_block_min , m_block_max = block_info .get_m_block_min_max (seqlen , n_block )
550562 # TODO: return early if m_block_max == 0
551563
552564 # ///////////////////////////////////////////////////////////////////////////////
@@ -786,61 +798,68 @@ def kernel(
786798 aux_data = aux_data ,
787799 )
788800
789- # ///////////////////////////////////////////////////////////////////////////////
790- # Prologue
791- # ///////////////////////////////////////////////////////////////////////////////
792- # Start async loads of the last mn-tile, where we take care of the mn residue
793- self .load_V (gmem_thr_copy_VdO , tVgV , tVsV , n_block , seqlen = seqlen .seqlen_k ,
794- headdim = d_head_v )
795- if cutlass .const_expr (self .V_in_regs ):
796- cute .arch .cp_async_commit_group ()
797- self .load_K (gmem_thr_copy_QK , tKgK , tKsK , n_block , seqlen = seqlen .seqlen_k ,
798- headdim = d_head )
799- cute .arch .cp_async_commit_group ()
800-
801- if cutlass .const_expr (self .V_in_regs ):
802- cute .arch .cp_async_wait_group (1 )
803- cute .arch .barrier ()
804- tdPrV_copy_view = smem_thr_copy_KV .retile (tdPrV )
805- cute .copy (smem_thr_copy_KV , tdPsV , tdPrV_copy_view )
806- # Sync to avoid loading Q to smem_q, which overlaps with smem_v
807- cute .arch .barrier ()
808-
809- m_block = m_block_min
810- assert self .num_stages_Q >= self .num_stages_dO
811- for stage in cutlass .range_constexpr (self .num_stages_Q ):
812- if cutlass .const_expr (self .num_stages_Q == 1 or stage < self .num_stages_Q - 1 ):
813- if stage == 0 or m_block + stage < m_block_max :
814- load_Q_LSE (m_block + stage , smem_pipe_write_q = stage )
815- cute .arch .cp_async_commit_group ()
816- if cutlass .const_expr (stage < self .num_stages_dO ):
817- if stage == 0 or m_block + stage < m_block_max :
818- load_dO_dPsum (m_block + stage , smem_pipe_write_q = stage )
801+ if m_block_min < m_block_max :
802+ # ///////////////////////////////////////////////////////////////////////////////
803+ # Prologue
804+ # ///////////////////////////////////////////////////////////////////////////////
805+ # Start async loads of the last mn-tile, where we take care of the mn residue
806+ self .load_V (gmem_thr_copy_VdO , tVgV , tVsV , n_block , seqlen = seqlen .seqlen_k ,
807+ headdim = d_head_v )
808+ if cutlass .const_expr (self .V_in_regs ):
819809 cute .arch .cp_async_commit_group ()
810+ self .load_K (gmem_thr_copy_QK , tKgK , tKsK , n_block , seqlen = seqlen .seqlen_k ,
811+ headdim = d_head )
812+ cute .arch .cp_async_commit_group ()
820813
821- # ///////////////////////////////////////////////////////////////////////////////
822- # Mainloop
823- # ///////////////////////////////////////////////////////////////////////////////
824- # Start processing of the first n-block.
825- mask = AttentionMask (self .m_block_size , self .n_block_size , seqlen )
826- mask_fn = partial (
827- mask .apply_mask , n_block = n_block , thr_mma = thr_mma_sdp ,
828- batch_idx = batch_idx , head_idx = head_idx ,
829- mask_seqlen = True , mask_causal = self .is_causal
830- )
831- smem_pipe_read_q = cutlass .Int32 (0 )
832- smem_pipe_read_do = cutlass .Int32 (0 )
833- smem_pipe_write_q = cutlass .Int32 (self .num_stages_Q - 1 )
834- smem_pipe_write_do = cutlass .Int32 (0 )
835- for m_tile in cutlass .range (m_block_min , m_block_max , unroll = 1 ):
836- compute_one_m_block (
837- m_tile , smem_pipe_read_q , smem_pipe_read_do , smem_pipe_write_q , smem_pipe_write_do ,
838- mask_fn = mask_fn ,
814+ if cutlass .const_expr (self .V_in_regs ):
815+ cute .arch .cp_async_wait_group (1 )
816+ cute .arch .barrier ()
817+ tdPrV_copy_view = smem_thr_copy_KV .retile (tdPrV )
818+ cute .copy (smem_thr_copy_KV , tdPsV , tdPrV_copy_view )
819+ # Sync to avoid loading Q to smem_q, which overlaps with smem_v
820+ cute .arch .barrier ()
821+
822+ m_block = m_block_min
823+ assert self .num_stages_Q >= self .num_stages_dO
824+ for stage in cutlass .range_constexpr (self .num_stages_Q ):
825+ if cutlass .const_expr (self .num_stages_Q == 1 or stage < self .num_stages_Q - 1 ):
826+ if stage == 0 or m_block + stage < m_block_max :
827+ load_Q_LSE (m_block + stage , smem_pipe_write_q = stage )
828+ cute .arch .cp_async_commit_group ()
829+ if cutlass .const_expr (stage < self .num_stages_dO ):
830+ if stage == 0 or m_block + stage < m_block_max :
831+ load_dO_dPsum (m_block + stage , smem_pipe_write_q = stage )
832+ cute .arch .cp_async_commit_group ()
833+
834+ # ///////////////////////////////////////////////////////////////////////////////
835+ # Mainloop
836+ # ///////////////////////////////////////////////////////////////////////////////
837+ # Start processing of the first n-block.
838+ mask = AttentionMask (
839+ self .m_block_size ,
840+ self .n_block_size ,
841+ seqlen ,
842+ window_size_left ,
843+ window_size_right ,
844+ )
845+ mask_fn = partial (
846+ mask .apply_mask , n_block = n_block , thr_mma = thr_mma_sdp ,
847+ batch_idx = batch_idx , head_idx = head_idx ,
848+ mask_seqlen = True , mask_causal = self .is_causal , mask_local = self .is_local
839849 )
840- smem_pipe_read_q = self .advance_pipeline (smem_pipe_read_q , self .num_stages_Q )
841- smem_pipe_read_do = self .advance_pipeline (smem_pipe_read_do , self .num_stages_dO )
842- smem_pipe_write_q = self .advance_pipeline (smem_pipe_write_q , self .num_stages_Q )
843- smem_pipe_write_do = self .advance_pipeline (smem_pipe_write_do , self .num_stages_dO )
850+ smem_pipe_read_q = cutlass .Int32 (0 )
851+ smem_pipe_read_do = cutlass .Int32 (0 )
852+ smem_pipe_write_q = cutlass .Int32 (self .num_stages_Q - 1 )
853+ smem_pipe_write_do = cutlass .Int32 (0 )
854+ for m_tile in cutlass .range (m_block_min , m_block_max , unroll = 1 ):
855+ compute_one_m_block (
856+ m_tile , smem_pipe_read_q , smem_pipe_read_do , smem_pipe_write_q , smem_pipe_write_do ,
857+ mask_fn = mask_fn ,
858+ )
859+ smem_pipe_read_q = self .advance_pipeline (smem_pipe_read_q , self .num_stages_Q )
860+ smem_pipe_read_do = self .advance_pipeline (smem_pipe_read_do , self .num_stages_dO )
861+ smem_pipe_write_q = self .advance_pipeline (smem_pipe_write_q , self .num_stages_Q )
862+ smem_pipe_write_do = self .advance_pipeline (smem_pipe_write_do , self .num_stages_dO )
844863
845864 # ///////////////////////////////////////////////////////////////////////////////
846865 # Epilogue
0 commit comments