Files
vllm-omni/vllm_omni/diffusion/attention/ops/block_sparse.py
T

116 lines
4.8 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Shared pooling, routing and provider calls for explicit block-sparse attention."""
import os
import torch
from vllm.logger import init_logger
logger = init_logger(__name__)
if not hasattr(torch.ops.vllm_omni, "fastvideo_block_sparse_attn_bshd"):
@torch.library.custom_op("vllm_omni::fastvideo_block_sparse_attn_bshd", mutates_args=())
def fastvideo_block_sparse_attn_bshd(
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
block_map: torch.Tensor,
variable_block_sizes: torch.Tensor,
logical_blocks: int,
) -> torch.Tensor:
"""Run explicit block-sparse attention on BSHD tensors in 64-row blocks.
Inputs and the block map may include transport-only blocks after
``logical_blocks``. The Triton provider sees only logical blocks and
the wrapper restores the original row count. Native provider selection
and its supported-input checks remain owned by FastVideo.
"""
q = query.transpose(1, 2).contiguous()
k = key.transpose(1, 2).contiguous()
v = value.transpose(1, 2).contiguous()
# Prefer the explicitly selected native provider when it supports
# these tensors; retain the existing Triton provider otherwise.
if os.environ.get("FASTVIDEO_VSA_SM100A", "0") == "1":
try:
from fastvideo_kernel import block_sparse_attn_sm100a
from fastvideo_kernel.triton_kernels.index import map_to_index
if block_sparse_attn_sm100a.is_supported(q, variable_block_sizes):
q2k_idx, q2k_num = map_to_index(block_map)
out, _ = block_sparse_attn_sm100a.block_sparse_attn_sm100a(
q,
k,
v,
q2k_idx.to(torch.int32).contiguous(),
q2k_num.to(torch.int32).contiguous(),
variable_block_sizes.to(torch.int32).contiguous(),
need_lse=False,
)
return out.transpose(1, 2).contiguous()
except torch.AcceleratorError:
# A device fault can poison the context; do not launch another kernel.
raise
except (ImportError, RuntimeError) as exc:
logger.warning_once(
"FASTVIDEO_VSA_SM100A=1 requested but the native Blackwell forward is "
"unavailable (%s); using the Triton block-sparse route instead.",
exc,
)
from fastvideo_kernel.block_sparse_attn import block_sparse_attn
logical_len = logical_blocks * 64
out, _ = block_sparse_attn(
q[:, :, :logical_len].contiguous(),
k[:, :, :logical_len].contiguous(),
v[:, :, :logical_len].contiguous(),
block_map[..., :logical_blocks, :logical_blocks].contiguous(),
variable_block_sizes[:logical_blocks].to(torch.int32).contiguous(),
)
out = out.transpose(1, 2).contiguous()
if out.shape[1] != query.shape[1]:
out = torch.nn.functional.pad(out, (0, 0, 0, 0, 0, query.shape[1] - out.shape[1]))
return out
@fastvideo_block_sparse_attn_bshd.register_fake
def _(query, key, value, block_map, variable_block_sizes, logical_blocks):
del key, value, block_map, variable_block_sizes, logical_blocks
return torch.empty_like(query)
fastvideo_block_sparse_attn_bshd = torch.ops.vllm_omni.fastvideo_block_sparse_attn_bshd
def mean_pool_tiles(x: torch.Tensor, sizes: torch.Tensor, block_size: int) -> torch.Tensor:
"""Mean-pool zero-padded BSHD tiles with FP32 accumulation.
``sizes`` gives the number of valid rows in each tile.
"""
batch, seq_len, heads, dim = x.shape
blocks = seq_len // block_size
pooled = x.view(batch, blocks, block_size, heads, dim).sum(dim=2, dtype=torch.float32)
pooled = pooled / sizes.view(1, -1, 1, 1).clamp_min(1)
return pooled.permute(0, 2, 1, 3)
def build_prefix_dense_block_map(
scores: torch.Tensor,
num_prefix_blocks: int,
num_sparse_blocks: int,
topk: int,
) -> torch.Tensor:
"""Keep prefix queries/keys dense and select top-k non-prefix keys per row."""
keep_sparse = min(topk, num_sparse_blocks)
if keep_sparse == num_sparse_blocks:
return torch.ones_like(scores, dtype=torch.bool)
block_map = torch.zeros_like(scores, dtype=torch.bool)
indices = scores[..., num_prefix_blocks:].topk(keep_sparse, dim=-1).indices + num_prefix_blocks
block_map.scatter_(-1, indices, True)
block_map[..., :num_prefix_blocks] = True
block_map[:, :, :num_prefix_blocks, :] = True
return block_map