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
109 lines
3.9 KiB
Python
109 lines
3.9 KiB
Python
"""Tests for perfxpert.tools.counters."""
|
|
|
|
import pytest
|
|
|
|
from perfxpert.knowledge import load_yaml
|
|
from perfxpert.tools import counters
|
|
from perfxpert.tools._class import ToolClass
|
|
|
|
|
|
def _arch_with_limit_for(block: str, needed: int = 1) -> str:
|
|
arch_limits = load_yaml("pmc_limits")["gpu_arch_limits"]
|
|
return next(gfx_id for gfx_id, limits in arch_limits.items() if limits.get(block, 0) >= needed)
|
|
|
|
|
|
def test_lookup_info_known_counter():
|
|
gfx_id = _arch_with_limit_for("SQ")
|
|
info = counters.lookup_info("SQ_WAVES", gfx_id=gfx_id)
|
|
assert info["name"] == "SQ_WAVES"
|
|
assert info["block"] == "SQ"
|
|
assert info["block_limit"] == load_yaml("pmc_limits")["gpu_arch_limits"][gfx_id]["SQ"]
|
|
|
|
|
|
def test_lookup_info_unknown_raises():
|
|
with pytest.raises(KeyError):
|
|
counters.lookup_info("DEFINITELY_NOT_A_COUNTER")
|
|
|
|
|
|
def test_validate_splits_tcc_derived_into_own_passes():
|
|
"""FETCH_SIZE + WRITE_SIZE must each be in their own pass (anti-Sakana)."""
|
|
result = counters.validate_for_gpu(
|
|
["SQ_WAVES", "GRBM_COUNT", "FETCH_SIZE", "WRITE_SIZE"],
|
|
gpu_arch=_arch_with_limit_for("SQ"),
|
|
)
|
|
assert result["ok"]
|
|
# FETCH_SIZE and WRITE_SIZE must be in separate passes
|
|
fetch_passes = [p for p in result["fixed_passes"] if "FETCH_SIZE" in p]
|
|
write_passes = [p for p in result["fixed_passes"] if "WRITE_SIZE" in p]
|
|
assert len(fetch_passes) == 1
|
|
assert len(write_passes) == 1
|
|
assert fetch_passes[0] != write_passes[0]
|
|
|
|
|
|
def test_validate_multi_pass_returns_escalation_guidance():
|
|
"""Multi-pass plans must include concrete escalation guidance."""
|
|
result = counters.validate_for_gpu(
|
|
["SQ_WAVES", "GRBM_COUNT", "FETCH_SIZE", "WRITE_SIZE"],
|
|
gpu_arch=_arch_with_limit_for("SQ"),
|
|
)
|
|
|
|
escalation = result["escalation"]
|
|
assert escalation["required"] is True
|
|
assert escalation["pass_count"] == 3
|
|
assert escalation["pmc_groups_path"] == "pmc_groups.txt"
|
|
assert escalation["pmc_groups"] == [
|
|
"pmc: SQ_WAVES GRBM_COUNT",
|
|
"pmc: FETCH_SIZE",
|
|
"pmc: WRITE_SIZE",
|
|
]
|
|
commands = {cmd["tool"]: cmd["full_command"] for cmd in escalation["commands"]}
|
|
assert commands["rocprof-compute"] == "rocprof-compute profile -- ./app"
|
|
assert commands["rocprofv3"] == "rocprofv3 -i pmc_groups.txt -- ./app"
|
|
|
|
|
|
def test_validate_single_pass_does_not_emit_escalation():
|
|
"""Single-pass plans should remain simple and omit escalation."""
|
|
result = counters.validate_for_gpu(
|
|
["SQ_WAVES", "GRBM_COUNT"],
|
|
gpu_arch=_arch_with_limit_for("SQ"),
|
|
)
|
|
assert result["fixed_passes"] == [["SQ_WAVES", "GRBM_COUNT"]]
|
|
assert result["escalation"] is None
|
|
|
|
|
|
def test_validate_for_gpu_uses_arch_specific_regular_block_limit():
|
|
catalog = load_yaml("counter_catalog")
|
|
sq_counters = [entry["name"] for entry in catalog if entry["block"] == "SQ"]
|
|
gfx_id = _arch_with_limit_for("SQ", needed=len(sq_counters))
|
|
|
|
result = counters.validate_for_gpu(sq_counters, gpu_arch=gfx_id)
|
|
|
|
assert result["fixed_passes"] == [sq_counters]
|
|
assert result["escalation"] is None
|
|
|
|
|
|
def test_analysis_pmc_split_uses_same_arch_specific_limit():
|
|
from perfxpert.analysis.pmc import _split_pmc_into_passes
|
|
|
|
catalog = load_yaml("counter_catalog")
|
|
sq_counters = [entry["name"] for entry in catalog if entry["block"] == "SQ"]
|
|
gfx_id = _arch_with_limit_for("SQ", needed=len(sq_counters))
|
|
|
|
commands = _split_pmc_into_passes(
|
|
sq_counters,
|
|
base_flags=[],
|
|
base_args=[],
|
|
output_dir="./out",
|
|
output_prefix="profile",
|
|
description="Collect counters",
|
|
gpu_arch=gfx_id,
|
|
)
|
|
|
|
assert [arg["value"] for arg in commands[0]["args"] if arg["name"] == "--pmc"] == [" ".join(sq_counters)]
|
|
assert len(commands) == 1
|
|
|
|
|
|
def test_is_read_only_class():
|
|
assert counters.lookup_info.__tool_class__ == ToolClass.READ_ONLY
|
|
assert counters.validate_for_gpu.__tool_class__ == ToolClass.READ_ONLY
|