alamb opened a new issue, #24658:
URL: https://github.com/apache/datafusion/issues/24658

   ### Is your feature request related to a problem or challenge?
   
   - Part of #19241
   
   Every `StaticFilter` in the `in_list` module handles dictionary-encoded 
needles itself, via the `handle_dictionary!` macro (or a hand-rolled copy in 
`ArrayStaticFilter`). The macro expands `downcast_dictionary_array!` — a match 
over all eight dictionary key types — into every monomorphized `contains` 
implementation.
   
   Measured with `cargo llvm-lines -p datafusion-physical-expr --lib` (as of 
#24102): the module has 44 monomorphized `contains` copies averaging ~744 LLVM 
IR lines each, of which ~85% is the repeated dictionary match. In total the 
duplicated dictionary handling accounts for ~27,000 IR lines, about 4% of the 
whole crate. Each new specialized filter in the #19241 series stamps out more 
copies.
   
   ### Describe the solution you'd like
   
   Implement dictionary handling once as a wrapper filter that delegates to any 
inner `StaticFilter` for the values, and wrap it once in 
`instantiate_static_filter`:
   
   ```rust
   /// Unwraps dictionary-encoded needles for the wrapped filter.
   struct DictionaryFilter {
       /// The haystack's (non-dictionary) value type.
       values_type: DataType,
       inner: StaticFilterRef,
   }
   
   impl StaticFilter for DictionaryFilter {
       fn null_count(&self) -> usize {
           self.inner.null_count()
       }
   
       fn contains(&self, v: &dyn Array, negated: bool) -> Result<BooleanArray> 
{
           downcast_dictionary_array! {
               v => {
                   if v.values().data_type() == &self.values_type {
                       let values_contains =
                           self.inner.contains(v.values().as_ref(), negated)?;
                       let result = take(&values_contains, v.keys(), None)?;
                       return Ok(downcast_array(result.as_ref()));
                   }
               }
               _ => {}
           }
           self.inner.contains(v, negated)
       }
   }
   ```
   
   The `handle_dictionary!` macro and `ArrayStaticFilter`'s inline dictionary 
block are then deleted. The type guard (only unwrap when the dictionary's value 
type matches the haystack type) preserves `ArrayStaticFilter`'s existing 
fallback for mismatched dictionaries; Arrow forbids nested dictionaries, so one 
unwrap level suffices.
   
   A prototype of this change compiles the dictionary machinery exactly once: 
the 44 `contains` copies drop from ~744 to ~116 IR lines on average, shrinking 
`datafusion-physical-expr` by ~27,450 IR lines (~4.1%) with all in_list unit 
tests and sqllogictests passing. Runtime cost is one virtual call plus a 
`DataType` match per batch; per-row hot loops are unchanged.
   
   Future representation adapters (e.g. #24088) then get dictionary support for 
free instead of embedding the match per instantiation.
   
   ### Describe alternatives you've considered
   
   Outlining the macro body into a shared helper function called from each 
filter. This achieves most of the same code-size win but keeps dictionary logic 
as a per-filter obligation rather than composing it once at construction.
   
   ### Additional context
   
   Discussed while reviewing #24102; the prototype applies on top of that PR.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to