liaoxin01 opened a new pull request, #66588:
URL: https://github.com/apache/doris/pull/66588

   ### What problem does this PR solve?
   
   > **Stacked on #66545.** Until that one merges, the diff here shows its 
commit as
   > well. The change this PR is about is the second commit,
   > `[improvement](load) sort memtable with the vectorized ColumnSorter`.
   
   Problem Summary:
   
   `MemTable::_sort()` ran its own multi-key sort: `pdqsort` over the row array,
   with the comparator passed as a `std::function` that called the virtual
   `IColumn::compare_at` once per comparison. Every comparison therefore paid an
   indirect call, a virtual dispatch and two random accesses into the block 
before
   it could even look at the key.
   
   The query engine already has the sort this needs. `ColumnSorter`
   (`be/src/exec/sort/sort_block.h`) implements the same sort-and-tie algorithm 
--
   sort by a key column, mark equal ranges, refine within them using the next 
key
   column -- but keeps an inline copy of the key next to the row id, so a 
comparison
   becomes a typed, inlinable operation on a compact array instead of a chain of
   random accesses.
   
   This PR makes `_sort()` and `_sort_by_cluster_keys()` build an
   `IColumn::Permutation` and run it through `ColumnSorter`. 
`_sort_one_column()`
   and `class Tie` have no users left and are removed. `_sort_by_cluster_keys()`
   no longer needs a row object per row either: the LSN sidecar it was carrying 
is
   already indexed by row position, so the permutation can reorder it directly.
   
   Measured with a micro-benchmark on 7.05M rows, one key column, Release build:
   
   | key type | current | ColumnSorter | speedup |
   | --- | --- | --- | --- |
   | int32 | 13.2 s | 0.96 s | 13.7x |
   | int64 | 13.3 s | 1.07 s | 12.4x |
   | decimal128(20,2) | 13.7 s | 1.28 s | 10.7x |
   | varchar ~4B | 19.6 s | 3.03 s | 6.5x |
   | varchar ~40B | 21.9 s | 3.75 s | 5.8x |
   | nullable int32 | 16.0 s | 1.04 s | 15.5x |
   | nullable varchar ~4B | 25.4 s | 2.81 s | 9.0x |
   
   Fixed-width keys gain the most because their inline value is the value 
itself;
   string keys still dereference the arena for the `memcmp`, so the longer the 
key
   the smaller the gain. No key type regressed.
   
   The motivating case was a load whose profile showed `MemTableSortTime` at 83 
s
   across 7 memtables of a single tablet, with the sink blocked in
   `WaitFlushLimitTime` for 45 s behind it.
   
   ### Memory
   
   Keeping a copy of the key next to each row id costs memory in proportion to 
the
   key width: 8 B/row for `INT32`, 16 B for `INT64`, 24 B for a `StringRef`, 32 
B
   for `Decimal128` after alignment. It is a `std::vector` local to
   `ColumnSorter::_sort_by_inline_permutation`, so it is released between key
   columns and the peak holds one of them, plus 8 B/row for the permutation and
   1 B/row for the equal flags.
   
   That is why #66545 comes first. Against the 24 bytes per row the memtable 
spends
   on the rows themselves after that change, the peak of the two together is 
below
   what the previous sort needed, for every key type:
   
   | key type | before both PRs | after both |
   | --- | --- | --- |
   | int32 | 592 MB | 288 MB |
   | int64 | 592 MB | 345 MB |
   | varchar | 592 MB | 401 MB |
   | decimal128(20,2) | 592 MB | 458 MB |
   
   Merging this one without #66545 would instead take the peak *up*, to 683 MB 
for
   an int32 key and 853 MB for a decimal128 one, which is why they are ordered.
   
   ### Tie-break
   
   Ordering is byte-for-byte identical to before, tie-break included: rows whose
   whole key is equal are stabilised on **descending** row position for 
`DUP_KEYS`
   and on **ascending** row position for everything else, exactly as the 
previous
   `is_dup ? lhs->_row_pos > rhs->_row_pos : lhs->_row_pos < rhs->_row_pos` did.
   
   That `DUP_KEYS` direction has no semantics behind it -- it reproduces the
   iteration order of the skip list MemTable used before #18686, where
   `SkipList::Insert` linked a new node *ahead* of the existing equal keys, so
   iterating yielded equal keys in reverse insertion order. The skip list is 
long
   gone and #18686, #19099 and #20392 each carried the ternary along without it
   meaning anything.
   
   Dropping it is nevertheless not free: a run of P0 with the tie-break 
normalised
   to ascending fails 39 cases, and only about half of those are a missing
   `ORDER BY`. In the rest the *content* changes rather than the order -- which 
row
   wins in a `UNIQUE` table fed by an unordered `select`, which element survives
   `collect_set(k, 1)`, the element order inside `array_agg`, the auto-increment
   id-to-row mapping, `first_value` over a window whose `ORDER BY` has ties. 
Those
   can only be "fixed" by rewriting the expected output. So the direction is 
kept
   here, and removing it is left as its own change.
   
   ### Release note
   
   None
   
   ### Check List (For Author)
   
   - Test
       - [x] Unit Test
       - [ ] Regression test
       - [ ] Manual test (add detailed scripts or steps below)
       - [ ] No need to test or manual test. Explain why:
           - [ ] This is a refactor/code format and no logic has been changed.
           - [ ] Previous test can cover this change.
           - [ ] No code files have been changed.
           - [ ] Other reason
   
   The `MemTable`-driven cases in `be/test/load/memtable/memtable_sort_test.cpp`
   come from #66545 and cover the ordering this change has to preserve. This PR
   adds one more, for a permutation that is a single long cycle rather than the
   short swaps the other cases happen to produce, since the permutation is 
applied
   to the row array in place by following its cycles. The tie-break case was
   verified to fail when the direction is flipped, so it does discriminate.
   
   - Behavior changed:
       - [x] No.
       - [ ] Yes.
   
   - Does this need documentation?
       - [x] No.
   


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