From 17e480f3d37bc35a61c66a7146f138488cd5a15f Mon Sep 17 00:00:00 2001 From: bogdan-petkovic Date: Fri, 17 Jul 2026 08:05:04 +0000 Subject: [PATCH] gate relDiff on absDiff for f32 attention CPU verify Signed-off-by: bogdan-petkovic --- .../performance/tests/test_tuningRunner.py | 30 ++++++++++++++++- mlir/utils/performance/tuningRunner.py | 32 +++++++++++++++++-- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/mlir/utils/performance/tests/test_tuningRunner.py b/mlir/utils/performance/tests/test_tuningRunner.py index de94136554a2..2cf441456b67 100644 --- a/mlir/utils/performance/tests/test_tuningRunner.py +++ b/mlir/utils/performance/tests/test_tuningRunner.py @@ -31,7 +31,8 @@ from tuningRunner import ( # noqa: E402 ConfigState, TuningState, TuningStateFile, TunedConfigsCache, Options, get_state_filepath, verify_mode_flags, format_error, get_config_class, get_git_commit_hash, NumaTopology, Operation, - NumaNodeLock, resolve_verify_mode, canonicalize_test_vector, DebugFileWriter, TuningResult) + NumaNodeLock, resolve_verify_mode, canonicalize_test_vector, DebugFileWriter, TuningResult, + attention_cpu_verify_flags, ATTENTION_F32_ABSDIFF_THRESHOLD) from perfRunner import ( # noqa: E402 GemmConfiguration, ConvConfiguration, AttentionConfiguration, ConvGemmConfiguration, GemmGemmConfiguration, PerfConfiguration, canonicalize_config) @@ -93,6 +94,33 @@ def test_invalid_raises(self): verify_mode_flags("invalid") +class TestAttentionCpuVerifyFlags: + """Tests for attention_cpu_verify_flags (f32 attention allclose-style relDiff gate).""" + + @staticmethod + def _cfg(cls, dtype): + # Bypass the heavy __init__ (arch-db lookups); the helper only needs the type + datatype. + cfg = object.__new__(cls) + cfg.datatype = dtype + return cfg + + def test_f32_attention_cpu_gets_absdiff_gate(self): + flags = attention_cpu_verify_flags(self._cfg(AttentionConfiguration, "f32"), "cpu") + assert flags == ["-absDiff_threshold", ATTENTION_F32_ABSDIFF_THRESHOLD] + + def test_non_f32_attention_no_gate(self): + # f16/bf16 disable relDiff in rocmlir-gen and i8 uses integer verification. + for dt in ("f16", "bf16", "i8"): + assert attention_cpu_verify_flags(self._cfg(AttentionConfiguration, dt), "cpu") == [] + + def test_non_attention_op_no_gate(self): + assert attention_cpu_verify_flags(self._cfg(GemmConfiguration, "f32"), "cpu") == [] + + def test_non_cpu_mode_no_gate(self): + for mode in ("gpu", "none"): + assert attention_cpu_verify_flags(self._cfg(AttentionConfiguration, "f32"), mode) == [] + + class TestFormatError: """Tests for format_error.""" diff --git a/mlir/utils/performance/tuningRunner.py b/mlir/utils/performance/tuningRunner.py index 76973f225af9..3d31d4fdbb26 100755 --- a/mlir/utils/performance/tuningRunner.py +++ b/mlir/utils/performance/tuningRunner.py @@ -1189,6 +1189,28 @@ def verify_mode_flags(verify_mode: str) -> str: raise ValueError(f"Unknown verification mode: {verify_mode}") +# f32 attention CPU-verification tolerance. +# +# The GPU attention kernel runs a flash / online-softmax reduction whose accumulation order +# differs from the sequential CPU reference. In f32 this diverges by up to ~1e-2 on a small +# fraction of output elements while RMS and average stay ~1e-3 or better, so the result is +# numerically sound but trips the strict relDiff_threshold. Gate relDiff on an absolute +# tolerance (numpy/torch allclose style, via -absDiff_threshold) so this benign accumulation +# noise does not false-fail tuning; RMS and absDiff still catch real regressions. rocmlir-gen +# already disables relDiff for f16/bf16 and i8 uses integer verification, so only f32 needs +# this. Same mechanism used by parameterSweeps.py and mlir/test/e2e/PrAttentionF32.toml. +ATTENTION_F32_ABSDIFF_THRESHOLD = "5e-2" + + +def attention_cpu_verify_flags(config: PerfConfiguration, verify_mode: str) -> List[str]: + """Extra rocmlir-gen flags for f32 attention CPU verification (allclose-style relDiff gate).""" + if verify_mode != "cpu": + return [] + if isinstance(config, AttentionConfiguration) and getattr(config, "datatype", "") == "f32": + return ["-absDiff_threshold", ATTENTION_F32_ABSDIFF_THRESHOLD] + return [] + + def kill_process(proc: Optional[subprocess.Popen]) -> None: """Terminate a subprocess and wait for cleanup.""" if proc is None: @@ -1269,8 +1291,14 @@ def verify_perfconfig(perfconfig: str, config: PerfConfiguration, paths: Paths, command_line_options = config.generate_mlir_driver_commandline(options.rocmlir_gen_flags, kernel_repeats=MLIR_N_REPEATS) - rocmlir_gen_command = [paths.mlir_paths.rocmlir_gen_path, '-print-verify-results=summary' - ] + verify_mode_flags(verify_mode).split() + command_line_options.split() + # Append op/dtype-specific verification tolerances unless the user already set them explicitly + # (user --rocmlir-gen-flags live in command_line_options and must win). + extra_verify_flags = attention_cpu_verify_flags(config, verify_mode) + if extra_verify_flags and '-absDiff_threshold' in command_line_options: + extra_verify_flags = [] + rocmlir_gen_command = ([paths.mlir_paths.rocmlir_gen_path, '-print-verify-results=summary'] + + verify_mode_flags(verify_mode).split() + extra_verify_flags + + command_line_options.split()) rocmlir_driver_command = [paths.mlir_paths.rocmlir_driver_path, '-c']