11use crate :: { DATA_PATH , RESULTS_DIR } ;
22use datafusion:: common:: utils:: get_available_parallelism;
33use datafusion:: common:: { Result , internal_datafusion_err} ;
4+ use datafusion_distributed_benchmarks:: stats:: median;
45use serde:: ser:: SerializeSeq ;
56use serde:: { Deserialize , Deserializer , Serialize , Serializer } ;
67use std:: fs;
@@ -13,6 +14,12 @@ use std::time::{Duration, SystemTime};
1314pub struct QueryIter {
1415 pub row_count : usize ,
1516 pub n_tasks : usize ,
17+ /// P50 q-error of the byte estimates made at dynamic stage boundaries.
18+ /// `None` when dynamic planning was disabled or no sampled boundary was present.
19+ pub stats_q_error_p50 : Option < f64 > ,
20+ /// P95 q-error of the byte estimates made at dynamic stage boundaries.
21+ /// `None` when dynamic planning was disabled or no sampled boundary was present.
22+ pub stats_q_error_p95 : Option < f64 > ,
1623 #[ serde(
1724 serialize_with = "serialize_elapsed" ,
1825 deserialize_with = "deserialize_elapsed"
@@ -279,29 +286,67 @@ impl BenchResult {
279286pub fn print_comparison_total ( base : & [ BenchResult ] , new : & [ BenchResult ] ) {
280287 let mut total_prev: u128 = 0 ;
281288 let mut total_new: u128 = 0 ;
289+ let mut stats_q_error_p50_prev = vec ! [ ] ;
290+ let mut stats_q_error_p50_new = vec ! [ ] ;
291+ let mut stats_q_error_p95_prev = vec ! [ ] ;
292+ let mut stats_q_error_p95_new = vec ! [ ] ;
282293 for query in new {
283294 let Some ( prev) = base. iter ( ) . find ( |v| v. id == query. id ) else {
284295 continue ;
285296 } ;
286297 if let ( Some ( p) , Some ( n) ) = ( prev. representative_time ( ) , query. representative_time ( ) ) {
287298 total_prev += p;
288299 total_new += n;
300+ stats_q_error_p50_prev. extend (
301+ prev. iterations
302+ . iter ( )
303+ . filter_map ( |iteration| iteration. stats_q_error_p50 ) ,
304+ ) ;
305+ stats_q_error_p50_new. extend (
306+ query
307+ . iterations
308+ . iter ( )
309+ . filter_map ( |iteration| iteration. stats_q_error_p50 ) ,
310+ ) ;
311+ stats_q_error_p95_prev. extend (
312+ prev. iterations
313+ . iter ( )
314+ . filter_map ( |iteration| iteration. stats_q_error_p95 ) ,
315+ ) ;
316+ stats_q_error_p95_new. extend (
317+ query
318+ . iterations
319+ . iter ( )
320+ . filter_map ( |iteration| iteration. stats_q_error_p95 ) ,
321+ ) ;
289322 }
290323 }
291- if total_prev == 0 && total_new == 0 {
292- return ;
324+
325+ if total_prev != 0 || total_new != 0 {
326+ let ( f, tag, emoji) = if total_new < total_prev {
327+ let f = total_prev as f64 / total_new as f64 ;
328+ ( f, "faster" , if f > 1.2 { "✅" } else { "✔" } )
329+ } else {
330+ let f = total_new as f64 / total_prev. max ( 1 ) as f64 ;
331+ ( f, "slower" , if f > 1.2 { "❌" } else { "✖" } )
332+ } ;
333+ println ! (
334+ "{:>8}: prev={total_prev} ms, new={total_new} ms, diff={f:.2} {tag} {emoji}" ,
335+ "TOTAL"
336+ ) ;
337+ }
338+
339+ print_q_error_comparison ( "QERR P50" , stats_q_error_p50_prev, stats_q_error_p50_new) ;
340+ print_q_error_comparison ( "QERR P95" , stats_q_error_p95_prev, stats_q_error_p95_new) ;
341+ }
342+
343+ fn print_q_error_comparison ( label : & str , prev : Vec < f64 > , new : Vec < f64 > ) {
344+ match ( median ( prev) , median ( new) ) {
345+ ( Some ( prev) , Some ( new) ) => println ! ( "{label:>8}: prev={prev:.2}x, new={new:.2}x" ) ,
346+ ( Some ( prev) , None ) => println ! ( "{label:>8}: prev={prev:.2}x, new=n/a" ) ,
347+ ( None , Some ( new) ) => println ! ( "{label:>8}: prev=n/a, new={new:.2}x" ) ,
348+ ( None , None ) => { }
293349 }
294- let ( f, tag, emoji) = if total_new < total_prev {
295- let f = total_prev as f64 / total_new as f64 ;
296- ( f, "faster" , if f > 1.2 { "✅" } else { "✔" } )
297- } else {
298- let f = total_new as f64 / total_prev. max ( 1 ) as f64 ;
299- ( f, "slower" , if f > 1.2 { "❌" } else { "✖" } )
300- } ;
301- println ! (
302- "{:>8}: prev={total_prev} ms, new={total_new} ms, diff={f:.2} {tag} {emoji}" ,
303- "TOTAL"
304- ) ;
305350}
306351
307352fn serialize_bench_results < S : Serializer > (
0 commit comments