Repository navigation
feat(rocke): enable head-dim-256 bf16 flash prefill on gfx942 + gfx950 #9129
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
Merged
Merged
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
1c4e5d8
feat(rocke/attention): deterministic LDS-budget resolver for 2D prefill
AviralGoelAMD 3738343
feat(rocke/attention): mirror LDS-budget resolver in the C++ engine +…
AviralGoelAMD 4fe8ddc
Merge remote-tracking branch 'origin/develop' into users/avirgoel/ck/…
AviralGoelAMD 53824f8
style(rocke/attention): black-format merged _tiled_spec_from_problem
AviralGoelAMD a7c8fb2
fix(rocke/attention): recognize fp8e4m3/bf8e5m2 in shared LDS kv-elem…
AviralGoelAMD 44d066d
refactor(rocke/attention): make LDS-budget resolver arch-agnostic
AviralGoelAMD File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 139 additions & 0 deletions
139
dnn-providers/hip-kernel-provider/rocke/library/tests/test_lds_budget_resolver.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| # Copyright (c) Advanced Micro Devices, Inc., or its affiliates. | ||
| # SPDX-License-Identifier: MIT | ||
| """Unit tests for the 2D-tiled LDS-budget resolver. | ||
|
|
||
| Pure codegen (no GPU, no subprocess). The resolver deterministically shrinks an | ||
| over-budget register-PV 2D spec until it fits the arch LDS cap, and is a strict | ||
| no-op for every already-fitting / non-register-PV / non-gfx950 config. | ||
|
|
||
| The load-bearing case: bf16 head_dim=256 long prefill on gfx950 overflows LDS at | ||
| the default (K double-buffered) geometry -- 204800 B > the 163840 B cap -- and the | ||
| resolver must single-buffer K so it compiles. The pre-fix codegen matrix only | ||
| covered D256 in fp16, so this path was previously untested. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import unittest | ||
| from dataclasses import replace | ||
| from unittest import mock | ||
|
|
||
| import kernels.common.attention_unified as au | ||
| from kernels import UnifiedAttentionProblem | ||
|
|
||
|
|
||
| def _d256_bf16_long_prefill(block_size: int = 64, max_seqlen: int = 4096): | ||
| # No sinks / sliding-window / softcap / alibi / qq_bias, so use_register_pv | ||
| # is eligible and the spec routes onto the register-PV 2D path. | ||
| return UnifiedAttentionProblem( | ||
| total_q=max_seqlen, | ||
| num_seqs=1, | ||
| num_query_heads=16, | ||
| num_kv_heads=2, | ||
| head_size=256, | ||
| block_size=block_size, | ||
| max_seqlen_q=max_seqlen, | ||
| max_seqlen_k=max_seqlen, | ||
| dtype="bf16", | ||
| ) | ||
|
|
||
|
|
||
| class TestLdsBudgetResolver(unittest.TestCase): | ||
| def test_d256_gfx950_bf16_prefill_shrinks_to_fit(self): | ||
| """D256 bf16 register-PV overflows at the default geometry; the resolver | ||
| must single-buffer K so the resolved spec fits the gfx950 160 KB cap.""" | ||
| with mock.patch.object(au, "_resolve_attention_arch", return_value="gfx950"): | ||
| spec = au._tiled_spec_from_problem(_d256_bf16_long_prefill()) | ||
| self.assertTrue(spec.use_register_pv) | ||
| # The cheapest lever (single-buffer K) was applied ... | ||
| self.assertTrue(spec.use_k_single_buffer) | ||
| # ... and the resolved geometry fits the arch cap. | ||
| self.assertLessEqual(au._lds_bytes_regpv(spec), au._lds_capacity_bytes()) | ||
|
|
||
| def test_pre_resolve_geometry_actually_overflows(self): | ||
| """Proves the resolver was necessary: the K-double-buffered geometry it | ||
| started from does exceed the cap (so the no-op guard didn't fire).""" | ||
| with mock.patch.object(au, "_resolve_attention_arch", return_value="gfx950"): | ||
| spec = au._tiled_spec_from_problem(_d256_bf16_long_prefill()) | ||
| k_double = replace(spec, use_k_single_buffer=False) | ||
| self.assertGreater(au._lds_bytes_regpv(k_double), au._lds_capacity_bytes()) | ||
|
|
||
| def test_resolver_is_noop_when_already_fitting(self): | ||
| """A spec that already fits is returned byte-identical (same object).""" | ||
| with mock.patch.object(au, "_resolve_attention_arch", return_value="gfx950"): | ||
| fitted = au._tiled_spec_from_problem(_d256_bf16_long_prefill()) | ||
| self.assertLessEqual(au._lds_bytes_regpv(fitted), au._lds_capacity_bytes()) | ||
| self.assertIs(au._resolve_lds_budget(fitted), fitted) | ||
|
|
||
| def test_resolver_is_noop_off_gfx950(self): | ||
| """On a non-gfx950 arch the resolver never engages -- it returns the spec | ||
| unchanged (the footprint model is only validated for gfx950).""" | ||
| with mock.patch.object(au, "_resolve_attention_arch", return_value="gfx950"): | ||
| spec = au._tiled_spec_from_problem(_d256_bf16_long_prefill()) | ||
| with mock.patch.object(au, "_resolve_attention_arch", return_value="gfx942"): | ||
| self.assertIs(au._resolve_lds_budget(spec), spec) | ||
|
|
||
| def test_regpv_footprint_uses_shared_helper(self): | ||
| """W: the register-PV footprint is the shared ``_tiled_2d_lds_bytes`` model | ||
| parameterised for the register-PV layout (Q/P^T dropped) -- a single source | ||
| of truth, so it cannot drift from the gfx942 admission gate.""" | ||
| with mock.patch.object(au, "_resolve_attention_arch", return_value="gfx950"): | ||
| spec = au._tiled_spec_from_problem(_d256_bf16_long_prefill()) | ||
| block_m = spec.num_warps * spec.block_m_per_warp | ||
| expected = au._tiled_2d_lds_bytes( | ||
| tile_size=spec.tile_size, | ||
| head_size=spec.head_size, | ||
| block_m=block_m, | ||
| kv_elem_bytes=au._kv_lds_elem_bytes(spec), | ||
| k_slots=1 if spec.use_k_single_buffer else 2, | ||
| v_slots=2 if spec.use_v_double_buffer else 1, | ||
| ) | ||
| self.assertEqual(au._lds_bytes_regpv(spec), expected) | ||
|
|
||
| def test_shared_helper_models_both_layouts(self): | ||
| """W: one helper serves both consumers -- the conservative gfx942 gate | ||
| layout (Q_lds/P_lds staged) and the exact register-PV layout (dropped).""" | ||
| common = dict(tile_size=128, head_size=256, block_m=16, kv_elem_bytes=2) | ||
| conservative = au._tiled_2d_lds_bytes( | ||
| k_slots=2, v_slots=1, include_q_lds=True, include_p_lds=True, **common | ||
| ) | ||
| regpv = au._tiled_2d_lds_bytes(k_slots=2, v_slots=1, **common) | ||
| # Dropping the P_lds term makes the register-PV footprint the smaller of | ||
| # the two; the conservative gate estimate never under-counts it. | ||
| self.assertGreater(conservative, regpv) | ||
| self.assertEqual(regpv, 204800) # matches the comgr-verified D256 number | ||
|
|
||
| def test_ldsfix_levers_report_reasons(self): | ||
| """S: the shrink levers return ``(None, reason)`` when they cannot apply, | ||
| so the resolver surfaces *why* rather than swallowing it.""" | ||
| with mock.patch.object(au, "_resolve_attention_arch", return_value="gfx950"): | ||
| spec = au._tiled_spec_from_problem(_d256_bf16_long_prefill()) | ||
| # single-K applies (returns a spec, no reason) ... | ||
| cand, why = au._ldsfix_single_k(replace(spec, use_k_single_buffer=False)) | ||
| self.assertIsNotNone(cand) | ||
| self.assertIsNone(why) | ||
| # ... but is a no-op once K is already single-buffered (reason given). | ||
| cand, why = au._ldsfix_single_k(replace(spec, use_k_single_buffer=True)) | ||
| self.assertIsNone(cand) | ||
| self.assertIn("single-buffered", why) | ||
| # tile64 is a no-op at T<=64. | ||
| cand, why = au._ldsfix_tile64(replace(spec, tile_size=64)) | ||
| self.assertIsNone(cand) | ||
| self.assertIn("64", why) | ||
|
|
||
| def test_infeasible_budget_raises_with_lever_diagnostics(self): | ||
| """S: when no lever fits, the resolver raises with each attempted lever's | ||
| result -- a diagnostic, not a cryptic downstream comgr abort.""" | ||
| with mock.patch.object(au, "_resolve_attention_arch", return_value="gfx950"): | ||
| spec = au._tiled_spec_from_problem(_d256_bf16_long_prefill()) | ||
| # Force an impossibly small cap so even single-K + T=64 overflows. | ||
| with mock.patch.object(au, "_lds_capacity_bytes", return_value=1024): | ||
| with self.assertRaises(RuntimeError) as ctx: | ||
| au._resolve_lds_budget(replace(spec, use_k_single_buffer=False)) | ||
| msg = str(ctx.exception) | ||
| self.assertIn("single-K", msg) | ||
| self.assertIn("T=64", msg) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.