azecevic000 opened a new pull request, #12141: URL: https://github.com/apache/arrow-rs/pull/12141
# Which issue does this PR close? N/A # Rationale for this change Buffers without an allocation have a dangling pointer in the unmapped first page. On x86-64 CPUs with AVX512 and glibc >=2.35 , the `memcmp` and `bcmp` of length 0 that Rust uses to compare empty slices take up to about 100 ns at such an address, instead of 2 ns. So each compare of values of an all-empty string or binary array is slow. The CPU walks the page tables on every call, because the TLB does not cache unmapped pages. Then it cancels the page fault, because the mask covers it. # What changes are included in this PR? All buffers without an allocation now point at `ZERO_SIZE_AREA`, a static aligned to 256 bytes (at least `ALIGNMENT` on all targets), as Arrow C++ does with `zero_size_area`: - `dangling_ptr()` returns its address. The `cfg(miri)` variant is no longer necessary, because the address now comes from a real static. - `From<Vec<T>> for MutableBuffer` (so also `Buffer::from_vec`) uses it for a `Vec` without an allocation. - `MutableBuffer` uses it after it shrinks to size 0. Before, it kept the pointer to the freed memory. Empty buffers already shared addresses before (`0x80`, or `0x1` for `Vec<u8>`), so code cannot rely on a unique address for them. # Are these changes tested? `test_buffers_without_allocation_point_to_zero_size_area` checks the three places. The existing tests pass. Intel Xeon Platinum 8375C (AVX512VL and AVX512BW), glibc 2.35 or later, one core, 1,048,576 empty strings. `arrow_writer` benchmark, `string_empty` from https://github.com/apache/arrow-rs/pull/12140, criterion median per iteration: | benchmark | `main` | this PR | change | |---|---:|---:|---:| | `string_empty/default` | 205.6 ms | 15.0 ms | -92.7% | | `string_empty/bloom_filter` | 205.9 ms | 15.4 ms | -92.5% | | `string_empty/parquet_2` | 205.9 ms | 15.1 ms | -92.7% | | `string_empty/zstd` | 205.7 ms | 15.1 ms | -92.6% | | `string_empty/zstd_parquet_2` | 205.4 ms | 15.2 ms | -92.6% | | `string_empty/cdc` | 208.3 ms | 17.4 ms | -91.6% | | `string_empty/number_distinct_values` | 226.4 ms | 36.2 ms | -84.0% | With this PR, `string_empty/default` is as fast as the same values in an allocated buffer (15.04 ms and 15.05 ms). No regressions: the existing `string`, `string_non_null` and `string_dictionary` cases (21 benchmarks) show no change (-3.8% to +3.5%, all within criterion's noise). In `buffer_create`, the changes are -1.7% to +2.8%; a control run of the unchanged code moved the same three bool cases by +1.2% to +1.8%. # Are there any significant user-facing changes? No. Only the address of empty buffers changes. # AI use An AI coding agent wrote the code. I have reviewed it and verified the benchmark results. -- 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]
