177 lines
6.2 KiB
Python
177 lines
6.2 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING
|
|
|
|
import torch
|
|
from vllm.logger import init_logger
|
|
|
|
# import torch.distributed as dist # Not used directly here, but good practice if needed
|
|
from vllm_omni.diffusion.attention.backends.ring.ring_globals import HAS_AITER, HAS_FA3, HAS_FA4, HAS_FLASH_ATTN
|
|
from vllm_omni.diffusion.attention.backends.ring.ring_selector import AttnType
|
|
from vllm_omni.diffusion.attention.parallel.base import (
|
|
ParallelAttentionContext,
|
|
# ParallelAttentionStrategy, # Not used in type hint below currently
|
|
)
|
|
from vllm_omni.diffusion.distributed.group_coordinator import SequenceParallelGroupCoordinator
|
|
|
|
if TYPE_CHECKING:
|
|
from vllm_omni.diffusion.attention.backends.abstract import AttentionMetadata
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class _RingCtx(ParallelAttentionContext):
|
|
"""Per-forward context for Ring sequence-parallel attention."""
|
|
|
|
# Ring attention typically doesn't need complex context for post-processing
|
|
# as the output is already correctly sharded along sequence dimension.
|
|
pass
|
|
|
|
|
|
class RingParallelAttention:
|
|
"""Ring sequence-parallel strategy.
|
|
|
|
This strategy prepares inputs for Ring Attention.
|
|
Key responsibilities:
|
|
- Concatenate joint_query (Text) to query (Image) if present.
|
|
- Keep joint_key/value separate in metadata for the Ring kernel to handle as static prefix.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
sp_group: SequenceParallelGroupCoordinator,
|
|
attn_backend_pref: str | None = None,
|
|
) -> None:
|
|
self._sp_group = sp_group
|
|
self.attn_backend_pref = attn_backend_pref
|
|
|
|
@property
|
|
def enabled(self) -> bool:
|
|
return True
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "ring"
|
|
|
|
def pre_attention(
|
|
self,
|
|
query: torch.Tensor,
|
|
key: torch.Tensor,
|
|
value: torch.Tensor,
|
|
attn_metadata: AttentionMetadata | None,
|
|
):
|
|
joint_tensor_query = None
|
|
joint_strategy = "front"
|
|
|
|
if attn_metadata is not None:
|
|
joint_tensor_query = attn_metadata.joint_query
|
|
joint_strategy = attn_metadata.joint_strategy
|
|
|
|
if joint_tensor_query is not None:
|
|
supported_joint_strategy = ["front", "rear"]
|
|
if joint_strategy not in supported_joint_strategy:
|
|
raise ValueError(f"joint_strategy: {joint_strategy} not supported.")
|
|
|
|
if joint_strategy == "front":
|
|
query = torch.cat([joint_tensor_query, query], dim=1)
|
|
else:
|
|
query = torch.cat([query, joint_tensor_query], dim=1)
|
|
|
|
# Note: We do NOT concatenate joint_key/value here.
|
|
# They are preserved in attn_metadata and will be passed
|
|
# explicitly to ring_flash_attn_func.
|
|
|
|
ctx = _RingCtx(name=self.name)
|
|
return query, key, value, attn_metadata, ctx
|
|
|
|
def post_attention(self, attn_output: torch.Tensor, ctx: ParallelAttentionContext | None) -> torch.Tensor:
|
|
# Ring attention output is already sharded correctly along sequence dimension.
|
|
return attn_output
|
|
|
|
def run_attention(
|
|
self,
|
|
query: torch.Tensor,
|
|
key: torch.Tensor,
|
|
value: torch.Tensor,
|
|
attn_metadata: AttentionMetadata | None,
|
|
softmax_scale: float | None = None,
|
|
causal: bool = False,
|
|
) -> torch.Tensor:
|
|
"""Run the actual Ring Attention kernel."""
|
|
if softmax_scale is None:
|
|
softmax_scale = query.shape[-1] ** -0.5
|
|
|
|
backend_pref = self.attn_backend_pref
|
|
if backend_pref is not None:
|
|
backend_pref = backend_pref.lower()
|
|
|
|
# FP32 is not supported by Flash Attention, force SDPA
|
|
if query.dtype == torch.float32:
|
|
backend_pref = "sdpa"
|
|
elif not HAS_FA3 and not HAS_FLASH_ATTN and not HAS_AITER:
|
|
if backend_pref != "sdpa":
|
|
logger.warning_once("Flash Attention (FA2/FA3/AITER) is not available! Force enabling SDPA.")
|
|
backend_pref = "sdpa"
|
|
|
|
# Extract joint tensors
|
|
joint_key, joint_value = None, None
|
|
joint_strategy = "front"
|
|
if attn_metadata is not None:
|
|
joint_key = attn_metadata.joint_key
|
|
joint_value = attn_metadata.joint_value
|
|
if attn_metadata.joint_strategy is not None:
|
|
joint_strategy = attn_metadata.joint_strategy
|
|
|
|
if backend_pref in {"sdpa", "torch", "torch_sdpa"}:
|
|
from vllm_omni.diffusion.attention.backends.ring_pytorch_attn import ring_pytorch_attn_func
|
|
|
|
return ring_pytorch_attn_func(
|
|
query,
|
|
key,
|
|
value,
|
|
softmax_scale=softmax_scale,
|
|
causal=causal,
|
|
group=self._sp_group.ring_group,
|
|
op_type="efficient",
|
|
joint_tensor_key=joint_key,
|
|
joint_tensor_value=joint_value,
|
|
joint_strategy=joint_strategy,
|
|
)
|
|
|
|
from vllm_omni.diffusion.attention.backends.ring_flash_attn import ring_flash_attn_func
|
|
|
|
# Prefer FA4 on Blackwell. An importable Hopper-only FA3 wheel can
|
|
# otherwise be selected and fail at launch with "no kernel image".
|
|
if HAS_FA4 and query.is_cuda and torch.cuda.get_device_capability(query.device)[0] >= 10:
|
|
attn_type = AttnType.FA4
|
|
# Prefer FA3 over FA2 on Ampere/Ada/Hopper. On ROCm, use AITER.
|
|
elif HAS_FA3:
|
|
attn_type = AttnType.FA3
|
|
elif HAS_AITER:
|
|
attn_type = AttnType.AITER
|
|
else:
|
|
attn_type = AttnType.FA
|
|
|
|
return ring_flash_attn_func(
|
|
query,
|
|
key,
|
|
value,
|
|
dropout_p=0.0,
|
|
softmax_scale=softmax_scale,
|
|
causal=causal,
|
|
window_size=(-1, -1),
|
|
softcap=0.0,
|
|
alibi_slopes=None,
|
|
deterministic=False,
|
|
group=self._sp_group.ring_group,
|
|
attn_type=attn_type,
|
|
joint_tensor_key=joint_key,
|
|
joint_tensor_value=joint_value,
|
|
joint_strategy=joint_strategy,
|
|
)
|