Keep nvbench-compare bulk debug output executable

* Define nan and inf in generated --bulk-debug-python scripts so pprint output
for non-finite timing values remains valid Python code. Add a regression test
that executes the generated script and verifies nan/inf values round-trip.

* Sharpen bulk-cycle confirmation gating. Only suppress summary-clock
fallback when both reference and compare inputs provide paired, non-empty bulk
sample/frequency payloads. Missing or empty bulk files are treated as
unavailable evidence and still allow sm_clock_rate/mean fallback, while
malformed non-empty payloads continue to produce AMBG.

Add regression coverage for missing bulk files falling back to summary-cycle
confirmation.

These changes resolve automated review feedback
This commit is contained in:
Oleksandr Pavlyk
2026-06-25 11:29:34 -05:00
parent d58119d7c6
commit 4744d26d26
2 changed files with 86 additions and 6 deletions

View File

@@ -1026,6 +1026,10 @@ def format_bulk_debug_python(bulk_rows: list[dict[str, Any]]) -> str:
"# NVB-BULK-BEGIN\n"
"# Generated by nvbench-compare --bulk-debug-python.\n"
"import numpy as np\n\n"
"# pprint emits bare nan/inf tokens for non-finite floats.\n"
"# Define them so this generated script remains executable.\n"
'nan = float("nan")\n'
'inf = float("inf")\n\n'
f"bulk_rows = {pprint.pformat(bulk_rows, sort_dicts=False)}\n\n"
"def read_float32(filename, expected_count=None):\n"
" if filename is None:\n"
@@ -1427,8 +1431,28 @@ def get_bulk_time_and_cycles(timing):
return samples, samples * frequencies
def has_bulk_time_or_frequency_source(timing):
return timing.sample_source is not None or timing.frequency_source is not None
def has_material_bulk_source(source):
if source is None:
return False
if isinstance(source, Float32BinarySource):
if source.count <= 0:
return False
filename = resolve_binary_filename(source.json_dir, source.filename)
try:
return os.path.getsize(filename) > 0
except OSError:
return False
values = getattr(source, "values", None)
return values is not None and len(values) > 0
def has_material_bulk_cycle_sources(timing):
return has_material_bulk_source(timing.sample_source) and has_material_bulk_source(
timing.frequency_source
)
def scale_interval(interval, scale):
@@ -1475,10 +1499,13 @@ def confirm_clear_gap_with_clock_rate(
def confirm_clear_gap_with_bulk_cycles(status, ref_timing, cmp_timing, thresholds):
has_bulk_sources = has_bulk_time_or_frequency_source(
# Only suppress the summary-clock fallback when both inputs advertise paired,
# non-empty bulk payloads. Missing or empty files are treated as unavailable;
# malformed non-empty payloads become AMBG after the lazy read below.
has_bulk_cycle_sources = has_material_bulk_cycle_sources(
ref_timing
) or has_bulk_time_or_frequency_source(cmp_timing)
if not has_bulk_sources:
) and has_material_bulk_cycle_sources(cmp_timing)
if not has_bulk_cycle_sources:
return None
ref_bulk = get_bulk_time_and_cycles(ref_timing)