Skip to content

TEZ-4760: IFile.Writer: buffer per-record writes before the compression and checksum streams - #544

Open
deniskuzZ wants to merge 2 commits into
apache:masterfrom
deniskuzZ:TEZ-ifile-write-perf
Open

deniskuzZ wants to merge 2 commits into
apache:masterfrom
deniskuzZ:TEZ-ifile-write-perf

Conversation

@deniskuzZ

Copy link
Copy Markdown
Member

IFile.Writer writes every record as several small writes (the key and value lengths as vints, then the key and value
bytes) directly into the codec stream, or into the checksum stream when there is no codec. Each call is a full trip
into the stream: with Snappy, a synchronized Compressor.setInput and friends per call; without a codec, the checksum
update per call.

The patch puts an unsynchronized 64 KB write buffer behind a NonSyncDataOutputStream between the writer and those
streams, so the per-record writes land in one array and reach the codec in large chunks. close() drains the buffer
before it finishes the codec and checksum streams. A Writer is confined to one thread, so the buffer takes no lock
(BufferedOutputStream locks on every call).

Microbenchmark (JDK 21, 6 runs each, median CPU per record; base = master):

Path Rows Snappy No codec
IFile.Writer default -12.5 % -67.6 %
IFile.Writer small -33 % -72 %
UnorderedPartitionedKVWriter, 24 partitions default / small -11.1 % / -11.8 %
UnorderedPartitionedKVWriter, 1,009 partitions default / small -5.8 % / -12.6 %

Context: in Hive on Tez (LLAP), TPC-DS 10 TB, the shuffle write path (ReduceSink serialization, then
UnorderedPartitionedKVWriter, IFile and Snappy) is 46-72 % of task CPU in the shuffle-heavy queries.

Tests: tez-runtime-library TestIFile, TestUnorderedPartitionedKVWriter, TestPipelinedSorter, TestDefaultSorter,
TestMergeManager, TestTezMerger: 455 run, 0 failures. Removing the drain in close() fails 179 of them, so the existing
round-trip tests cover the new path.

…on and checksum streams

- IFile.Writer writes each record as several small writes (key and value lengths, key, value) straight into the
  codec stream (Snappy: a synchronized Compressor.setInput per call) or the checksum stream
- An unsynchronized 64 KB write buffer behind NonSyncDataOutputStream batches them; close() drains it before
  finishing the codec and checksum streams; a Writer is confined to one thread
- Microbenchmark (median of 6, JDK 21): IFile.Writer per record -12.5 % (Snappy) and -67.6 % (no codec); small rows
  -33 % / -72 %; UnorderedPartitionedKVWriter end to end -5.8 % to -12.6 % (24 and 1,009 partitions)
- Hive on Tez, TPC-DS 10 TB: the shuffle write path (ReduceSink serialization + UnorderedPartitionedKVWriter + IFile
  + Snappy) is 46-72 % of task CPU in the shuffle-heavy queries

@abstractdog abstractdog left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, minor comments only

@InterfaceStability.Unstable
@SuppressWarnings({"unchecked", "rawtypes"})
public static class Writer {
private static final int WRITE_BUFFER_SIZE = 64 * 1024;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possible follow-up: use initial size of 4 * 1024 and grow until 64 * 1024

@deniskuzZ deniskuzZ Oct 23, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • BufferedOutputStream (thread-safe) +5–6 % (Snappy) / +13–47 % (no codec);
  • 4 KB costing +150 % on the no-codec path, which is Tez's default configuration.

We could do more measurements in the follow-up

}

/** {@link java.io.BufferedOutputStream} without its per-call lock. */
private static final class WriteBuffer extends FilterOutputStream {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add unit tests the exercise 3 different paths:

  1. single 64KB write
  2. single > 64KB write
  3. 63 KB + 2 KB writes

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added + extracted writer to a separate class - NonSyncBufferedOutputStream

…o and cover it with tests

