andygrove opened a new pull request, #2809:
URL: https://github.com/apache/datafusion-ballista/pull/2809

   > **Experimental / WIP.** Opened as a draft to get early feedback on the 
format and the approach. Not ready to merge.
   >
   > **Stacked on #2805** (which is stacked on #2802). Review only the last 
commit, `perf: store shuffle string offsets as lengths before zstd compression`.
   
   # Which issue does this PR close?
   
   Closes #2804.
   
   # Rationale for this change
   
   Shuffle data is Arrow IPC with arrow-ipc's per-buffer compression. The 
offsets buffers of `Utf8`, `Binary`, list and map columns hold running sums. 
Those never repeat, so zstd compresses them poorly even when every value has 
the same length, and they often cost about as much as the string bytes. Stored 
as value lengths split into byte planes, they compress to almost nothing. 
DataFusion Comet made the same change in apache/datafusion-comet#6943.
   
   `shuffle_bench`, one task, 16 hash partitions, 8,192-row batches, zstd, 
TPC-H SF100 tables with all columns, two interleaved runs each:
   
   | Table (rows) | Offsets as is | Offsets as lengths | Smaller by |
   |---|---:|---:|---:|
   | customer (15M) | 977.2 MB | 803.1 MB | 17.8% |
   | part (20M) | 791.7 MB | 530.1 MB | 33.0% |
   | orders (first 30M) | 1,106.3 MB | 845.7 MB | 23.6% |
   | lineitem (first 30M) | 1,240.1 MB | 943.6 MB | 23.9% |
   
   Write time and the time to read every partition back stayed within 
run-to-run noise (for example lineitem write 12.2 s / 12.0 s off vs 12.4 s / 
12.2 s on, read back 2.63 s / 2.63 s off vs 2.51 s / 2.57 s on). These are 
single-machine numbers. Cluster runs are still to do.
   
   # What changes are included in this PR?
   
   - `ballista_core::shuffle_ipc` (new):
     - `offsets.rs` ports Comet's buffer-role mapping and the in-place rewrite 
between offsets and byte-plane lengths.
     - `ShuffleStreamWriter` encodes each batch uncompressed with 
`IpcDataGenerator`, rewrites the offsets, compresses the body as one zstd frame 
and rebuilds the message. `ShuffleEncoding` decides once per schema whether to 
do that: only with zstd, only for schemas with an offsets buffer whose types 
all have a mapped layout, and only when arrow-ipc lays out an empty batch of 
the schema as the mapping expects. Everything else gets arrow-ipc's 
`StreamWriter` and identical bytes.
     - `ShuffleIpcReader` (a `Read` adapter for `StreamReader`) and 
`ShuffleBytesTranslator` (push-based, for block transport) turn rewritten 
messages back into standard uncompressed IPC messages and pass all other 
messages through byte for byte.
   - Format: the stream keeps Arrow IPC framing. A rewritten record-batch 
message has a `BodyCompression` codec Arrow does not define (`0x40`), buffer 
offsets that describe the uncompressed body, and a body holding the 
uncompressed and compressed lengths followed by the zstd frame. Readers that do 
not know it fail with arrow-ipc's "compression type not supported" error 
instead of misreading the data. Each stream stays self-contained, so the sort 
shuffle's byte-copied spill streams and its index are unchanged.
   - Writers: the passthrough `ShuffleWriterExec` (`write_stream_to_disk`), 
sort shuffle spills and the sort shuffle's in-memory partition encoding.
   - Readers: local passthrough reads, `MultiStreamPartitionStream`, the Flight 
`do_get` serve path (which still re-encodes with LZ4, so clients are 
unaffected) and `BlockDataStream`.
   - Not covered: range shuffle (IPC file format with per-message slicing), 
pipelined in-memory shuffle, and LZ4 (lz4_flex decodes long runs slowly, 
PSeitz/lz4_flex#227).
   - `ballista.shuffle.compression.offsets_as_lengths` (default `true`, only 
has an effect with zstd) as an off switch.
   - `BALLISTA_PROTOCOL_VERSION` 3 → 4, because block transport ships raw 
shuffle bytes to the consuming executor.
   - `shuffle_bench` gains `--codec` and `--offsets-as-lengths`, and reports 
shuffle bytes and read-back time.
   - Tests: Comet's role and round-trip unit tests, plus round trips of string, 
binary, list, large list, fixed-size list, map and struct columns with nulls, 
empty values, sliced inputs and empty batches under every codec and setting. 
They cover both readers, with block-transport bytes fed in blocks of 1 to N 
bytes, and check that:
     - plain streams match arrow-ipc's byte for byte,
     - unmapped schemas (views, dictionaries) and schemas without offsets fall 
back,
     - concatenated streams are crossed correctly,
     - an unaware arrow reader rejects the format,
     - truncated or unmapped rewritten batches are errors.
   
     The existing `sort_shuffle` end-to-end matrix (local, Flight, block 
transport) passes with the setting on by default.
   
   # Are there any user-facing changes?
   
   Yes:
   
   - Shuffle files written with zstd are smaller for schemas with string, 
binary, list or map columns.
   - The new `ballista.shuffle.compression.offsets_as_lengths` setting.
   - `BALLISTA_PROTOCOL_VERSION` goes to 4, so the scheduler and all executors 
must be upgraded together.
   - `write_stream_to_disk` and `SpillManager::new` take a `ShuffleCompression` 
instead of an `Option<CompressionType>`.
   
   The upgrade guide has entries for these.
   


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