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 ,
@@ -304,6 +309,28 @@ def _write_results(results):
304309 logger .info ("Performance results written to %s" , filepath )
305310
306311
312+ _WORKER_LOG_DIRNAME = "worker_logs"
313+
314+
315+ def _worker_log_dir ():
316+ script_dir = os .path .dirname (os .path .abspath (__file__ ))
317+ return os .path .join (script_dir , "results" , _WORKER_LOG_DIRNAME )
318+
319+
320+ def _copy_log_to_perf_reports (log_path ):
321+ """Best-effort copy so the log survives as a Buildkite artifact even when
322+ compare_baseline.py never runs (PR hard-regression path). No-op locally."""
323+ reports_dir = os .environ .get ("PERF_REPORTS_DIR" , "/root/data/perf_reports" )
324+ try :
325+ dest_dir = os .path .join (reports_dir , _WORKER_LOG_DIRNAME )
326+ os .makedirs (dest_dir , exist_ok = True )
327+ for path in (f"{ log_path } .1" , log_path ):
328+ if os .path .isfile (path ):
329+ shutil .copy2 (path , dest_dir )
330+ except OSError as exc :
331+ logger .warning ("Could not copy worker log to %s: %s" , reports_dir , exc )
332+
333+
307334def _backend_name (value ) -> str :
308335 if hasattr (value , "name" ):
309336 return str (value .name )
@@ -480,6 +507,7 @@ def _build_result_record(
480507 runtime_identity : Mapping [str , Any ],
481508 device_name : str ,
482509 timestamp : str | None = None ,
510+ worker_log_path : str | None = None ,
483511) -> dict [str , Any ]:
484512 if not times or not peak_memories :
485513 raise ValueError ("Cannot build a performance result record without measurement runs" )
@@ -512,6 +540,7 @@ def _build_result_record(
512540 "max_peak_memory_mb" : round (max_peak_memory , 1 ),
513541 "individual_peak_memories_mb" : [round (m , 1 ) for m in peak_memories ],
514542 "thresholds" : dict (thresholds ),
543+ "worker_log_path" : worker_log_path ,
515544 "regression_thresholds" : cfg .get ("regression_thresholds" , {}),
516545 "commit" : os .environ .get ("BUILDKITE_COMMIT" , "" ),
517546 ** _ci_provenance_fields (),
@@ -558,10 +587,19 @@ def _run_benchmark(cfg):
558587 os .makedirs (output_dir , exist_ok = True )
559588 gen_kwargs ["output_path" ] = output_dir
560589
590+ capture = WorkerLogCapture (
591+ _worker_log_dir (),
592+ cfg ["benchmark_id" ],
593+ datetime .now (timezone .utc ).strftime ("%Y%m%dT%H%M%SZ" ),
594+ )
561595 generator = None
562596 try :
597+ # log_queue only goes to from_pretrained: workers keep the handler for
598+ # their lifetime, covering model load + warmups + measured runs.
599+ # Passing it to generate_video would detach it after the first call.
563600 generator = VideoGenerator .from_pretrained (
564601 model_path = model_info ["model_path" ],
602+ log_queue = capture .log_queue ,
565603 ** init_kwargs ,
566604 )
567605 runtime_identity = _runtime_identity_from_generator (generator )
@@ -585,7 +623,11 @@ def _run_benchmark(cfg):
585623 peak_memories .append (peak_mb )
586624 all_component_times .append (component_times )
587625 finally :
626+ # Shutdown stops workers producing; close() then drains the queue so
627+ # the log file is complete before any assertion reads it back.
588628 _shutdown_executor (generator )
629+ capture .close ()
630+ _copy_log_to_perf_reports (capture .log_path )
589631
590632 avg_time = sum (times ) / len (times )
591633 max_peak_memory = max (peak_memories )
@@ -604,13 +646,24 @@ def _run_benchmark(cfg):
604646 prompt = prompt ,
605647 runtime_identity = runtime_identity ,
606648 device_name = device_name ,
649+ worker_log_path = capture .log_path ,
607650 )
608651
609652 logger .info (
610653 "Performance results: avg_time=%.2fs, "
611654 "max_peak_memory=%.0fMB" , avg_time , max_peak_memory )
612655 _write_results (results )
613656
657+ try :
658+ _assert_thresholds (results , thresholds , device_name )
659+ except AssertionError :
660+ print (format_worker_log_tail (cfg ["benchmark_id" ], capture .log_path ), flush = True )
661+ raise
662+
663+
664+ def _assert_thresholds (results , thresholds , device_name ):
665+ avg_time = results ["avg_generation_time_s" ]
666+ max_peak_memory = results ["max_peak_memory_mb" ]
614667 max_time = thresholds ["max_generation_time_s" ]
615668 max_mem = thresholds ["max_peak_memory_mb" ]
616669
0 commit comments