neilconway opened a new pull request, #26377:
URL: https://github.com/apache/datafusion/pull/26377
## Which issue does this PR close?
- N/A
## Rationale for this change
#26192 switched `upper` for non-ASCII `Utf8View` inputs from using
`std::to_uppercase` to doing a per-character loop. While that change avoids an
allocation, it regresses inputs that have some ASCII-only bytes, because
`std::to_uppercase` has a special-case for that situation. For example, `upper`
on a `Utf8View` array with a single non-ASCII string was 19x slower.
This PR reverts that change, but in the course of doing so, we also add
several related optimizations to `upper`, `lower`, and the optimized string
builders; details below.
Benchmarks: (x86, AMD EPYC Milan)
upper
- Some non-ASCII rows:
- Utf8View, 4096 rows: 952.7 µs → 254.5 µs (−73.3%)
- Utf8View, 4096 rows, 10% nulls: 881.7 µs → 270.6 µs (−69.3%)
- Utf8View, 8192 rows: 1.94 ms → 539.7 µs (−72.2%)
- Utf8View, 8192 rows, 10% nulls: 1.79 ms → 559.4 µs (−68.7%)
- Non-ASCII words:
- Utf8: 825.5 µs → 726.6 µs (−12.0%)
- LargeUtf8: 824.7 µs → 726.3 µs (−11.9%)
- Utf8View: 759.5 µs → 727.6 µs (−4.2%)
- Mostly ASCII:
- Utf8, len 8: 204.0 µs → 19.6 µs (−90.4%)
- Utf8View, len 8: 214.6 µs → 36.5 µs (−83.0%)
- Utf8, len 32: 789.6 µs → 23.2 µs (−97.1%)
- Utf8View, len 32: 757.4 µs → 36.2 µs (−95.2%)
- Utf8, len 128: 3.13 ms → 51.7 µs (−98.3%)
- Utf8View, len 128: 2.89 ms → 47.5 µs (−98.4%)
- Late non-ASCII:
- Utf8, len 8: 268.2 µs → 154.1 µs (−42.5%)
- Utf8View, len 8: 259.4 µs → 156.5 µs (−39.7%)
- Utf8, len 32: 881.8 µs → 136.4 µs (−84.5%)
- Utf8View, len 32: 798.5 µs → 138.1 µs (−82.7%)
- Utf8, len 128: 3.20 ms → 164.5 µs (−94.9%)
- Utf8View, len 128: 2.95 ms → 163.9 µs (−94.4%)
lower
- Some non-ASCII rows:
- Utf8View, 4096 rows: 270.3 µs → 232.8 µs (−13.9%)
- Utf8View, 4096 rows, 10% nulls: 271.0 µs → 235.8 µs (−13.0%)
- Utf8View, 8192 rows: 568.8 µs → 495.8 µs (−12.8%)
- Utf8View, 8192 rows, 10% nulls: 572.5 µs → 502.4 µs (−12.3%)
- Non-ASCII words:
- Utf8: 514.2 µs → 511.0 µs (−0.6%)
- LargeUtf8: 507.8 µs → 516.4 µs (+1.7%)
- Utf8View: 510.2 µs → 509.4 µs (−0.2%)
- Mostly ASCII:
- Utf8, len 8: 67.8 µs → 18.6 µs (−72.5%)
- Utf8View, len 8: 73.9 µs → 36.5 µs (−50.7%)
- Utf8, len 32: 60.5 µs → 22.9 µs (−62.2%)
- Utf8View, len 32: 68.4 µs → 36.5 µs (−46.6%)
- Utf8, len 128: 76.7 µs → 52.1 µs (−32.1%)
- Utf8View, len 128: 82.6 µs → 46.8 µs (−43.3%)
- Late non-ASCII:
- Utf8, len 8: 136.9 µs → 138.8 µs (+1.4%)
- Utf8View, len 8: 140.0 µs → 139.8 µs (−0.1%)
- Utf8, len 32: 115.3 µs → 124.8 µs (+8.2%)
- Utf8View, len 32: 117.4 µs → 125.5 µs (+6.9%)
- Utf8, len 128: 138.9 µs → 150.0 µs (+8.0%)
- Utf8View, len 128: 142.4 µs → 152.3 µs (+7.0%)
translate (ASCII scalar from/to, no deletions)
- Utf8:
- 1024 rows, len 8: 10.1 µs → 9.1 µs (−9.8%)
- 1024 rows, len 32: 17.9 µs → 15.9 µs (−11.2%)
- 1024 rows, len 128: 48.8 µs → 39.2 µs (−19.6%)
- 1024 rows, len 1024: 363.8 µs → 270.7 µs (−25.6%)
- 4096 rows, len 8: 37.5 µs → 33.7 µs (−10.1%)
- 4096 rows, len 32: 68.6 µs → 62.8 µs (−8.5%)
- 4096 rows, len 128: 195.8 µs → 154.6 µs (−21.1%)
- 4096 rows, len 1024: 1.42 ms → 1.07 ms (−24.7%)
- Utf8View:
- 1024 rows, len 8: 9.1 µs → 8.4 µs (−8.2%)
- 1024 rows, len 32: 14.5 µs → 13.8 µs (−4.6%)
- 1024 rows, len 128: 36.2 µs → 35.0 µs (−3.3%)
- 1024 rows, len 1024: 236.2 µs → 235.4 µs (−0.3%)
- 4096 rows, len 8: 34.4 µs → 31.1 µs (−9.5%)
- 4096 rows, len 32: 55.4 µs → 52.5 µs (−5.2%)
- 4096 rows, len 128: 142.2 µs → 137.0 µs (−3.7%)
- 4096 rows, len 1024: 937.7 µs → 940.8 µs (+0.3%)
replace (single ASCII byte, Utf8)
- 1024 rows, len 32: 26.2 µs → 9.0 µs (−65.6%)
- 1024 rows, len 128: 92.6 µs → 16.4 µs (−82.3%)
- 1024 rows, len 32, 20% nulls: 22.8 µs → 9.1 µs (−60.3%)
- 1024 rows, len 128, 20% nulls: 76.5 µs → 14.6 µs (−80.9%)
- 4096 rows, len 32: 103.2 µs → 31.2 µs (−69.7%)
- 4096 rows, len 128: 372.6 µs → 61.9 µs (−83.4%)
- 4096 rows, len 32, 20% nulls: 89.9 µs → 33.5 µs (−62.7%)
- 4096 rows, len 128, 20% nulls: 316.5 µs → 56.8 µs (−82.1%)
## What changes are included in this PR?
* Add `StringViewArrayBuilder::try_append_byte_map`, a fallible variant of
the existing `append_byte_map`. (We need this to avoid regressing error
handling behavior on overflow when we use this method below.)
* `lower`, `upper`, for non-ASCII input arrays: we now check if each row is
ASCII. If it is, we construct the result value directly via the string
builder's `try_append_byte_map` method, which vectorizes. Non-ASCII rows are
converted via `std::to_uppercase / std::to_lowercase`.
* Optimize `GenericStringArrayBuilder::try_append_byte_map` to avoid calling
Arrow's `MutableBuffer::extend`, which is slow. Since we know exactly how many
bytes will be appended, we reserve that space in the value buffer first and
then do the per-byte map. This speeds up the callers of `try_append_byte_map`,
which include `translate` and `replace`.
* Comment cleanups / improvements
* Test consolidation: remove redundant tests, move more tests to SLT,
improve test coverage
* Benchmark consolidation: move `upper` and `lower` benchmarks to the same
place, add benchmarks for `upper` and `lower` on mixed ASCII and non-ASCII
inputs, add benchmark for `translate` on `Utf8View`
## What is the testing strategy for this PR?
Existing tests pass; new tests added.
## Are there any user-facing changes?
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]