multimodalart's picture
multimodalart HF Staff
Bernini-Diffusers-v2 r2v demo
fed6c68 verified
Raw
History Blame Contribute Delete
3.42 kB
# Copyright (c) 2026 Bytedance Ltd. and/or its affiliate
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Variable-length attention with an auto-selected backend.
Backend priority, probed once at import time:
1. FlashAttention-3 (``flash_attn_interface``) -- fastest on Hopper / H100
2. FlashAttention-2 (``flash_attn``) -- general CUDA GPUs
3. PyTorch SDPA -- always available, no extra dep
All backends share the same varlen contract: ``q``/``k``/``v`` are packed as
``[total_tokens, num_heads, head_dim]`` and ``cu_seqlens_*`` give the per-sample
offsets into ``total_tokens``.
"""
import torch
import torch.nn.functional as F
_BACKEND = None
_flash_varlen = None
def _select_backend():
global _BACKEND, _flash_varlen
if _BACKEND is not None:
return
try:
from flash_attn_interface import flash_attn_varlen_func # FA3
_flash_varlen, _BACKEND = flash_attn_varlen_func, "fa3"
return
except Exception:
pass
try:
from flash_attn import flash_attn_varlen_func # FA2
_flash_varlen, _BACKEND = flash_attn_varlen_func, "fa2"
return
except Exception:
pass
_BACKEND = "sdpa"
def get_attention_backend() -> str:
_select_backend()
return _BACKEND
def _sdpa_varlen(q, k, v, cu_seqlens_q, cu_seqlens_k, causal):
"""Varlen attention via SDPA: run each sample's segment, then concatenate."""
cq = cu_seqlens_q.tolist()
ck = cu_seqlens_k.tolist()
outs = []
for i in range(len(cq) - 1):
# [seq, H, D] -> [1, H, seq, D]
qi = q[cq[i] : cq[i + 1]].transpose(0, 1).unsqueeze(0)
ki = k[ck[i] : ck[i + 1]].transpose(0, 1).unsqueeze(0)
vi = v[ck[i] : ck[i + 1]].transpose(0, 1).unsqueeze(0)
oi = F.scaled_dot_product_attention(qi, ki, vi, is_causal=causal)
outs.append(oi.squeeze(0).transpose(0, 1)) # back to [seq, H, D]
return torch.cat(outs, dim=0)
def varlen_attention(
q,
k,
v,
cu_seqlens_q,
cu_seqlens_k,
max_seqlen_q,
max_seqlen_k,
causal: bool = False,
):
"""Variable-length attention. Returns ``[total_q_tokens, num_heads, head_dim]``."""
_select_backend()
if _BACKEND == "fa3":
out = _flash_varlen(
q,
k,
v,
cu_seqlens_q=cu_seqlens_q,
cu_seqlens_k=cu_seqlens_k,
max_seqlen_q=int(max_seqlen_q),
max_seqlen_k=int(max_seqlen_k),
causal=causal,
)
return out[0] if isinstance(out, tuple) else out
if _BACKEND == "fa2":
return _flash_varlen(
q,
k,
v,
cu_seqlens_q,
cu_seqlens_k,
int(max_seqlen_q),
int(max_seqlen_k),
causal=causal,
)
return _sdpa_varlen(q, k, v, cu_seqlens_q, cu_seqlens_k, causal)