4242
4343@dataclass
4444class TimingRecord :
45+ task : str
46+ model : str
4547 entry_point : str
48+ device : str
4649 scenario : str
4750 run : int
4851 duration : float
@@ -60,8 +63,48 @@ def add(
6063 run : int ,
6164 duration : float ,
6265 output_dir : str = "" ,
66+ * ,
67+ task : str | None = None ,
68+ model : str | None = None ,
69+ device : str | None = None ,
6370 ) -> None :
64- self .records .append (TimingRecord (entry_point , scenario , run , duration , output_dir ))
71+ resolved_task , resolved_entry_point = _normalise_task_entrypoint (task , entry_point )
72+ self .records .append (
73+ TimingRecord (
74+ task = resolved_task ,
75+ model = model or scenario ,
76+ entry_point = resolved_entry_point ,
77+ device = device or _default_device_for_task (resolved_task ),
78+ scenario = scenario ,
79+ run = run ,
80+ duration = duration ,
81+ output_dir = output_dir ,
82+ )
83+ )
84+
85+
86+ def _normalise_task_entrypoint (task : str | None , entry_point : str ) -> tuple [str , str ]:
87+ if task :
88+ return task , entry_point
89+ if entry_point .startswith ("Scale " ):
90+ return "Scale Observation" , entry_point .removeprefix ("Scale " )
91+ if entry_point .startswith ("Stereo " ):
92+ return "Stereo Depth" , entry_point .removeprefix ("Stereo " )
93+ return "Unknown" , entry_point
94+
95+
96+ def _default_device_for_task (task : str ) -> str :
97+ if task == "Scale Observation" :
98+ return "CPU"
99+ try :
100+ import torch
101+ except ImportError :
102+ return "CPU"
103+ if torch .cuda .is_available ():
104+ return "CUDA"
105+ if hasattr (torch .backends , "mps" ) and torch .backends .mps .is_available ():
106+ return "MPS"
107+ return "CPU"
65108
66109
67110# Module-level singleton — pytest_terminal_summary reads from it after the session
@@ -315,12 +358,14 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config): # noqa: ARG0
315358 if not records :
316359 return
317360
318- W = 82
319- EP = 10 # entry-point col width
320- SC = 16 # scenario col width
321- RN = 4 # run col width
322- DU = 10 # duration col width
323- ST = 20 # status col width
361+ W = 120
362+ TASK = 28
363+ MODEL = 16
364+ EP = 10
365+ DEV = 7
366+ COLD = 17
367+ WARM = 17
368+ FPS = 13
324369
325370 def _write (line : str = "" ) -> None :
326371 terminalreporter .write_line (line )
@@ -329,52 +374,67 @@ def _thick() -> None:
329374 _write ("━" * W )
330375
331376 def _thin () -> None :
332- _write (f" { '─' * EP } ─┼─{ '─' * SC } ─┼─{ '─' * RN } ─┼─{ '─' * (DU )} ─┼─{ '─' * ST } " )
377+ _write (
378+ f" { '─' * TASK } ─┼─{ '─' * MODEL } ─┼─{ '─' * EP } ─┼─{ '─' * DEV } ─┼─"
379+ f"{ '─' * COLD } ─┼─{ '─' * WARM } ─┼─{ '─' * FPS } "
380+ )
333381
334- def _row (ep = "" , sc = "" , run = "" , dur = "" , status = "" ) -> None :
335- _write (f" { ep :<{EP }} │ { sc :<{SC }} │ { run :^{RN }} │ { dur :>{DU }} │ { status } " )
382+ def _row (task = "" , model = "" , entrypoint = "" , device = "" , cold = "" , warm = "" , fps = "" ) -> None :
383+ _write (
384+ f" { task :<{TASK }} │ { model :<{MODEL }} │ { entrypoint :<{EP }} │ { device :<{DEV }} │ "
385+ f"{ cold :>{COLD }} │ { warm :>{WARM }} │ { fps :>{FPS }} "
386+ )
336387
337388 _write ()
338389 _thick ()
339390 _write (f" { 'VIZION3D · INTEGRATION INFERENCE TIMING REPORT' :^{W - 4 }} " )
340391 _thick ()
341392 _write ()
342- _row ("Entry Point" , "Scenario" , "Run" , "Duration" , "Status" )
393+ _row (
394+ "Task" ,
395+ "Model" ,
396+ "Entrypoint" ,
397+ "Device" ,
398+ "Cold run duration" ,
399+ "Warm run duration" ,
400+ "Estimated FPS" ,
401+ )
343402 _thin ()
344403
345404 def sort_key (r ):
346- return (r .entry_point , r .scenario , r .run )
405+ return (r .task , r . model , r . entry_point , r .device , r .run )
347406
348407 def group_key (r ):
349- return (r .entry_point , r .scenario )
408+ return (r .task , r . model , r . entry_point , r .device )
350409
351410 first_loads : list [float ] = []
352411 warm_times : list [float ] = []
353412
354413 sorted_records = sorted (records , key = sort_key )
355414 groups = [(k , list (v )) for k , v in groupby (sorted_records , key = group_key )]
356415
357- for g_idx , ((ep , sc ), recs ) in enumerate (groups ):
416+ for g_idx , ((task , model , entrypoint , device ), recs ) in enumerate (groups ):
417+ recs = sorted (recs , key = lambda r : r .run )
418+ cold_runs = [r .duration for r in recs if r .run == 1 ]
419+ warm_runs = [r .duration for r in recs if r .run != 1 ]
420+ cold = min (cold_runs ) if cold_runs else recs [0 ].duration
421+ warm = sum (warm_runs ) / len (warm_runs ) if warm_runs else 0.0
422+ fps = 1.0 / warm if warm > 0 else 0.0
423+ first_loads .append (cold )
424+ if warm_runs :
425+ warm_times .extend (warm_runs )
426+
358427 if g_idx > 0 :
359428 _thin ()
360-
361- recs = sorted (recs , key = lambda r : r .run )
362- first_dur = recs [0 ].duration
363-
364- for i , rec in enumerate (recs ):
365- ep_label = ep if i == 0 else ""
366- sc_label = sc if i == 0 else ""
367- dur_str = f"{ rec .duration :7.3f} s"
368-
369- if rec .run == 1 :
370- status = "◉ COLD LOAD"
371- first_loads .append (rec .duration )
372- else :
373- pct = (1.0 - rec .duration / first_dur ) * 100.0
374- status = f"⚡ { pct :4.1f} % faster"
375- warm_times .append (rec .duration )
376-
377- _row (ep_label , sc_label , str (rec .run ), dur_str , status )
429+ _row (
430+ task ,
431+ model ,
432+ entrypoint ,
433+ device ,
434+ f"{ cold :.3f} s" ,
435+ f"{ warm :.3f} s" if warm_runs else "n/a" ,
436+ f"{ fps :.2f} " if warm_runs else "n/a" ,
437+ )
378438
379439 _write ()
380440 _thick ()
0 commit comments