1- import time
1+ """benchmark.py
2+
3+ This module provides the Benchmark class for evaluating pipeline performance
4+ based on configurable parameters and stream counts.
5+ """
26from typing import List , Dict , Tuple
7+ import math
38import logging
49from utils import run_pipeline_and_extract_metrics
5- import math
610
711logging .basicConfig (level = logging .INFO )
812
913
1014class Benchmark :
15+
16+ """Benchmarking class for pipeline evaluation."""
17+
1118 def __init__ (
1219 self ,
1320 video_path : str ,
@@ -16,101 +23,105 @@ def __init__(
1623 rate : int ,
1724 parameters : Dict [str , str ],
1825 constants : Dict [str , str ],
19- elements : List [tuple [str , str , str ]] = [] ,
26+ elements : List [tuple [str , str , str ]] = None ,
2027 ):
2128 self .video_path = video_path
2229 self .pipeline_cls = pipeline_cls
2330 self .fps_floor = fps_floor
2431 self .rate = rate
2532 self .parameters = parameters
2633 self .constants = constants
27- self .elements = elements
34+ self .elements = elements if elements is not None else []
2835 self .best_result = None
2936 self .results = []
3037
3138 self .logger = logging .getLogger ("Benchmark" )
3239
40+ def _run_pipeline_and_extract_metrics (
41+ self ,
42+ pipeline_cls ,
43+ constants : Dict [str , str ],
44+ parameters : Dict [str , str ],
45+ channels : Tuple [int , int ],
46+ elements : List [tuple [str , str , str ]],
47+ ) -> List [Dict [str , float ]]:
48+ """Run the pipeline and extract metrics."""
49+ return run_pipeline_and_extract_metrics (
50+ pipeline_cls ,
51+ constants = constants ,
52+ parameters = parameters ,
53+ channels = channels ,
54+ elements = elements ,
55+ )
56+
3357 def run (self ) -> Tuple [int , int , int , float ]:
34- start_time = time .time ()
35- streams = 1
36- last_good_config = (0 , 0 , 0 , 0.0 )
58+ """Run the benchmark and return the best configuration."""
59+ n_streams = 1
60+ increments = 1
61+ incrementing = True
62+ best_config = (0 , 0 , 0 , 0.0 )
3763
38- # Phase 1: Exponential Expansion
3964 while True :
40- if time .time () - start_time > 300 :
41- self .logger .info ("Time limit reached during exponential phase" )
42- break
43-
44- ai_streams = math .ceil (streams * (self .rate / 100 ))
45- non_ai_streams = streams - ai_streams
46- results = run_pipeline_and_extract_metrics (
47- self .pipeline_cls ,
48- constants = self .constants ,
49- parameters = self .parameters ,
50- channels = (non_ai_streams , ai_streams ),
51- elements = self .elements ,
52- )
53- result = results [0 ]
65+ ai_streams = math .ceil (n_streams * (self .rate / 100 ))
66+ non_ai_streams = n_streams - ai_streams
5467
5568 try :
56- raw_fps_value = result ["per_stream_fps" ]
57- per_stream_fps = float (raw_fps_value )
58- if per_stream_fps >= self .fps_floor :
59- last_good_config = (
60- result ["num_streams" ],
61- ai_streams ,
62- non_ai_streams ,
63- per_stream_fps ,
64- )
65- streams *= 2
66- else :
67- failed_streams = streams
68- break
69- except (ValueError , TypeError ):
70- self .logger .info (
71- "Invalid FPS value, skipping this result:" , per_stream_fps
72- )
73- failed_streams = streams
74- break
75-
76- # Phase 2: Binary Search
77- low = last_good_config [0 ] + 1
78- high = failed_streams - 1
79- best_config = last_good_config
80-
81- while low <= high :
82- if time .time () - start_time > 300 :
83- self .logger .info ("Time limit reached during Binary phase." )
84- break
85- mid = (low + high ) // 2
86- ai_streams = math .ceil (mid * (self .rate / 100 ))
87- non_ai_streams = mid - ai_streams
88-
89- results = run_pipeline_and_extract_metrics (
90- self .pipeline_cls ,
91- constants = self .constants ,
92- parameters = self .parameters ,
93- channels = (non_ai_streams , ai_streams ),
94- elements = self .elements ,
95- )
96-
97- if not results :
98- self .logger .info (
99- "No results returned from run_pipeline_and_extract_metrics"
69+ results = self ._run_pipeline_and_extract_metrics (
70+ self .pipeline_cls ,
71+ constants = self .constants ,
72+ parameters = self .parameters ,
73+ channels = (non_ai_streams , ai_streams ),
74+ elements = self .elements ,
10075 )
101- break
76+ except StopIteration :
77+ return (0 , 0 , 0 , 0.0 )
10278
79+ if not results or results [0 ] is None or not isinstance (results [0 ], dict ):
80+ return (0 , 0 , 0 , 0.0 )
81+ if results [0 ].get ("exit_code" ) != 0 :
82+ return (0 , 0 , 0 , 0.0 )
83+
10384 result = results [0 ]
85+ try :
86+ total_fps = float (result ["total_fps" ])
87+ per_stream_fps = total_fps / n_streams if n_streams > 0 else 0.0
88+ except (ValueError , TypeError , ZeroDivisionError ):
89+ return (0 , 0 , 0 , 0.0 )
90+ if total_fps == 0 or math .isnan (per_stream_fps ):
91+ return (0 ,0 ,0 ,0.0 )
92+
93+ self .logger .info (
94+ "n_streams=%d, total_fps=%f, per_stream_fps=%f, increments=%d, incrementing=%s" ,
95+ n_streams , total_fps , per_stream_fps , increments , incrementing
96+ )
10497
105- per_stream_fps = float (result ["per_stream_fps" ])
106- if (
107- isinstance (per_stream_fps , (int , float ))
108- and per_stream_fps >= self .fps_floor
109- ):
110- if result ["num_streams" ] > best_config [0 ]:
111- best_config = (mid , ai_streams , non_ai_streams , per_stream_fps )
112- low = mid + 1
98+ if incrementing :
99+ if per_stream_fps >= self .fps_floor :
100+ increments = int (per_stream_fps / self .fps_floor )
101+ self .logger .info (
102+ "n_streams=%d, total_fps=%f, per_stream_fps=%f, increments=%d, incrementing=%s" ,
103+ n_streams , total_fps , per_stream_fps , increments , incrementing
104+ )
105+ if increments <= 1 :
106+ increments = 5
107+ else :
108+ incrementing = False
109+ increments = - 1
113110 else :
114- high = mid - 1
111+ if per_stream_fps >= self .fps_floor :
112+ best_config = (n_streams , ai_streams , non_ai_streams , per_stream_fps )
113+ break # Success
114+ else :
115+ if n_streams <= 1 :
116+ self .logger .info ("Failed to find a valid configuration." )
117+ break # Fail
118+
119+ n_streams += increments
120+ if n_streams <= 0 :
121+ n_streams = 1 # Prevent N from going below 1
115122
116- return best_config
123+ return (
124+ best_config
125+ if best_config [0 ] > 0
126+ else (n_streams , ai_streams , non_ai_streams , per_stream_fps )
127+ )
0 commit comments