@@ -283,40 +283,12 @@ def from_request_times(
283283 )
284284
285285 # First convert to timing events based on type
286- events : list [tuple [float , float ]] = []
287-
288- if distribution_type == "concurrency" :
289- # For concurrency, each request adds to concurrency at start
290- # and subtracts at end
291- for (start , end ), weight in zip (requests , weights , strict = False ):
292- events .append ((start , weight ))
293- events .append ((end , - 1 * weight ))
294- elif distribution_type == "rate" :
295- # For rate, each request is added at the end time only
296- global_start = min (start for start , _ in requests ) if requests else 0.0
297- events .append ((global_start , 0.0 ))
298- for (_ , end ), weight in zip (requests , weights , strict = False ):
299- events .append ((end , weight ))
300- else :
301- raise ValueError (
302- f"Invalid distribution_type '{ distribution_type } '. "
303- "Must be 'concurrency' or 'rate'."
304- )
305-
306- # Combine any events within epsilon of each other for stability
307- sorted_events = sorted (events , key = lambda event : event [0 ])
308- flattened_events : list [tuple [float , float ]] = (
309- [sorted_events .pop (0 )] if sorted_events else []
286+ events = DistributionSummary ._convert_to_timing_events (
287+ requests , distribution_type , weights
310288 )
311- last_time = flattened_events [0 ][0 ] if flattened_events else 0.0
312289
313- for time , val in sorted_events :
314- if abs (time - last_time ) <= epsilon :
315- last_val = flattened_events [- 1 ][1 ]
316- flattened_events [- 1 ] = (last_time , last_val + val )
317- else :
318- last_time = time
319- flattened_events .append ((time , val ))
290+ # Combine any events within epsilon of each other for stability
291+ flattened_events = DistributionSummary ._combine_events (events , epsilon )
320292
321293 # Convert events to value distribution function
322294 distribution : dict [float , float ] = defaultdict (float )
@@ -357,6 +329,53 @@ def from_request_times(
357329 include_cdf = include_cdf ,
358330 )
359331
332+ @staticmethod
333+ def _convert_to_timing_events (
334+ requests : list [tuple [float , float ]],
335+ distribution_type : Literal ["concurrency" , "rate" ],
336+ weights : list [float ],
337+ ) -> list [tuple [float , float ]]:
338+ events : list [tuple [float , float ]] = []
339+
340+ if distribution_type == "concurrency" :
341+ # For concurrency, each request adds to concurrency at start
342+ # and subtracts at end
343+ for (start , end ), weight in zip (requests , weights , strict = False ):
344+ events .append ((start , weight ))
345+ events .append ((end , - 1 * weight ))
346+ elif distribution_type == "rate" :
347+ # For rate, each request is added at the end time only
348+ global_start = min (start for start , _ in requests ) if requests else 0.0
349+ events .append ((global_start , 0.0 ))
350+ for (_ , end ), weight in zip (requests , weights , strict = False ):
351+ events .append ((end , weight ))
352+ else :
353+ raise ValueError (
354+ f"Invalid distribution_type '{ distribution_type } '. "
355+ "Must be 'concurrency' or 'rate'."
356+ )
357+ return events
358+
359+ @staticmethod
360+ def _combine_events (
361+ events : list [tuple [float , float ]],
362+ epsilon : float ,
363+ ) -> list [tuple [float , float ]]:
364+ sorted_events = sorted (events , key = lambda event : event [0 ])
365+ flattened_events : list [tuple [float , float ]] = (
366+ [sorted_events .pop (0 )] if sorted_events else []
367+ )
368+ last_time = flattened_events [0 ][0 ] if flattened_events else 0.0
369+
370+ for time , val in sorted_events :
371+ if abs (time - last_time ) <= epsilon :
372+ last_val = flattened_events [- 1 ][1 ]
373+ flattened_events [- 1 ] = (last_time , last_val + val )
374+ else :
375+ last_time = time
376+ flattened_events .append ((time , val ))
377+ return flattened_events
378+
360379 @staticmethod
361380 def from_iterable_request_times (
362381 requests : list [tuple [float , float ]],
0 commit comments