[rocprofiler-compute] Threshold Based Clamping in Analyze Stage (#2565)

* add threshold clamping function + parse in parser.py (with I/O)

* implemented hybrid threshold solution

* update changelog

* removed absolute threshold hybrid approach; restored relative threshold + warn

* edited warning msg, threshold -> 1%

* update changelog

* added 2 test cases

* ran master workflow yaml config files

* added to FAQ

* Revert "ran master workflow yaml config files"

This reverts commit 75a670e14d6f1619ebbda0ec218755ccbe0d22b1.

* update FAQ

* update config hashes

* Broke down long functions into Class with sub-functions

* ruff format

* addressed comments
This commit is contained in:
jamessiddeley-amd
2026-01-23 00:54:54 -05:00
committed by GitHub
parent 7af2dba741
commit 69281bbcf4
17 changed files with 631 additions and 277 deletions
@@ -7844,3 +7844,130 @@ def test_validate_roofline_csv_invalid_inconsistent_columns():
assert is_valid is False
assert "Inconsistent row length" in error_msg
assert "row 2" in error_msg
# =============================================================================
# TESTS FOR NOISE_CLAMP: Multi-Pass Profiling Variance Handling
# =============================================================================
@pytest.mark.noise_clamp
def test_noise_clamp_clamping_behavior():
"""Core behavior: positives unchanged, negatives clamped to 0."""
import numpy as np
from utils.parser import to_noise_clamp
# Scalar: positive unchanged
assert to_noise_clamp(1000.0, 100000.0) == 1000.0
# Scalar: negative clamped
assert to_noise_clamp(-100.0, 1000000.0) == 0.0
# Series: mixed values
diff = pd.Series([100.0, -50.0, 200.0, -100.0])
ref = pd.Series([1e6, 1e6, 1e6, 1e6])
result = to_noise_clamp(diff, ref)
pd.testing.assert_series_equal(result, pd.Series([100.0, 0.0, 200.0, 0.0]))
# NumPy array
diff_np = np.array([100.0, -50.0])
ref_np = np.array([1e6, 1e6])
result_np = to_noise_clamp(diff_np, ref_np)
np.testing.assert_array_equal(result_np, np.array([100.0, 0.0]))
@pytest.mark.noise_clamp
def test_noise_clamp_zero_reference():
"""Edge case: zero reference should not cause division by zero."""
from utils.parser import to_noise_clamp
assert to_noise_clamp(-100.0, 0.0) == 0.0
result = to_noise_clamp(pd.Series([-100.0]), pd.Series([0.0]))
assert result.iloc[0] == 0.0
@pytest.mark.noise_clamp
def test_noise_clamp_warning_above_threshold():
"""Warning recorded when relative error >= 1%."""
from utils.parser import (
clear_noise_clamp_warnings,
get_noise_clamp_warnings,
to_noise_clamp,
)
clear_noise_clamp_warnings()
# 2% error (above 1% threshold) - should record
to_noise_clamp(pd.Series([-20000.0]), pd.Series([1000000.0]))
stats = get_noise_clamp_warnings()
assert stats["count"] == 1
assert stats["max_rel"] >= 0.01
@pytest.mark.noise_clamp
def test_noise_clamp_no_warning_below_threshold():
"""No warning when relative error < 1%."""
from utils.parser import (
clear_noise_clamp_warnings,
get_noise_clamp_warnings,
to_noise_clamp,
)
clear_noise_clamp_warnings()
# 0.5% error (below 1% threshold) - still clamped, no warning
result = to_noise_clamp(pd.Series([-5000.0]), pd.Series([1000000.0]))
assert result.iloc[0] == 0.0
assert get_noise_clamp_warnings()["count"] == 0
@pytest.mark.noise_clamp
def test_noise_clamp_empty_input():
"""Empty inputs should return empty without error."""
from utils.parser import to_noise_clamp
result = to_noise_clamp(pd.Series([], dtype=float), pd.Series([], dtype=float))
assert len(result) == 0
@pytest.mark.noise_clamp
def test_noise_clamp_threshold_boundary():
"""Exactly 1% error should trigger warning (>= not >)."""
from utils.parser import (
clear_noise_clamp_warnings,
get_noise_clamp_warnings,
to_noise_clamp,
)
clear_noise_clamp_warnings()
# Exactly 1% error: -10000 / 1000000 = 0.01
to_noise_clamp(pd.Series([-10000.0]), pd.Series([1000000.0]))
assert get_noise_clamp_warnings()["count"] == 1
@pytest.mark.noise_clamp
def test_noise_clamper_instance_isolation():
"""Separate NoiseClamper instances should have independent state."""
import numpy as np
from utils.parser import NoiseClamper
clamper1 = NoiseClamper()
clamper2 = NoiseClamper()
clamper1.clamp(pd.Series([-20000.0]), pd.Series([1000000.0]))
assert clamper1.get_stats()["count"] == 1
assert clamper2.get_stats()["count"] == 0
clamper1.clear()
assert clamper1.get_stats()["count"] == 0
assert clamper2.get_stats()["count"] == 0
clamper1.clamp(np.array([-50000.0]), np.array([1000000.0]))
clamper2.clamp(np.array([-30000.0, -40000.0]), np.array([1000000.0, 1000000.0]))
assert clamper1.get_stats()["count"] == 1
assert clamper2.get_stats()["count"] == 2