@@ -118,10 +118,19 @@ fn line_comparison_series_data<'a>(
118118 formatter : & dyn ValueFormatter ,
119119 all_curves : & [ & ( & ' a BenchmarkId , Vec < f64 > ) ] ,
120120) -> ( & ' static str , Vec < ( Option < & ' a String > , Vec < f64 > , Vec < f64 > ) > ) {
121+ // CODSPEED FIX: Changed from fold(::std::f64::NAN, f64::max) to fold(0.0, f64::max)
122+ // and added filter(|x| x.is_finite()) to prevent NaN propagation.
123+ //
124+ // Previously, starting the fold with NaN meant that f64::max would always return NaN
125+ // (since NaN comparisons always return false). This caused plotters to panic with
126+ // "assertion failed: !(range.0.is_nan() || range.1.is_nan())" when trying to create
127+ // plot ranges. This was particularly problematic with very small measurements (picoseconds)
128+ // and high outlier percentages, which could occur intermittently in CI.
121129 let max = all_curves
122130 . iter ( )
123131 . map ( |& ( _, data) | Sample :: new ( data) . mean ( ) )
124- . fold ( :: std:: f64:: NAN , f64:: max) ;
132+ . filter ( |x| x. is_finite ( ) )
133+ . fold ( 0.0 , f64:: max) ;
125134
126135 let mut dummy = [ 1.0 ] ;
127136 let unit = formatter. scale_values ( max, & mut dummy) ;
@@ -166,8 +175,14 @@ pub fn violin(
166175 . map ( |& & ( id, ref sample) | {
167176 let ( x, mut y) = kde:: sweep ( Sample :: new ( sample) , KDE_POINTS , None ) ;
168177 let y_max = Sample :: new ( & y) . max ( ) ;
169- for y in y. iter_mut ( ) {
170- * y /= y_max;
178+ // CODSPEED FIX: Only normalize if y_max is finite and non-zero to prevent
179+ // division by zero or NaN propagation. Previously, dividing by y_max without
180+ // checking could produce Inf or NaN values if y_max was 0.0 or non-finite,
181+ // which would cause plotters to panic when creating plot ranges.
182+ if y_max. is_finite ( ) && y_max > 0.0 {
183+ for y in y. iter_mut ( ) {
184+ * y /= y_max;
185+ }
171186 }
172187
173188 ( id. as_title ( ) , x, y)
0 commit comments