Repository navigation
Add aiter_sage_hub attention backend for AMD RDNA4 (gfx1201)
#15060
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -23,8 +23,13 @@ | |||
| import torch.nn.functional as F | ||||
|
|
||||
| from diffusers.models._modeling_parallel import ContextParallelConfig, ParallelConfig | ||||
| from diffusers.models.attention_dispatch import ( | ||||
| _HUB_KERNELS_REGISTRY, | ||||
| AttentionBackendName, | ||||
| _AttentionBackendRegistry, | ||||
| dispatch_attention_fn, | ||||
| ) | ||||
| from diffusers.models.attention_dispatch import attention_backend as attention_backend_ctx | ||||
| from diffusers.models.attention_dispatch import dispatch_attention_fn | ||||
|
|
||||
| from ..testing_utils import ( | ||||
| is_attention, | ||||
|
|
@@ -35,6 +40,7 @@ | |||
| require_torch_multi_accelerator, | ||||
| torch_device, | ||||
| ) | ||||
| from .testing_utils.attention import _IS_GFX1201 | ||||
| from .testing_utils.parallelism import DEVICE_CONFIG, _find_free_port | ||||
|
|
||||
|
|
||||
|
|
@@ -231,3 +237,78 @@ def make_qkv(seq_len): | |||
| ) | ||||
| finally: | ||||
| torch.compiler.reset() | ||||
|
|
||||
|
|
||||
| @is_attention | ||||
| class TestAiterSageHubFallback: | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good but it needs to be decorated with something like
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tests replace the Hub kernel with a stub, so they run on CPU without a gfx1201 GPU or
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that would be nice? We could add that to
WDYT?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in a09f58b: On a gfx1201 GPU (torch 2.13, kernels 0.16.1):
|
||||
| """`aiter_sage_hub` runs the gfx1201 kernel only on inputs it supports and falls back to native SDPA otherwise. | ||||
|
|
||||
| The kernel is replaced by a stub, so this runs without a gfx1201 GPU or the Hub kernel. | ||||
| """ | ||||
|
|
||||
| @pytest.fixture | ||||
| def kernel_calls(self, monkeypatch): | ||||
| calls = [] | ||||
|
|
||||
| def fake_sageattn(q, k, v, tensor_layout, is_causal, sm_scale): | ||||
| calls.append(tensor_layout) | ||||
| return torch.zeros_like(q) | ||||
|
|
||||
| config = _HUB_KERNELS_REGISTRY[AttentionBackendName.AITER_SAGE_HUB] | ||||
| monkeypatch.setattr(config, "kernel_fn", fake_sageattn) | ||||
| return calls | ||||
|
|
||||
| @staticmethod | ||||
| def _run(query, key, value, **kwargs): | ||||
| backend_fn = _AttentionBackendRegistry._backends[AttentionBackendName.AITER_SAGE_HUB] | ||||
| return backend_fn(query, key, value, **kwargs) | ||||
|
|
||||
| @staticmethod | ||||
| def _qkv(seq_q=16, seq_kv=16, head_dim=128, dtype=torch.bfloat16): | ||||
| q = torch.randn(1, seq_q, 2, head_dim, dtype=dtype) | ||||
| k = torch.randn(1, seq_kv, 2, head_dim, dtype=dtype) | ||||
| v = torch.randn(1, seq_kv, 2, head_dim, dtype=dtype) | ||||
| return q, k, v | ||||
|
|
||||
| def test_supported_inputs_use_kernel(self, kernel_calls): | ||||
| self._run(*self._qkv()) | ||||
| assert len(kernel_calls) == 1 | ||||
|
|
||||
| @pytest.mark.parametrize( | ||||
| "qkv_kwargs, call_kwargs", | ||||
| [ | ||||
| ({"head_dim": 64}, {}), | ||||
| ({"dtype": torch.float32}, {}), | ||||
| ({"seq_kv": 8}, {}), | ||||
| ({}, {"is_causal": True}), | ||||
| ({}, {"attn_mask": torch.ones(16, 16, dtype=torch.bool)}), | ||||
| ], | ||||
| ids=["head_dim_64", "fp32", "cross_attention", "causal", "mask"], | ||||
| ) | ||||
| def test_unsupported_inputs_fall_back_to_native(self, kernel_calls, qkv_kwargs, call_kwargs): | ||||
| query, key, value = self._qkv(**qkv_kwargs) | ||||
| out = self._run(query, key, value, **call_kwargs) | ||||
| ref = _AttentionBackendRegistry._backends[AttentionBackendName.NATIVE](query, key, value, **call_kwargs) | ||||
| assert kernel_calls == [] | ||||
| assert torch.equal(out, ref) | ||||
|
|
||||
| def test_ring_attention_raises(self, kernel_calls): | ||||
| config = ParallelConfig(context_parallel_config=ContextParallelConfig(ring_degree=2)) | ||||
| with pytest.raises(NotImplementedError, match="ring_degree"): | ||||
| self._run(*self._qkv(), _parallel_config=config) | ||||
|
|
||||
|
|
||||
| @is_attention | ||||
| @pytest.mark.skipif(not _IS_GFX1201, reason="A gfx1201 GPU is required for the aiter_sage_hub kernel.") | ||||
| @pytest.mark.skipif(not is_kernels_available(), reason="`kernels` is not available.") | ||||
| class TestAiterSageHubKernel: | ||||
| def test_output_close_to_native(self): | ||||
| torch.manual_seed(0) | ||||
| query, key, value = (torch.randn(1, 1024, 4, 128, device=torch_device, dtype=torch.bfloat16) for _ in range(3)) | ||||
| ref = dispatch_attention_fn(query, key, value, backend=AttentionBackendName.NATIVE) | ||||
| with attention_backend_ctx(AttentionBackendName.AITER_SAGE_HUB): | ||||
| out = dispatch_attention_fn(query, key, value) | ||||
| # The quantized kernel never matches SDPA exactly, so equality would mean the fallback ran. | ||||
| assert not torch.equal(out, ref) | ||||
| cos = F.cosine_similarity(out.float().flatten(), ref.float().flatten(), dim=0) | ||||
| assert cos > 0.99 | ||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we tested the CP bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. With Ulysses degree 2 on two gfx1201 GPUs, the output of
dispatch_attention_fnis bit-identical to the single-GPU run, both for self-attention (kernel) and for cross-attention with shorter K/V (fallback). Ring attention raisesNotImplementedError.TestContextParallelAttentionBackwardchecks gradients, so it doesn't apply to this inference-only backend.