99import glob
1010import json
1111import os
12+ import shutil
1213import time
1314from collections .abc import Mapping
1415from datetime import datetime , timezone
1920
2021from fastvideo import VideoGenerator
2122from fastvideo .logger import init_logger
23+ from fastvideo .tests .performance .worker_log_capture import (
24+ WorkerLogCapture ,
25+ format_worker_log_tail ,
26+ )
2227from fastvideo .tests .performance .identity import (
2328 benchmark_identity_from_config ,
2429 build_recipe_from_benchmark_config ,
@@ -292,6 +297,28 @@ def _write_results(results):
292297 logger .info ("Performance results written to %s" , filepath )
293298
294299
300+ _WORKER_LOG_DIRNAME = "worker_logs"
301+
302+
303+ def _worker_log_dir ():
304+ script_dir = os .path .dirname (os .path .abspath (__file__ ))
305+ return os .path .join (script_dir , "results" , _WORKER_LOG_DIRNAME )
306+
307+
308+ def _copy_log_to_perf_reports (log_path ):
309+ """Best-effort copy so the log survives as a Buildkite artifact even when
310+ compare_baseline.py never runs (PR hard-regression path). No-op locally."""
311+ reports_dir = os .environ .get ("PERF_REPORTS_DIR" , "/root/data/perf_reports" )
312+ try :
313+ dest_dir = os .path .join (reports_dir , _WORKER_LOG_DIRNAME )
314+ os .makedirs (dest_dir , exist_ok = True )
315+ for path in (f"{ log_path } .1" , log_path ):
316+ if os .path .isfile (path ):
317+ shutil .copy2 (path , dest_dir )
318+ except OSError as exc :
319+ logger .warning ("Could not copy worker log to %s: %s" , reports_dir , exc )
320+
321+
295322def _backend_name (value ) -> str :
296323 if hasattr (value , "name" ):
297324 return str (value .name )
@@ -461,6 +488,7 @@ def _build_result_record(
461488 runtime_identity : Mapping [str , Any ],
462489 device_name : str ,
463490 timestamp : str | None = None ,
491+ worker_log_path : str | None = None ,
464492) -> dict [str , Any ]:
465493 if not times or not peak_memories :
466494 raise ValueError ("Cannot build a performance result record without measurement runs" )
@@ -498,6 +526,8 @@ def _build_result_record(
498526 "individual_peak_memories_mb" : [round (m , 1 ) for m in peak_memories ],
499527 "thresholds" :
500528 dict (thresholds ),
529+ "worker_log_path" :
530+ worker_log_path ,
501531 "regression_thresholds" :
502532 cfg .get ("regression_thresholds" , {}),
503533 "commit" :
@@ -548,10 +578,19 @@ def _run_benchmark(cfg):
548578 os .makedirs (output_dir , exist_ok = True )
549579 gen_kwargs ["output_path" ] = output_dir
550580
581+ capture = WorkerLogCapture (
582+ _worker_log_dir (),
583+ cfg ["benchmark_id" ],
584+ datetime .now (timezone .utc ).strftime ("%Y%m%dT%H%M%SZ" ),
585+ )
551586 generator = None
552587 try :
588+ # log_queue only goes to from_pretrained: workers keep the handler for
589+ # their lifetime, covering model load + warmups + measured runs.
590+ # Passing it to generate_video would detach it after the first call.
553591 generator = VideoGenerator .from_pretrained (
554592 model_path = model_info ["model_path" ],
593+ log_queue = capture .log_queue ,
555594 ** init_kwargs ,
556595 )
557596 runtime_identity = _runtime_identity_from_generator (generator )
@@ -575,7 +614,11 @@ def _run_benchmark(cfg):
575614 peak_memories .append (peak_mb )
576615 all_component_times .append (component_times )
577616 finally :
617+ # Shutdown stops workers producing; close() then drains the queue so
618+ # the log file is complete before any assertion reads it back.
578619 _shutdown_executor (generator )
620+ capture .close ()
621+ _copy_log_to_perf_reports (capture .log_path )
579622
580623 avg_time = sum (times ) / len (times )
581624 max_peak_memory = max (peak_memories )
@@ -594,12 +637,23 @@ def _run_benchmark(cfg):
594637 prompt = prompt ,
595638 runtime_identity = runtime_identity ,
596639 device_name = device_name ,
640+ worker_log_path = capture .log_path ,
597641 )
598642
599643 logger .info ("Performance results: avg_time=%.2fs, "
600644 "max_peak_memory=%.0fMB" , avg_time , max_peak_memory )
601645 _write_results (results )
602646
647+ try :
648+ _assert_thresholds (results , thresholds , device_name )
649+ except AssertionError :
650+ print (format_worker_log_tail (cfg ["benchmark_id" ], capture .log_path ), flush = True )
651+ raise
652+
653+
654+ def _assert_thresholds (results , thresholds , device_name ):
655+ avg_time = results ["avg_generation_time_s" ]
656+ max_peak_memory = results ["max_peak_memory_mb" ]
603657 max_time = thresholds ["max_generation_time_s" ]
604658 max_mem = thresholds ["max_peak_memory_mb" ]
605659
0 commit comments