CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
138 lines
4.6 KiB
Python
138 lines
4.6 KiB
Python
"""Tests for perfxpert.tools.pragma (Phase 10 advanced specialist).
|
|
|
|
Covers the three READ_ONLY MCP tools:
|
|
* lookup_pragmas
|
|
* explain_pragma
|
|
* suggest_pragmas_for_kernel
|
|
|
|
Plus the Amdahl gate, Triton skip, YAML factor_sweep invariance.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from perfxpert.tools import pragma
|
|
from perfxpert.tools._class import ToolClass
|
|
|
|
|
|
# -- lookup_pragmas -------------------------------------------------------
|
|
|
|
def test_lookup_pragmas_gpu_only_returns_allowlist():
|
|
entries = pragma.lookup_pragmas(gpu_only=True)
|
|
assert len(entries) == 3, (
|
|
"Phase 10 spec: exactly 3 allowlisted GPU-applicable pragmas."
|
|
)
|
|
for e in entries:
|
|
assert e["gpu_applicable"] is True
|
|
assert e["allowlist"] is True
|
|
pragma_ids = {e["pragma"] for e in entries}
|
|
assert pragma_ids == {
|
|
"clang_loop_unroll_full",
|
|
"clang_loop_unroll_count",
|
|
"clang_loop_unroll_disable",
|
|
}
|
|
|
|
|
|
def test_lookup_pragmas_not_gpu_only_returns_all():
|
|
entries = pragma.lookup_pragmas(gpu_only=False)
|
|
assert len(entries) >= 10, (
|
|
"Spec: 3 allowlisted + 7 explicitly rejected = 10 entries total."
|
|
)
|
|
pragma_ids = {e["pragma"] for e in entries}
|
|
# Explicitly rejected entries the fence must *see* to refuse.
|
|
for rejected in (
|
|
"clang_loop_vectorize",
|
|
"clang_loop_vectorize_width",
|
|
"clang_loop_interleave",
|
|
"clang_loop_interleave_count",
|
|
"clang_loop_distribute",
|
|
"clang_loop_pipeline",
|
|
"clang_loop_vectorize_predicate",
|
|
):
|
|
assert rejected in pragma_ids, f"missing rejected entry: {rejected}"
|
|
|
|
|
|
# -- explain_pragma -------------------------------------------------------
|
|
|
|
def test_explain_pragma_known():
|
|
info = pragma.explain_pragma("clang_loop_unroll_count")
|
|
assert info["pragma"] == "clang_loop_unroll_count"
|
|
assert info["factor_sweep"] == [2, 4, 8]
|
|
assert info["gpu_applicable"] is True
|
|
assert info["allowlist"] is True
|
|
|
|
|
|
def test_explain_pragma_unknown_raises_KeyError():
|
|
with pytest.raises(KeyError):
|
|
pragma.explain_pragma("clang_loop_totally_made_up")
|
|
|
|
|
|
# -- suggest_pragmas_for_kernel (Amdahl gate + Triton skip) ---------------
|
|
|
|
def test_suggest_pragmas_for_kernel_honors_amdahl():
|
|
"""Kernel with <5% total time → empty list."""
|
|
signals = {"hotspot_pct": 2.0, "loop_trip_count": 16}
|
|
assert pragma.suggest_pragmas_for_kernel("fused_add", signals) == []
|
|
|
|
|
|
def test_suggest_pragmas_skips_triton_kernel():
|
|
"""Source path contains .triton/ → tool skips the kernel."""
|
|
signals = {
|
|
"hotspot_pct": 25.0,
|
|
"source_path": "/home/user/.triton/cache/fused_matmul.hip",
|
|
"loop_trip_count": 16,
|
|
}
|
|
assert pragma.suggest_pragmas_for_kernel("fused_matmul", signals) == []
|
|
|
|
|
|
# -- suggest_pragmas_for_kernel (rule firing) -----------------------------
|
|
|
|
def test_suggest_pragmas_unroll_full_when_literal_trip_count():
|
|
signals = {"loop_trip_count": 16, "hotspot_pct": 25}
|
|
out = pragma.suggest_pragmas_for_kernel("gemm_kernel", signals)
|
|
assert len(out) == 1
|
|
rec = out[0]
|
|
assert rec["pragma"] == "clang_loop_unroll_full"
|
|
assert rec["kernel_name"] == "gemm_kernel"
|
|
assert rec["triggered_by"] == "literal_trip_count_on_hotspot"
|
|
assert rec["factor_sweep"] == [] # full-unroll has no factor
|
|
|
|
|
|
def test_suggest_pragmas_unroll_count_when_valu_stalled():
|
|
signals = {
|
|
"valu_util_pct": 0.6,
|
|
"vgpr_per_thread": 48,
|
|
"loop_body_size": 12,
|
|
"hotspot_pct": 15,
|
|
}
|
|
out = pragma.suggest_pragmas_for_kernel("stencil", signals)
|
|
ids = {r["pragma"] for r in out}
|
|
assert "clang_loop_unroll_count" in ids
|
|
unroll_count_rec = [r for r in out if r["pragma"] == "clang_loop_unroll_count"][0]
|
|
assert unroll_count_rec["factor_sweep"] == [2, 4, 8], (
|
|
"YAML-locked factor sweep — the agent must NEVER invent other values."
|
|
)
|
|
|
|
|
|
def test_suggest_pragmas_unroll_disable_when_vgpr_pressure():
|
|
# VGPR at 80% of arch max + scratch > 0 fires the disable rule.
|
|
signals = {
|
|
"hotspot_pct": 20,
|
|
"vgpr_per_thread": 208, # 208 / 256 = 0.8125 >= 0.80
|
|
"arch_max_vgpr": 256,
|
|
"scratch_size": 64,
|
|
"waves_per_eu": 1,
|
|
}
|
|
out = pragma.suggest_pragmas_for_kernel("heavy_matmul", signals)
|
|
ids = {r["pragma"] for r in out}
|
|
assert "clang_loop_unroll_disable" in ids
|
|
|
|
|
|
# -- tool class enforcement -----------------------------------------------
|
|
|
|
def test_all_three_tools_are_read_only():
|
|
assert pragma.lookup_pragmas.__tool_class__ == ToolClass.READ_ONLY
|
|
assert pragma.explain_pragma.__tool_class__ == ToolClass.READ_ONLY
|
|
assert pragma.suggest_pragmas_for_kernel.__tool_class__ == ToolClass.READ_ONLY
|