Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions benchmarks/rocm/bench_block_sparse_attention.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ def _sweep(kinds, dry_run_iters: int, repeat_iters: int, nb: int, seed: int) ->
"num_kv_heads": num_kv,
"density": density,
"num_blocks": nb,
# Layout and inputs are both seed-derived, so two
# seeds at one density time differently; without
# this the rows are indistinguishable.
"seed": seed,
}
try:
if kind == "variable":
Expand Down Expand Up @@ -402,6 +406,10 @@ def main() -> None:
"block size would shrink the sparse problem below the dense baseline."
)

# --seed reached only the block mask; q/k/v came from the global RNG,
# so the seed recorded per row did not pin --accuracy's inputs.
torch.manual_seed(args.seed)

for key, value in _provenance().items():
print(f"# {key}: {value}")

Expand Down
6 changes: 5 additions & 1 deletion benchmarks/rocm/testlist_rocm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,16 @@
--routine BatchPrefillWithRaggedKVCacheWrapper --backends fa2 auto --batch_size 16 --s_qo 1024 --s_kv 1024 --num_qo_heads 64 --num_kv_heads 8 --head_dim_qk 128 --head_dim_vo 128 --causal --q_dtype bfloat16 --kv_dtype bfloat16 --refcheck --generate_repro_command --case_tag "Llama-3.1-70B"
--routine BatchPrefillWithRaggedKVCacheWrapper --backends fa2 auto --batch_size 4 --s_qo 4096 --s_kv 4096 --num_qo_heads 64 --num_kv_heads 8 --head_dim_qk 128 --head_dim_vo 128 --causal --q_dtype bfloat16 --kv_dtype bfloat16 --refcheck --generate_repro_command --case_tag "Llama-3.1-70B"

## Norm -- bf16 and fp16
## Norm -- bf16
# The backend column reads "cuda": these routines take no backend argument, so
# each op runs at its own default, which for norm and rope is native. For the
# native-vs-AITER comparison run rocm/bench_norm.py with no flag; --aa there is
# native against itself, i.e. the noise floor a ratio has to clear.
#
# fp16 is deliberately absent: vec_size is gcd(16/sizeof(T), d) and both
# 2-byte types give the same one, so fp16 would re-run this path for
# numerical noise alone.
#
# hidden_size 111 is in the sweep on purpose: the native kernel's vec_size is
# gcd(16/sizeof(T), d), so an ill-aligned d goes fully scalar. A powers-of-two
# sweep never sees it.
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/routines/rocm/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
# norm
"rmsnorm": ("rmsnorm", _NATIVE),
"fused_add_rmsnorm": ("fused_add_rmsnorm", _NATIVE),
"gemma_rmsnorm": ("layernorm", _NATIVE),
"gemma_fused_add_rmsnorm": ("layernorm", _NATIVE),
"gemma_rmsnorm": ("gemma_rmsnorm", _NATIVE),
"gemma_fused_add_rmsnorm": ("gemma_rmsnorm", _NATIVE),
# rope. apply_rope_with_cos_sin_cache is absent: the routine builds
# cos_sin_cache in --input_dtype, but the op requires float32 and
# --input_dtype offers none, so it fails on CUDA too.
Expand Down
40 changes: 40 additions & 0 deletions tests/rocm/test_arch_caps.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,46 @@ def test_every_op_the_library_asks_for_is_declared(self):
f"{sorted(used - declared)}"
)

def test_benchmark_registry_ops_are_declared(self):
"""The benchmark registry names capability ops; check the targets exist.

Parsed rather than imported: this lane has no torch, and support.py
reaches flashinfer. It is also why the regex scan above cannot cover it
-- support.py passes `op` as a variable, so the strings live in a dict
literal and never appear at a call site.

`layernorm` sat here until arch_caps stopped declaring it; git merged
both changes cleanly and two Gemma routines silently stopped producing
rows. tests/rocm/test_benchmark_harness.py catches it too, but runs on
no CI lane.
"""
import ast

root = pathlib.Path(__file__).resolve().parents[2]
src = root / "benchmarks" / "routines" / "rocm" / "support.py"
tree = ast.parse(src.read_text())
referenced = set()
for node in ast.walk(tree):
if not isinstance(node, ast.Assign):
continue
if not any(
isinstance(t, ast.Name) and t.id == "_ROCM_ROUTINE_TO_CAP_OP"
for t in node.targets
):
continue
for value in node.value.values:
op = value.elts[0]
assert isinstance(op, ast.Constant), f"non-literal cap op: {op!r}"
referenced.add(op.value)

assert referenced, "no capability ops parsed; the registry moved or renamed"
assert "rmsnorm" in referenced, "parse found no known op; the shape changed"
declared = {c.op for c in arch_caps.CAPABILITIES if c.backend == "hip"}
assert referenced <= declared, (
"benchmark registry names capability ops with no hip row: "
f"{sorted(referenced - declared)}"
)

def test_known_bad_rows_explain_themselves(self):
"""A gate with no detail is unactionable for whoever hits it."""
for cap in arch_caps.CAPABILITIES:
Expand Down
Loading