diff --git a/megatron/core/transformer/attention.py b/megatron/core/transformer/attention.py index c53664d037d..f89259be442 100644 --- a/megatron/core/transformer/attention.py +++ b/megatron/core/transformer/attention.py @@ -81,6 +81,13 @@ except ImportError as e: pass +try: + from flash_attn.cute import flash_attn_varlen_func as flash_attn4_varlen_func + + HAVE_FA4 = True +except ImportError: + HAVE_FA4 = False + try: from flash_mla import flash_mla_with_kvcache, get_mla_metadata @@ -828,7 +835,21 @@ def flash_decode_and_prefill( softmax_scale = self.softmax_scale else: softmax_scale = q.shape[-1] ** -0.5 - if HAVE_FA3: + if HAVE_FA4: + output_total, _ = flash_attn4_varlen_func( + q, + k, + v, + cu_seqlens_q=cu_seqlens_q, + max_seqlen_q=max_seqlen_q, + max_seqlen_k=max_seqlen_k, + seqused_k=seqlens_k, + page_table=block_table, + softmax_scale=softmax_scale, + causal=True, + num_splits=1, + ) + elif HAVE_FA3: # TODO(ksanthanam): Replace with call to flash_attn_varlen_func once # it accepts block_table output_total = self._flash_attention_3_forward_wrapper( @@ -898,22 +919,47 @@ def flash_decode_and_prefill( causal=True, ) else: - flash_attn_args = { - "q": q, - "k_cache": k, - "v_cache": v, - "cache_seqlens": seqlens_k, - "causal": True, - "page_table" if HAVE_FA3 else "block_table": block_table, - "num_splits": 0 if not self.batch_invariant_mode else 1, - } - if HAVE_FA3: - output_total = flash_attn3_with_kvcache(**flash_attn_args) + if HAVE_FA4: + if getattr(self, "softmax_scale", None) is not None: + softmax_scale = self.softmax_scale + else: + softmax_scale = q.shape[-1] ** -0.5 + # Reshape q from (B, S, H, D) to (B*S, H, D) for varlen interface + q_varlen = q.reshape(-1, q.shape[-2], q.shape[-1]) + output_total, _ = flash_attn4_varlen_func( + q_varlen, + k, + v, + cu_seqlens_q=cu_seqlens_q, + max_seqlen_q=tokens_per_request, + max_seqlen_k=max_seqlen_k, + seqused_k=seqlens_k, + page_table=block_table, + softmax_scale=softmax_scale, + causal=True, + num_splits=1, + ) + # Reshape back to (B, S, H, D) + output_total = output_total.reshape( + num_requests, tokens_per_request, *output_total.shape[1:] + ) else: - assert ( - not self.batch_invariant_mode - ), "Batch invariant mode is not supported for flash attention 2" - output_total = flash_attn_with_kvcache(**flash_attn_args) + flash_attn_args = { + "q": q, + "k_cache": k, + "v_cache": v, + "cache_seqlens": seqlens_k, + "causal": True, + "page_table" if HAVE_FA3 else "block_table": block_table, + "num_splits": 0 if not self.batch_invariant_mode else 1, + } + if HAVE_FA3: + output_total = flash_attn3_with_kvcache(**flash_attn_args) + else: + assert ( + not self.batch_invariant_mode + ), "Batch invariant mode is not supported for flash attention 2" + output_total = flash_attn_with_kvcache(**flash_attn_args) # Reshape back to (B*S, 1, H, D) for consistent output shape. output_total = output_total.reshape( @@ -973,8 +1019,8 @@ def forward( inference_context = deprecate_inference_params(inference_context, inference_params) if inference_context and inference_context.is_dynamic_batching(): - assert HAVE_FA3 or is_fa_min_version( - "2.7.3" + assert ( + HAVE_FA4 or HAVE_FA3 or is_fa_min_version("2.7.3") ), "flash attn verion v2.7.3 and above is required for dynamic batching." # hidden_states: [sq, b, h]