Repository navigation
Conversation
9a16045 to
05dce65
Compare
Measures normalize_floats, which runs on every float key column before it is compared, hashed or grouped.
`normalize_floats` first scans a float column for values that need rewriting and returns it unchanged when there are none, the common case. The scan used an early-exit `iter().any()`, which checks one value at a time and does not vectorize. Fold over every value instead. The fold has no early exit, so the compiler turns it into a SIMD OR-reduction. It always scans the whole column, but that only costs extra when the column has a value to rewrite, and then the column is copied anyway.
05dce65 to
5bd940c
Compare
I think given the complexity we don't need to split PRs that much |
fair point :-) |
kumarUjjawal
left a comment
There was a problem hiding this comment.
Thank you @mkleen
Left few comments please take a look
| if !arr.values().iter().any(|v| v.needs_normalization()) { | ||
| // Not `any()`: its early exit checks one value at a time, while a fold | ||
| // over every value vectorizes. | ||
| let needs_normalization = arr |
There was a problem hiding this comment.
The fold now always reads the whole column, even when a -0.0 is in the first row. Folding in fixed chunks (e.g. 512 values) and stopping after the first chunk that finds one would keep the SIMD speedup and still exit early.
The first-row case is the one where the check can stop right away.
The fold over the whole column vectorizes, but it always reads every value, even when the first row is a -0.0. Fold over chunks of 1024 values instead and stop after the first chunk with a match. Each chunk still vectorizes, and a -0.0 near the start no longer costs a full scan.
Name the chunk size, so the test follows it if it is retuned, cover Float16 and Float32 as well, and describe the chunked check in the normalize_floats docs.
94a403a to
e294cf8
Compare
|
Thanks for the review @kumarUjjawal! I addressed your feedback:
I updated the benchmark run in the pr description. |
Which issue does this PR close?
Rationale for this change
normalize_floatsruns on every float key column before it is compared, hashed or grouped. It first scans the column for values that need rewriting. When there are none, which is the common case, it returns the input unchanged, so that scan is the whole cost.The scan used an early-exit
iter().any(). Because the loop can stop at any value, the compiler checks one value at a time and doesn't vectorize it.Results from the
float_normalizationbenchmark on 8192 values from my local machine (Apple M1 Max):What changes are included in this PR?
normalize_primitivenow checks the values in chunks of 1024. Inside a chunk it folds over every value with no early exit, so the compiler turns it into a SIMD OR-reduction. It stops after the first chunk that finds a value to rewrite.-0.0is near the start, the whole first chunk is still checked before the scan stops. That adds 20–90 ns, and the column is copied right after anyway.float_normalizationbenchmark covers f64, f32 and f16, with nothing to rewrite, a-0.0in the first row, and a-0.0in the last row.What is the testing strategy for this PR?
The behavior is unchanged. The existing
normalize_floatsunit tests infloat_semantics.rsand the float sqllogictests cover both the case with nothing to normalize and the rewrite path.Are there any user-facing changes?
No.
LLM-generated code disclosure
This PR includes LLM-generated code and comments. All LLM-generated content has been manually reviewed.