jayzhan211 opened a new pull request, #24800:
URL: https://github.com/apache/datafusion/pull/24800

   ## Which issue does this PR close?
   
   - Closes #.
   
   ## Rationale for this change
   
   Physical planning asks "is this ordering already satisfied?" constantly — 
sort
   removal, `EnforceSorting`, `EnforceDistribution`, and the requirement checks 
for
   windows, joins and aggregates all call into
   `EquivalenceProperties::ordering_satisfy`, `ordering_satisfy_requirement` and
   `extract_common_sort_prefix`.
   
   Each of those calls deep-clones the entire `EquivalenceProperties` — every
   equivalence class, every equivalent ordering, and the normalized ordering 
cache —
   before doing anything else, even when it never modifies the copy.
   
   The clone exists for a real reason: as the check walks a multi-key ordering 
left
   to right, it registers each satisfied key as a constant so the next key is
   evaluated within that key's tie group. That mutates state, so it needs its 
own
   copy. But two cases pay for it and get nothing back:
   
   1. **A single-key check never mutates anything.** There is no "next key" to 
set up
      for, so the whole clone is wasted. This is the most common shape of these 
calls.
   2. **The last key of *any* check registers constants nobody reads.** After 
the
      final key is verified, the code still calls `add_satisfied_key_constants`,
      which rebuilds the ordering cache and re-runs ordering discovery — and 
then the
      object is dropped.
   
   ## What changes are included in this PR?
   
   Two changes in `EquivalenceProperties`, to `ordering_satisfy_requirement` and
   `common_sort_prefix_length` (the latter backs `ordering_satisfy`,
   `extract_common_sort_prefix` and `reorder`):
   
   - **Clone on first write instead of up front.** The loop borrows `self` and 
clones
     only when it actually needs to register a constant. Single-key checks never
     clone at all.
   - **Skip the registration after the last key.** Nothing reads it.
   
   Plus a new criterion benchmark, `equivalence_properties`, covering these 
entry
   points.
   
   This only changes *when* the copy is made — the results of these functions 
are
   unchanged.
   
   ## Metrics
   
   Apple M4 Pro, rustc 1.97.0, criterion. All changes significant at p = 0.00.
   
   Properties under test: 3 equivalent orderings (`[c0,c1,c2,c3]`, `[c4,c5]`, 
`[c6]`)
   and a varying number of equivalence classes.
   
   **At 8 equivalence classes:**
   
   | benchmark | before | after | change |
   |---|---:|---:|---|
   | `ordering_satisfy` — 1 key | 2.72 µs | 0.41 µs | **−84.9%** |
   | `ordering_satisfy` — 1 key, unsatisfied | 1.47 µs | 0.41 µs | **−72.5%** |
   | `ordering_satisfy_requirement` — 1 key | 2.70 µs | 0.36 µs | **−86.3%** |
   | `ordering_satisfy_requirement` — 4 keys | 7.15 µs | 6.07 µs | −13.8% |
   | `ordering_satisfy` — 4 keys | 6.99 µs | 6.20 µs | −11.6% |
   | `extract_common_sort_prefix` — 4 keys | 7.22 µs | 6.38 µs | −9.2% |
   
   **How it scales** (`ordering_satisfy`, 1 key):
   
   | equivalence classes | before | after | change |
   |---:|---:|---:|---|
   | 2 | 2.44 µs | 0.43 µs | −82.5% |
   | 8 | 2.72 µs | 0.41 µs | −84.9% |
   | 32 | 4.63 µs | 0.41 µs | **−90.8%** |
   
   Reading the tables: for an *N*-key check the work goes from
   `1 clone + N registrations` to `(N > 1 ? 1 : 0) clones + (N − 1) 
registrations`.
   
   - **1-key checks** drop both the clone and the registration. Note the "after"
     column is flat at ~0.41 µs regardless of how many equivalence classes 
exist —
     with the clone gone, the check no longer scales with the size of the
     equivalence group at all. The "before" column does, which is why the win 
grows
     from −82% to −91%.
   - **Multi-key checks** still clone once and save one of *N* registrations. 
Since a
     registration rebuilds the ordering cache and re-runs ordering discovery, 
that
     single saved call is worth 9–14% here, rising to −37.8% for `4_keys` at 32
     classes.
   
   ### Reproducing
   
   The benchmark is included in this PR, so reverting just the one source file 
gives
   you the baseline:
   
   ```bash
   # baseline: this PR's parent version of the file, with the new benchmark kept
   git checkout HEAD^ -- 
datafusion/physical-expr/src/equivalence/properties/mod.rs
   cargo bench -p datafusion-physical-expr --bench equivalence_properties -- 
--save-baseline before
   
   # with the change
   git checkout HEAD -- 
datafusion/physical-expr/src/equivalence/properties/mod.rs
   cargo bench -p datafusion-physical-expr --bench equivalence_properties -- 
--baseline before
   ```
   
   The second run prints criterion's own `change: [...] (p = ...)` line per
   benchmark.
   
   ## Are these changes tested?
   
   No new correctness tests: this does not change what any of these functions
   return, so existing coverage is the right check. Covered by the `equivalence`
   unit tests in `datafusion/physical-expr` and, for plan-shape regressions, by
   sqllogictest — these functions decide whether a `SortExec` can be removed, 
so a
   behavior change would surface as a diff in an `EXPLAIN` plan.
   
   Full workspace suite
   (`--features 
avro,json,backtrace,extended_tests,recursive_protection,parquet_encryption`):
   10,981 passed, 0 failed, and all 505 sqllogictest files pass. 
`./dev/rust_lint.sh`
   is clean.
   
   ## Are there any user-facing changes?
   
   No. No public API or behavior changes — planning is just faster.


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