Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /eic-opticks/tests/check_simg4ox_timing.py was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 #!/usr/bin/env python3
0002 """Validate simg4ox per-event CPU/GPU timing and run metadata."""
0003 
0004 from __future__ import annotations
0005 
0006 import argparse
0007 import csv
0008 import json
0009 import math
0010 from pathlib import Path
0011 
0012 
0013 ORDERED_COLUMNS = (
0014     "start_offset_s",
0015     "cpu_pre_start_time_offset_s",
0016     "cpu_pre_end_time_offset_s",
0017     "gpu_submit_time_offset_s",
0018     "gpu_wait_start_time_offset_s",
0019     "gpu_start_time_offset_s",
0020     "gpu_end_time_offset_s",
0021     "gpu_wait_end_time_offset_s",
0022     "cpu_post_start_time_offset_s",
0023     "cpu_post_end_time_offset_s",
0024     "end_offset_s",
0025 )
0026 
0027 NONNEGATIVE_COLUMNS = (
0028     "runtime_s",
0029     "cpu_pre_runtime_s",
0030     "cpu_post_runtime_s",
0031     "cpu_runtime_s",
0032     "gpu_queue_delay_s",
0033     "gpu_runtime_s",
0034     "gpu_wait_runtime_s",
0035     "process_cpu_s",
0036     "thread_cpu_s",
0037 )
0038 
0039 
0040 def main() -> None:
0041     parser = argparse.ArgumentParser(description=__doc__)
0042     parser.add_argument("timing_csv", type=Path)
0043     parser.add_argument("--events", type=int, required=True)
0044     parser.add_argument("--particle")
0045     parser.add_argument("--require-photons", action="store_true")
0046     args = parser.parse_args()
0047 
0048     with args.timing_csv.open(newline="") as stream:
0049         rows = list(csv.DictReader(stream))
0050 
0051     if len(rows) != args.events:
0052         raise AssertionError(f"Expected {args.events} timing rows, found {len(rows)}")
0053     if [int(row["event_id"]) for row in rows] != list(range(args.events)):
0054         raise AssertionError("Event IDs are not consecutive and zero-based")
0055 
0056     for row in rows:
0057         event_id = int(row["event_id"])
0058         ordered = [float(row[column]) for column in ORDERED_COLUMNS]
0059         if not all(math.isfinite(value) for value in ordered):
0060             raise AssertionError(f"Event {event_id} contains non-finite timestamps")
0061         if any(later + 1e-9 < earlier for earlier, later in zip(ordered, ordered[1:])):
0062             raise AssertionError(f"Event {event_id} stage timestamps are not monotonic: {ordered}")
0063         for column in NONNEGATIVE_COLUMNS:
0064             value = float(row[column])
0065             if not math.isfinite(value) or value < 0:
0066                 raise AssertionError(f"Event {event_id} has invalid {column}={value}")
0067         expected_wait = float(row["gpu_queue_delay_s"]) + float(row["gpu_runtime_s"])
0068         if not math.isclose(
0069             float(row["gpu_wait_runtime_s"]),
0070             expected_wait,
0071             rel_tol=1e-6,
0072             abs_tol=1e-9,
0073         ):
0074             raise AssertionError(f"Event {event_id} blocking GPU wait does not span queue plus execution")
0075         if args.particle and row["primary_particle"] != args.particle:
0076             raise AssertionError(f"Event {event_id} particle is {row['primary_particle']}, expected {args.particle}")
0077         if args.require_photons and int(row["num_photons"]) <= 0:
0078             raise AssertionError(f"Event {event_id} did not produce GPU optical photons")
0079 
0080     manifest_path = args.timing_csv.with_suffix(".manifest.json")
0081     manifest = json.loads(manifest_path.read_text())
0082     if manifest["schema_version"] != 2:
0083         raise AssertionError("Manifest does not use the shared timeline schema version")
0084     if manifest["dispatch_mode"] != "blocking":
0085         raise AssertionError("simg4ox timing manifest must identify blocking dispatch")
0086     if manifest["event_count"] != args.events:
0087         raise AssertionError("Manifest event_count does not match timing CSV")
0088     if args.particle and manifest["primary_particle"] != args.particle:
0089         raise AssertionError("Manifest particle does not match the requested source")
0090 
0091     print(f"TIMING_EVENTS={len(rows)}")
0092     print(f"TIMING_CPU_BUSY_S={sum(float(row['cpu_runtime_s']) for row in rows):.9f}")
0093     print(f"TIMING_GPU_BUSY_S={sum(float(row['gpu_runtime_s']) for row in rows):.9f}")
0094 
0095 
0096 if __name__ == "__main__":
0097     main()