1717
1818mod data_utils;
1919
20- use arrow:: datatypes:: { DataType , IntervalUnit , TimeUnit } ;
20+ use arrow:: array:: { Array , AsArray } ;
21+ use arrow:: datatypes:: {
22+ DataType , Int32Type , Int64Type , IntervalUnit , TimeUnit , TimestampNanosecondType ,
23+ } ;
2124use criterion:: { Criterion , criterion_group, criterion_main} ;
2225use data_utils:: create_table_provider;
2326use datafusion:: datasource:: MemTable ;
@@ -440,23 +443,47 @@ fn fully_ordered(c: &mut Criterion) {
440443 let mut group = c. benchmark_group ( "fully_ordered" ) ;
441444 for ( name, key_expr, key_type, run_length) in cases {
442445 let ctx = rt
443- . block_on ( fully_ordered_context ( key_expr, & key_type, run_length) )
446+ . block_on ( fully_ordered_context (
447+ key_expr, & key_type, run_length, false ,
448+ ) )
444449 . unwrap ( ) ;
445450 group. bench_function ( name, |b| {
446451 b. iter ( || query ( ctx. clone ( ) , & rt, FULLY_ORDERED_QUERY ) )
447452 } ) ;
448453 }
454+ // Add accumulator work for one representative timestamp case without
455+ // multiplying every key type and run length by the aggregate expression.
456+ let ctx = rt
457+ . block_on ( fully_ordered_context (
458+ "arrow_cast(signed_key, 'Timestamp(Nanosecond)')" ,
459+ & Timestamp ( Nanosecond , None ) ,
460+ 31 ,
461+ true ,
462+ ) )
463+ . unwrap ( ) ;
464+ group. bench_function ( "timestamp_ns_sum" , |b| {
465+ b. iter ( || query ( ctx. clone ( ) , & rt, FULLY_ORDERED_SUM_QUERY ) )
466+ } ) ;
449467 group. finish ( ) ;
450468}
451469
452470const FULLY_ORDERED_QUERY : & str = "SELECT k1, k2 FROM ordered_input GROUP BY k1, k2" ;
471+ const FULLY_ORDERED_SUM_QUERY : & str =
472+ "SELECT k1, k2, SUM(v) FROM ordered_input GROUP BY k1, k2" ;
453473
454474async fn fully_ordered_context (
455475 key_expr : & str ,
456476 key_type : & DataType ,
457477 run_length : usize ,
478+ with_sum : bool ,
458479) -> Result < Arc < Mutex < SessionContext > > > {
459480 const ROWS : usize = 2_097_152 ;
481+ // Use source row numbers as nonconstant values for the aggregate case.
482+ let ( value_projection, input_value, sql) = if with_sum {
483+ ( ", v" , ", value AS v" , FULLY_ORDERED_SUM_QUERY )
484+ } else {
485+ ( "" , "" , FULLY_ORDERED_QUERY )
486+ } ;
460487 let config = SessionConfig :: new ( )
461488 . with_target_partitions ( 1 )
462489 . with_batch_size ( 8192 )
@@ -467,10 +494,10 @@ async fn fully_ordered_context(
467494 let input = ctx
468495 . sql ( & format ! (
469496 "SELECT CAST(group_id / 32 AS INTEGER) AS k1, \
470- CASE WHEN key_value = 0 THEN NULL ELSE {key_expr} END AS k2 \
497+ CASE WHEN key_value = 0 THEN NULL ELSE {key_expr} END AS k2{value_projection} \
471498 FROM (SELECT value / {run_length} AS group_id, \
472499 value / {run_length} % 32 AS key_value, \
473- value / {run_length} % 32 - 16 AS signed_key \
500+ value / {run_length} % 32 - 16 AS signed_key{input_value} \
474501 FROM range({ROWS})) \
475502 ORDER BY k1 ASC NULLS FIRST, k2 ASC NULLS FIRST"
476503 ) )
@@ -492,11 +519,7 @@ async fn fully_ordered_context(
492519 ] ] ) ;
493520 ctx. register_table ( "ordered_input" , Arc :: new ( table) ) ?;
494521
495- let plan = ctx
496- . sql ( FULLY_ORDERED_QUERY )
497- . await ?
498- . create_physical_plan ( )
499- . await ?;
522+ let plan = ctx. sql ( sql) . await ?. create_physical_plan ( ) . await ?;
500523 let description = displayable ( plan. as_ref ( ) ) . indent ( true ) . to_string ( ) ;
501524 assert ! (
502525 description. contains( "group_clustering_mode=Full" ) ,
@@ -511,6 +534,31 @@ async fn fully_ordered_context(
511534 result. iter( ) . map( |batch| batch. num_rows( ) ) . sum:: <usize >( ) ,
512535 ROWS . div_ceil( run_length)
513536 ) ;
537+ if with_sum {
538+ let rows = i64:: try_from ( ROWS ) . unwrap ( ) ;
539+ let run_length = i64:: try_from ( run_length) . unwrap ( ) ;
540+ for batch in & result {
541+ let k1 = batch. column ( 0 ) . as_primitive :: < Int32Type > ( ) ;
542+ let k2 = batch. column ( 1 ) . as_primitive :: < TimestampNanosecondType > ( ) ;
543+ let sums = batch. column ( 2 ) . as_primitive :: < Int64Type > ( ) ;
544+ assert_eq ! ( sums. null_count( ) , 0 ) ;
545+ for row in 0 ..batch. num_rows ( ) {
546+ // Each input group covers a contiguous range of source row
547+ // numbers. Recover it from the keys, independently of output
548+ // order, and check the arithmetic-series sum, including the
549+ // final short group and groups with a NULL timestamp key.
550+ let key_offset = if k2. is_null ( row) {
551+ 0
552+ } else {
553+ k2. value ( row) + 16
554+ } ;
555+ let group_id = i64:: from ( k1. value ( row) ) * 32 + key_offset;
556+ let start = group_id * run_length;
557+ let end = ( start + run_length) . min ( rows) ;
558+ assert_eq ! ( sums. value( row) , ( start + end - 1 ) * ( end - start) / 2 ) ;
559+ }
560+ }
561+ }
514562 Ok ( Arc :: new ( Mutex :: new ( ctx) ) )
515563}
516564
0 commit comments