  - NonSyncBufferedOutputStream in tez-common, beside NonSyncDataOutputStream and the two
    NonSyncByteArray streams; IFile uses it instead of a private copy
  - tests for the three buffer paths plus the single-byte boundary, close() and a rejected size:
    they assert the downstream write sizes, so a regression in the large-write bypass is caught
  - java.io.BufferedOutputStream measured as the alternative: +5-6% CPU/record with Snappy and
    +13-47% without a codec (1M records, median of 9 after 3 warmups, JDK 21), so the
    unsynchronized copy stays
@tez-yetus

Copy link
Copy Markdown

💔 -1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 0m 13s Docker mode activated.
_ Prechecks _
+1 💚 dupname 0m 0s No case conflicting files found.
+0 🆗 detsecrets 0m 0s detect-secrets was not available.
+1 💚 @author 0m 0s The patch does not contain any @author tags.
+1 💚 test4tests 0m 0s The patch appears to include 1 new or modified test files.
_ master Compile Tests _
+0 🆗 mvndep 0m 39s Maven dependency ordering for branch
+1 💚 mvninstall 4m 40s master passed
+1 💚 compile 4m 4s master passed
+1 💚 checkstyle 1m 5s master passed
+1 💚 javadoc 1m 4s master passed
+0 🆗 spotbugs 0m 54s tez-common in master has 13 extant spotbugs warnings.
+0 🆗 spotbugs 1m 11s tez-runtime-library in master has 143 extant spotbugs warnings.
_ Patch Compile Tests _
+0 🆗 mvndep 0m 9s Maven dependency ordering for patch
+1 💚 mvninstall 3m 41s the patch passed
+1 💚 codespell 1m 34s No new issues.
+1 💚 compile 4m 3s the patch passed
+1 💚 javac 4m 3s the patch passed
+1 💚 blanks 0m 0s The patch has no blanks issues.
+1 💚 checkstyle 0m 24s The patch passed checkstyle in tez-common
+1 💚 checkstyle 0m 33s tez-runtime-library: The patch generated 0 new + 58 unchanged - 2 fixed = 58 total (was 60)
+1 💚 javadoc 1m 4s the patch passed
-1 ❌ spotbugs 1m 3s /new-spotbugs-tez-common.html tez-common generated 1 new + 13 unchanged - 0 fixed = 14 total (was 13)
_ Other Tests _
+1 💚 unit 76m 14s root in the patch passed.
+1 💚 asflicense 0m 58s The patch does not generate ASF License warnings.
106m 56s
Reason Tests
SpotBugs module:tez-common
Exception thrown in class org.apache.tez.common.io.NonSyncBufferedOutputStream at new org.apache.tez.common.io.NonSyncBufferedOutputStream(OutputStream, int) will leave the constructor. The object under construction remains partially initialized and may be vulnerable to Finalizer attacks. At NonSyncBufferedOutputStream.java:at new org.apache.tez.common.io.NonSyncBufferedOutputStream(OutputStream, int) will leave the constructor. The object under construction remains partially initialized and may be vulnerable to Finalizer attacks. At NonSyncBufferedOutputStream.java:[line 38]
Subsystem Report/Notes
Docker ClientAPI=1.56 ServerAPI=1.56 base: https://ci-hadoop.apache.org/job/tez-multibranch/job/PR-544/2/artifact/out/Dockerfile
Optional Tests dupname compile unit asflicense javac javadoc spotbugs checkstyle codespell detsecrets
uname Linux 93a62f4b102f 5.15.0-186-generic #196-Ubuntu SMP Sat Jun 20 16:09:34 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Build tool maven
Personality tez-personality.sh
git revision master / 87824af
Default Java Eclipse Adoptium-21.0.12.1+1-LTS
Test Results https://ci-hadoop.apache.org/job/tez-multibranch/job/PR-544/2/testReport/
Max. process+thread count 2123 (vs. ulimit of 5500)
modules C: tez-common tez-runtime-library U: .
Console output https://ci-hadoop.apache.org/job/tez-multibranch/job/PR-544/2/console
versions git=2.43.0 maven=3.9.15 spotbugs=4.9.3 codespell=2.4.1
Powered by Apache Yetus 0.15.1 https://yetus.apache.org

This message was automatically generated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants