cetra3 opened a new pull request, #11511:
URL: https://github.com/apache/arrow-rs/pull/11511

   # Which issue does this PR close?
   
   - N/A
   
   This PR supersedes #11505 and #11506. It makes the same changes in a general 
form.
   
   # Rationale for this change
   
   Every access to a variant value pays for checks that the access does not 
use. `Variant::new` and the unvalidated constructors of `VariantMetadata`, 
`VariantObject` and `VariantList` read the first and last offsets of each 
header and trim the byte slices. A field lookup then validates the UTF-8 of 
every dictionary name that its binary search probes. Kernels such as 
`variant_get` construct a full `Variant` for every row, and then walk the path 
through it.
   
   For most kernel work, a value needs much less: the header of each object or 
list along a path, the names that the binary search probes, and the bytes of 
the value that it finds.
   
   # What changes are included in this PR?
   
   - A new module, `parquet_variant::view` (`#[doc(hidden)]`), adds 
`MetadataView`, `ValueView`, `ObjectView` and `ListView`.
     - Building a view reads one header and nothing else.
     - Each accessor reads and bounds-checks only the bytes that it needs, and 
returns an error on malformed input. It never panics.
     - Field names are compared as bytes, which orders them as `str` does, so a 
search does not validate UTF-8.
     - A view promotes to the existing types: `MetadataView::into_metadata`, 
`ObjectView::into_object`, `ListView::into_list` and `ValueView::to_variant` 
add the first and last offset checks that unvalidated instances guarantee.
   - `VariantMetadata`, `VariantObject` and `VariantList` are now built by 
promoting a view, and their accessors (`get`, `field`, `field_name`, `iter`, 
`get_entry`) delegate to it. Their public behavior is unchanged.
   - `variant_get` reads unshredded columns through the views, for field and 
index paths and for typed and `Variant` output. A row reads only the headers 
and names along the path, and decodes only the value that it finds.
   - `shred_variant` and `unshred_variant` copy leftover object fields by field 
id with their encoded bytes, through a new `#[doc(hidden)]` 
`ObjectBuilder::insert_encoded`. A leftover field is no longer decoded into a 
`Variant` and then re-encoded.
   - `ObjectBuilder::finish` sorts fields with a new 
`MetadataBuilder::cmp_field_names`. `ReadOnlyMetadataBuilder` compares the 
names as bytes.
   - `VariantMetadata::borrowed_field_id` is removed. No caller in the 
repository needs it after the changes above. Its tests now cover `get_entry`.
   
   # Are these changes tested?
   
   The existing tests of `parquet-variant`, `parquet-variant-compute`, 
`parquet-variant-json`, and the `parquet` variant integration tests pass. The 
public types now delegate to the views, so their tests also cover the views.
   
   Benchmarks (local run, `main` vs this PR, back to back, Apple Silicon 
laptop):
   
   | benchmark | main | this PR | change |
   |---|---:|---:|---:|
   | `variant_get_unshredded_object_path_262k_rows` | 21.93 ms | 11.98 ms | 
−45% |
   | `variant_get_list_index_unshredded` | 573.4 µs | 266.5 µs | −54% |
   | `variant_get_utf8_from_int` | 272.4 µs | 172.8 µs | −37% |
   | `variant_get_utf8_from_unshredded_string` | 336.0 µs | 226.5 µs | −33% |
   | `variant_get_binary_from_unshredded_binary` | 283.0 µs | 179.2 µs | −37% |
   | `variant_get_utf8_from_list` | 364.6 µs | 329.3 µs | −10% |
   | `variant_get_utf8_from_map_in_list` | 376.4 µs | 331.1 µs | −12% |
   | `shred_variant_partial_object_8k_rows` | 14.03 ms | 9.29 ms | −34% |
   | `shred_variant_unmatched_object_8k_rows` | 18.50 ms | 11.65 ms | −37% |
   | `variant_get_list_index_shredded` | 59.4 µs | 58.8 µs | −1% |
   | `variant_get_shredded_utf8` | 343.1 ns | 343.6 ns | +0% |
   | `object_get_by_name/128` | 86.0 ns | 69.1 ns | −20% |
   | `object_iter/128` | 2.0 µs | 1.6 µs | −21% |
   | `object_field_name/128` | 1.1 µs | 962.6 ns | −12% |
   | `metadata_iter/128` | 688.5 ns | 609.8 ns | −11% |
   | `bench_validate_large_object` | 19.39 ms | 19.46 ms | +0% |
   | `bench_validate_large_nested_list` | 7.18 ms | 7.34 ms | +2% |
   
   The 16 `variant_get_decimal_*` benchmarks are 25–36% faster. No benchmark in 
`variant_kernels` or `variant_validation` is more than 2% slower.
   
   On real-world telemetry data with per-row metadata, `variant_get` on 
unshredded object fields is 1.6–1.9x faster than `main` for keys that most rows 
contain, and 2.2–2.4x faster for keys that are rare or absent.
   
   # Are there any significant user-facing changes?
   
   No public API changes, other than the new `#[doc(hidden)]` items. Behavior 
differences:
   
   - On malformed variant bytes, `variant_get`, `shred_variant` and the 
residual copy in `unshred_variant` return an error, or a null under safe 
casting. Before, these paths could panic.
   - Some out-of-range errors from metadata and list accessors have new message 
text.
   - A caller outside the repository that passes field names borrowed from the 
metadata dictionary to `ObjectBuilder::insert_bytes` now resolves them through 
`get_entry` and the existing name cache, not through `borrowed_field_id`.
   


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

Reply via email to