33//
44//! Decode-path microbenchmarks for the OnPair Vortex array.
55//!
6- //! * `decompress_into ` — the upstream `onpair::decompress_into ` decoder hot
6+ //! * `decode_into ` — the upstream `onpair::decode_into ` decoder hot
77//! loop, fed by pre-materialised [`DecodeInputs`]. Measures the inner loop
88//! only (no child `execute`, no allocation).
99//! * `canonicalize_to_varbinview` — the full Vortex
@@ -28,7 +28,7 @@ use std::mem::MaybeUninit;
2828use std:: sync:: LazyLock ;
2929
3030use divan:: Bencher ;
31- use onpair:: Parts ;
31+ use onpair:: CompactDictionaryView ;
3232use vortex_array:: ArrayRef ;
3333use vortex_array:: ExecutionCtx ;
3434use vortex_array:: IntoArray ;
@@ -38,43 +38,45 @@ use vortex_array::arrays::PrimitiveArray;
3838use vortex_array:: arrays:: VarBinArray ;
3939use vortex_array:: arrays:: VarBinViewArray ;
4040use vortex_array:: arrays:: filter:: FilterKernel ;
41+ use vortex_array:: buffer:: BufferHandle ;
4142use vortex_array:: builtins:: ArrayBuiltins ;
4243use vortex_array:: dtype:: DType ;
4344use vortex_array:: dtype:: NativePType ;
4445use vortex_array:: dtype:: Nullability ;
4546use vortex_buffer:: Buffer ;
46- use vortex_buffer:: ByteBuffer ;
4747use vortex_mask:: Mask ;
4848use vortex_onpair:: DEFAULT_DICT12_CONFIG ;
4949use vortex_onpair:: OnPair ;
5050use vortex_onpair:: OnPairArray ;
5151use vortex_onpair:: OnPairArraySlotsExt ;
5252
5353/// Host-resident decode inputs, materialised once so the decode-loop benchmark
54- /// measures only `onpair::decompress_into ` (not child `execute`/allocation).
54+ /// measures only `onpair::decode_into ` (not child `execute`/allocation).
5555struct DecodeInputs {
56- dict_bytes : ByteBuffer ,
56+ dict_bytes : BufferHandle ,
5757 dict_offsets : Buffer < u32 > ,
5858 codes : Buffer < u16 > ,
59- bits : u32 ,
6059}
6160
6261impl DecodeInputs {
63- fn as_parts ( & self ) -> Parts < ' _ > {
64- Parts {
65- dict_bytes : self . dict_bytes . as_slice ( ) ,
66- dict_offsets : self . dict_offsets . as_slice ( ) ,
67- bits : self . bits ,
68- codes : self . codes . as_slice ( ) ,
62+ fn dict ( & self ) -> CompactDictionaryView < ' _ > {
63+ // SAFETY: `materialise` validates this borrowed dictionary once after
64+ // widening offsets. The benchmark then keeps both buffers immutable.
65+ unsafe {
66+ CompactDictionaryView :: new_unchecked (
67+ self . dict_bytes . as_host ( ) . as_slice ( ) ,
68+ self . dict_offsets . as_slice ( ) ,
69+ )
6970 }
7071 }
7172
72- fn decompressed_len ( & self ) -> usize {
73- onpair:: decompressed_len ( self . as_parts ( ) )
73+ fn decoded_len ( & self ) -> usize {
74+ onpair:: decoded_len ( self . codes . as_slice ( ) , self . dict ( ) )
7475 }
7576
76- fn decompress_into ( & self , out : & mut [ MaybeUninit < u8 > ] ) -> usize {
77- onpair:: decompress_into ( self . as_parts ( ) , out)
77+ fn decode_into ( & self , out : & mut [ MaybeUninit < u8 > ] ) -> usize {
78+ // SAFETY: callers allocate `decoded_len + DECODE_PADDING` bytes.
79+ unsafe { onpair:: decode_into ( self . codes . as_slice ( ) , self . dict ( ) , out) }
7880 }
7981}
8082use vortex_onpair:: onpair_compress;
@@ -175,13 +177,16 @@ fn widen<T: NativePType>(arr: &ArrayRef, ctx: &mut ExecutionCtx) -> Buffer<T> {
175177
176178fn materialise ( arr : & OnPairArray , ctx : & mut ExecutionCtx ) -> ( DecodeInputs , usize ) {
177179 let view = arr. as_view ( ) ;
180+ let dict_offsets = widen :: < u32 > ( view. dict_offsets ( ) , ctx) ;
181+ let dict_bytes = view. dict_bytes_handle ( ) . clone ( ) ;
182+ CompactDictionaryView :: validate ( dict_bytes. as_host ( ) . as_slice ( ) , dict_offsets. as_slice ( ) )
183+ . expect ( "valid OnPair dictionary" ) ;
178184 let inputs = DecodeInputs {
179- dict_bytes : view . dict_bytes ( ) . clone ( ) ,
180- dict_offsets : widen :: < u32 > ( view . dict_offsets ( ) , ctx ) ,
185+ dict_bytes,
186+ dict_offsets,
181187 codes : widen :: < u16 > ( view. codes ( ) , ctx) ,
182- bits : view. bits ( ) ,
183188 } ;
184- let total = inputs. decompressed_len ( ) ;
189+ let total = inputs. decoded_len ( ) ;
185190 ( inputs, total)
186191}
187192
@@ -194,16 +199,16 @@ const CASES: &[(Shape, usize)] = &[
194199] ;
195200
196201/// Raw decode loop time, excluding child `execute` and the output allocation.
197- /// Hits `onpair::decompress_into ` directly.
202+ /// Hits `onpair::decode_into ` directly.
198203#[ divan:: bench( args = CASES ) ]
199- fn decompress_into_bench ( bencher : Bencher , case : ( Shape , usize ) ) {
204+ fn decode_into_bench ( bencher : Bencher , case : ( Shape , usize ) ) {
200205 let mut ctx = SESSION . create_execution_ctx ( ) ;
201206 let ( shape, n) = case;
202207 let arr = compress ( n, shape, & mut ctx) ;
203208 let ( inputs, total) = materialise ( & arr, & mut ctx) ;
204209 bencher. bench_local ( || {
205- let mut out: Vec < u8 > = Vec :: with_capacity ( total) ;
206- let written = inputs. decompress_into ( out. spare_capacity_mut ( ) ) ;
210+ let mut out: Vec < u8 > = Vec :: with_capacity ( total + onpair :: DECODE_PADDING ) ;
211+ let written = inputs. decode_into ( out. spare_capacity_mut ( ) ) ;
207212 unsafe { out. set_len ( written) } ;
208213 divan:: black_box ( out) ;
209214 } ) ;
0 commit comments