[diffusion] fix: fix RuntimeError in SageAttention3 on Blackwell with Qwen-Image (#16335)
Co-authored-by: qimcis <qimcis@users.noreply.github.com> Co-authored-by: Mick <mickjagger19@icloud.com>
This commit is contained in:
co-authored by
qimcis
Mick
parent
87ef05e2e1
commit
dcacc492d0
@@ -3,6 +3,7 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
import torch.nn.functional as F
|
||||||
from sageattn3 import sageattn3_blackwell
|
from sageattn3 import sageattn3_blackwell
|
||||||
|
|
||||||
from sglang.multimodal_gen.runtime.layers.attention.backends.attention_backend import (
|
from sglang.multimodal_gen.runtime.layers.attention.backends.attention_backend import (
|
||||||
@@ -17,7 +18,6 @@ logger = init_logger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class SageAttention3Backend(AttentionBackend):
|
class SageAttention3Backend(AttentionBackend):
|
||||||
|
|
||||||
accept_output_buffer: bool = True
|
accept_output_buffer: bool = True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -38,6 +38,7 @@ class SageAttention3Backend(AttentionBackend):
|
|||||||
|
|
||||||
|
|
||||||
class SageAttention3Impl(AttentionImpl):
|
class SageAttention3Impl(AttentionImpl):
|
||||||
|
_warned_gqa_fallback_global: bool = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -63,6 +64,29 @@ class SageAttention3Impl(AttentionImpl):
|
|||||||
query = query.transpose(1, 2)
|
query = query.transpose(1, 2)
|
||||||
key = key.transpose(1, 2)
|
key = key.transpose(1, 2)
|
||||||
value = value.transpose(1, 2)
|
value = value.transpose(1, 2)
|
||||||
output = sageattn3_blackwell(query, key, value, is_causal=self.causal)
|
# SageAttention3's Blackwell kernel assumes MHA (Hq == Hkv). For GQA/MQA
|
||||||
|
# (Hq != Hkv), fall back to torch SDPA which supports GQA.
|
||||||
|
if key.shape[1] != query.shape[1]:
|
||||||
|
if query.shape[1] % key.shape[1] != 0:
|
||||||
|
raise ValueError(
|
||||||
|
"GQA/MQA requires query heads to be a multiple of KV heads, "
|
||||||
|
f"got q_heads={query.shape[1]} and kv_heads={key.shape[1]}"
|
||||||
|
)
|
||||||
|
if not type(self)._warned_gqa_fallback_global:
|
||||||
|
logger.warning(
|
||||||
|
"SageAttention3 does not support GQA/MQA (Hq != Hkv); falling back to torch SDPA."
|
||||||
|
)
|
||||||
|
type(self)._warned_gqa_fallback_global = True
|
||||||
|
output = F.scaled_dot_product_attention(
|
||||||
|
query,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
is_causal=self.causal,
|
||||||
|
dropout_p=self.dropout,
|
||||||
|
scale=self.softmax_scale,
|
||||||
|
enable_gqa=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
output = sageattn3_blackwell(query, key, value, is_causal=self.causal)
|
||||||
output = output.transpose(1, 2)
|
output = output.transpose(1, 2)
|
||||||
return output
|
return output
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"""Code inside this file can safely assume cuda platform, e.g. importing
|
"""Code inside this file can safely assume cuda platform, e.g. importing
|
||||||
pynvml. However, it should not initialize cuda context.
|
pynvml. However, it should not initialize cuda context.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from functools import lru_cache, wraps
|
from functools import lru_cache, wraps
|
||||||
@@ -130,7 +131,6 @@ class CudaPlatformBase(Platform):
|
|||||||
sm = capability.to_int() if capability else 0
|
sm = capability.to_int() if capability else 0
|
||||||
|
|
||||||
if sm in SHARED_SYSMEM_DEVICE_MEM_SMS:
|
if sm in SHARED_SYSMEM_DEVICE_MEM_SMS:
|
||||||
|
|
||||||
free_gpu_memory = psutil.virtual_memory().available
|
free_gpu_memory = psutil.virtual_memory().available
|
||||||
else:
|
else:
|
||||||
free_gpu_memory, _ = torch.cuda.mem_get_info(device_id)
|
free_gpu_memory, _ = torch.cuda.mem_get_info(device_id)
|
||||||
@@ -151,6 +151,7 @@ class CudaPlatformBase(Platform):
|
|||||||
head_size: int,
|
head_size: int,
|
||||||
dtype: torch.dtype,
|
dtype: torch.dtype,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
target_backend: AttentionBackendEnum | None = None
|
||||||
# TODO(will): maybe come up with a more general interface for local attention
|
# TODO(will): maybe come up with a more general interface for local attention
|
||||||
# if distributed is False, we always try to use Flash attn
|
# if distributed is False, we always try to use Flash attn
|
||||||
if selected_backend == AttentionBackendEnum.SLIDING_TILE_ATTN:
|
if selected_backend == AttentionBackendEnum.SLIDING_TILE_ATTN:
|
||||||
@@ -187,6 +188,7 @@ class CudaPlatformBase(Platform):
|
|||||||
logger.info(
|
logger.info(
|
||||||
"Sage Attention backend is not installed (To install it, run `pip install sageattention==2.2.0 --no-build-isolation`). Falling back to Flash Attention."
|
"Sage Attention backend is not installed (To install it, run `pip install sageattention==2.2.0 --no-build-isolation`). Falling back to Flash Attention."
|
||||||
)
|
)
|
||||||
|
target_backend = AttentionBackendEnum.FA
|
||||||
elif selected_backend == AttentionBackendEnum.SAGE_ATTN_3:
|
elif selected_backend == AttentionBackendEnum.SAGE_ATTN_3:
|
||||||
try:
|
try:
|
||||||
from sglang.multimodal_gen.runtime.layers.attention.backends.sage_attn3 import ( # noqa: F401
|
from sglang.multimodal_gen.runtime.layers.attention.backends.sage_attn3 import ( # noqa: F401
|
||||||
@@ -198,8 +200,9 @@ class CudaPlatformBase(Platform):
|
|||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
logger.info(e)
|
logger.info(e)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Sage Attention 3 backend is not installed (To install it, see https://github.com/thu-ml/SageAttention/tree/main/sageattention3_blackwell#installation). Falling back to Flash Attention."
|
"Sage Attention 3 backend is not installed (To install it, see https://github.com/thu-ml/SageAttention/tree/main/sageattention3_blackwell#installation). Falling back to Torch SDPA."
|
||||||
)
|
)
|
||||||
|
target_backend = AttentionBackendEnum.TORCH_SDPA
|
||||||
elif selected_backend == AttentionBackendEnum.VIDEO_SPARSE_ATTN:
|
elif selected_backend == AttentionBackendEnum.VIDEO_SPARSE_ATTN:
|
||||||
try:
|
try:
|
||||||
from vsa import block_sparse_attn # noqa: F401
|
from vsa import block_sparse_attn # noqa: F401
|
||||||
@@ -245,43 +248,51 @@ class CudaPlatformBase(Platform):
|
|||||||
elif selected_backend in [
|
elif selected_backend in [
|
||||||
AttentionBackendEnum.FA,
|
AttentionBackendEnum.FA,
|
||||||
]:
|
]:
|
||||||
if cls.is_blackwell():
|
if cls.is_sm120():
|
||||||
|
logger.info(
|
||||||
|
"FlashAttention is not supported on SM12.x in this build; falling back to Torch SDPA."
|
||||||
|
)
|
||||||
|
target_backend = AttentionBackendEnum.TORCH_SDPA
|
||||||
|
elif cls.is_blackwell():
|
||||||
from sglang.multimodal_gen.runtime.layers.attention.backends.flash_attn import (
|
from sglang.multimodal_gen.runtime.layers.attention.backends.flash_attn import (
|
||||||
set_fa_ver,
|
set_fa_ver,
|
||||||
)
|
)
|
||||||
|
|
||||||
set_fa_ver(4)
|
set_fa_ver(4)
|
||||||
target_backend = AttentionBackendEnum.FA
|
target_backend = AttentionBackendEnum.FA
|
||||||
|
else:
|
||||||
|
target_backend = AttentionBackendEnum.FA
|
||||||
elif selected_backend:
|
elif selected_backend:
|
||||||
raise ValueError(f"Invalid attention backend for {cls.device_name}")
|
raise ValueError(f"Invalid attention backend for {cls.device_name}")
|
||||||
else:
|
else:
|
||||||
|
if cls.is_sm120():
|
||||||
if cls.is_blackwell():
|
# On SM12.x, the sgl-kernel FlashAttention wheels may not include
|
||||||
|
# support yet. Default to Torch SDPA for correctness.
|
||||||
|
logger.info("Defaulting to Torch SDPA backend on SM12.x")
|
||||||
|
target_backend = AttentionBackendEnum.TORCH_SDPA
|
||||||
|
elif cls.is_blackwell():
|
||||||
from sglang.multimodal_gen.runtime.layers.attention.backends.flash_attn import (
|
from sglang.multimodal_gen.runtime.layers.attention.backends.flash_attn import (
|
||||||
set_fa_ver,
|
set_fa_ver,
|
||||||
)
|
)
|
||||||
|
|
||||||
set_fa_ver(4)
|
set_fa_ver(4)
|
||||||
target_backend = AttentionBackendEnum.FA
|
target_backend = AttentionBackendEnum.FA
|
||||||
if cls.is_sm120():
|
else:
|
||||||
try:
|
target_backend = AttentionBackendEnum.FA
|
||||||
from sglang.multimodal_gen.runtime.layers.attention.backends.sage_attn3 import ( # noqa: F401
|
|
||||||
SageAttention3Backend,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info("Using Sage Attention 3 backend")
|
# Ensure we have a target backend selected before validation/fallback.
|
||||||
return "sglang.multimodal_gen.runtime.layers.attention.backends.sage_attn3.SageAttention3Backend"
|
if target_backend is None:
|
||||||
except ImportError as e:
|
target_backend = AttentionBackendEnum.FA
|
||||||
logger.info(e)
|
|
||||||
logger.info(
|
if target_backend == AttentionBackendEnum.FA and cls.is_blackwell():
|
||||||
"Sage Attention 3 backend is not installed, Falling back to Torch SDPA (To install it, see https://github.com/thu-ml/SageAttention/tree/main/sageattention3_blackwell#installation)"
|
from sglang.multimodal_gen.runtime.layers.attention.backends.flash_attn import (
|
||||||
)
|
set_fa_ver,
|
||||||
target_backend = AttentionBackendEnum.TORCH_SDPA
|
)
|
||||||
|
|
||||||
|
set_fa_ver(4)
|
||||||
|
|
||||||
if not cls.has_device_capability(80):
|
if not cls.has_device_capability(80):
|
||||||
logger.info(
|
logger.info("Cannot use FlashAttention backend for Volta and Turing GPUs.")
|
||||||
"Cannot use FlashAttention backend for Volta and Turing " "GPUs."
|
|
||||||
)
|
|
||||||
target_backend = AttentionBackendEnum.TORCH_SDPA
|
target_backend = AttentionBackendEnum.TORCH_SDPA
|
||||||
elif dtype not in (torch.float16, torch.bfloat16):
|
elif dtype not in (torch.float16, torch.bfloat16):
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -332,7 +343,6 @@ class CudaPlatformBase(Platform):
|
|||||||
# all the related functions work on real physical device ids.
|
# all the related functions work on real physical device ids.
|
||||||
# the major benefit of using NVML is that it will not initialize CUDA
|
# the major benefit of using NVML is that it will not initialize CUDA
|
||||||
class NvmlCudaPlatform(CudaPlatformBase):
|
class NvmlCudaPlatform(CudaPlatformBase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@lru_cache(maxsize=8)
|
@lru_cache(maxsize=8)
|
||||||
@with_nvml_context
|
@with_nvml_context
|
||||||
@@ -431,7 +441,6 @@ class NvmlCudaPlatform(CudaPlatformBase):
|
|||||||
|
|
||||||
|
|
||||||
class NonNvmlCudaPlatform(CudaPlatformBase):
|
class NonNvmlCudaPlatform(CudaPlatformBase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_device_capability(cls, device_id: int = 0) -> DeviceCapability:
|
def get_device_capability(cls, device_id: int = 0) -> DeviceCapability:
|
||||||
major, minor = torch.cuda.get_device_capability(device_id)
|
major, minor = torch.cuda.get_device_capability(device_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user