jayzhan211 commented on PR #24456:
URL: https://github.com/apache/datafusion/pull/24456#issuecomment-6384138773

   @Dandandan I ran this branch on JOB, TPC-DS SF1 and TPC-H SF10 to see where 
it stands. The enumeration works well. On the queries where I could hand-write 
a good join order, it recovers almost all of that gain:
   
   | Query | Enumeration off | Enumeration on | Hand-written order |
   |---|---|---|---|
   | JOB 17b/c/d/f | 4.5–6.8 s | 0.16–0.27 s | 0.16–0.25 s |
   | JOB 16a–d | 2.3–5.2 s | 0.13–0.36 s | 0.13–0.38 s |
   | TPC-DS q72 | 1.42 s | 0.06 s | 0.08 s |
   | TPC-H q18 | 0.67 s | 0.37 s | 0.41 s |
   
   TPC-DS and TPC-H have no real regressions against the commit this branch is 
based on. On JOB, though, 21 queries get more than 1.2x slower (19d 4.6x, 5b 
4.5x, 5a 3.7x, 2c 3.3x, 18a 2.1x). I found two causes, and neither is in the 
search itself.
   
   **1. The new `IN` list estimate in `FilterExec`.** `in_list_selectivity` 
divides the number of literals by `max_distinct_count`, which falls back to the 
row count when the column has no NDV. Most Parquet files don't carry NDV, so a 
string `IN` list is treated as if every value were unique. In 19d, `ci.note IN 
(4 values)` is estimated at 11 rows, but 815K rows match. Every join above it 
is then estimated at about 11 rows, so the search has nothing to go on. The 
same happens with an `OR` of equalities (18a). It also changes plans with 
enumeration turned off: 9a goes from 0.25 s to 1.7–2.7 s.
   
   Skipping the estimate when there is no real distinct count fixes it:
   
   ```diff
        let stats = column_statistics.get(column.index())?;
   +    // Without a real distinct count, `max_distinct_count` falls back to 
the row
   +    // count, which treats every value as unique; leave such a column to 
the default.
   +    stats.distinct_count.get_value()?;
        let distinct = *max_distinct_count(num_rows, stats).get_value()?;
   ```
   
   With that change, JOB runs at 0.65x of the base by geomean (46.6 s down to 
18.6 s in total). 62 queries are faster, 3 are slower by more than 1.2x, and 
results are identical on all 113 queries.
   
   **2. Rebuilt joins lose statistics on cast keys.** `key_expr` puts 
`CAST(col)` directly into the join's `on`, but `estimate_join_statistics` only 
reads keys that are plain columns (`equijoin_column_indices`). The rebuilt join 
then reports a much larger estimate than before, and `JoinSelection` picks the 
wrong build side. In 2c it builds on `title` (2.5M rows) instead of the empty 
side, and 16 ms becomes 53 ms. Computing the cast in a projection below the 
join, as the physical planner does, or letting the join statistics look through 
lossless casts would both fix it.
   
   The other two queries still slower after the first fix are small:
   
   - 4c (71 to 88 ms): the new order is about as good as the written one by 
intermediate rows, but the big `movie_keyword` join now hashes two keys from 
the same equivalence class, `movie_id = movie_id` and `CAST(t.id) = 
CAST(mk.movie_id)`. The second is already implied by the join below it. That 
join takes 66 ms where the base plan's equivalent takes 32 ms. Keeping one key 
per equivalence class when rebuilding a join would avoid it.
   - 23b (183 to 225 ms): the rebuilt plan builds on `movie_keyword` (4.52M 
rows) against a side that produces 6 rows. `JoinSelection` compares bytes 
before rows, and that side reports 45 MB for an estimated 164K rows, more than 
the 36 MB of the `movie_keyword` scan. The inflated size doesn't come from this 
PR: on main, inner join statistics keep each input column's `byte_size` 
unscaled, and the semi join above adds them up. So this one needs a statistics 
fix on main rather than a change here.
   
   Where it falls short of the hand-written order (8c, 8d, 17e), the cause is 
filter and fan-out estimates, not the search.
   
   Setup: M4 Pro, 12 threads, Parquet files, best of 3 runs. Other work was 
running on the machine, so differences under about 10% are noise.
   
   I'm happy to keep working on top of this PR.
   
   cc @2010YOUY01 since you're working on join ordering.
   


-- 
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