Eliminate AsciiString's resize-copy after exact allocation

perfloop/netty · AVOIDABLE COPY

https://perfloop.ai/t/oss/case_dyqr11pchm

Verdict

VERIFIED · settled 2026-08-04

What happened: The paired measurements met the required improvement.

Hypothesis

I ran a source-level value-flow check, not a runtime profile: `ByteBufUtil` lines 829-832 allocate `utf8MaxBytes(seq)`; lines 1154-1158 make that exactly n for `AsciiString`; but lines 844-846 recompute the reservation from the integer length as 3n. The traced write path calls `ensureWritable0` before encoding, while the `AsciiString` encoder writes only n bytes (lines 882-892 and 911-914). `AbstractByteBuf.ensureWritable0` grows when capacity is below the requested reservation (lines 284-305). For the standard unpooled heap implementation, `UnpooledHeapByteBuf.capacity` allocates a new array and `System.arraycopy`s the entire old capacity on growth (lines 118-137), so an empty newly allocated n-byte buffer can copy n bytes solely because the later reservation is 3n. This establishes the conditional mechanism, but not its production frequency, allocator mix, or CPU share. Reproduce with a focused benchmark using non-empty `AsciiString` values across 1 KiB to 64 KiB and an unpooled heap allocator plus the deployed allocator: compare current code with the carried-reserve variant, and require allocator/JFR allocation evidence that the baseline enters capacity growth and copies n bytes while the variant does not, then measure allocation bytes/op and encoding latency at the observed call cadence.

Change to test: Carry the initial `utf8MaxBytes(seq)` result into `reserveAndWriteUtf8` instead of calling the overload that recomputes a 3x reservation; retain the existing 3x reserve for non-`AsciiString` inputs and verify returned-buffer compatibility.

Where it lives

perfloop/netty · buffer/src/main/java/io/netty/buffer/ByteBufUtil.java

Evidence

AsciiString UTF-8 write plus three equal appends, unpooled heap, 1 KiB · 10 sample pairs

metric baseline candidate paired median change confidence range required result
ns/op 749.8 563.9 −24.4% (−183) −199.1 to −109 < 0 PASSED
B/op 5216 4176 −19.9% (−1040) −1040 to −1040 < 0 PASSED

AsciiString UTF-8 write plus three equal appends, unpooled heap, 4 KiB · 10 sample pairs

metric baseline candidate paired median change confidence range required result
B/op 20576 16464 −20% (−4112) −4112 to −4112 < 0 PASSED

AsciiString UTF-8 write plus three equal appends, unpooled heap, 16 KiB · 10 sample pairs

metric baseline candidate paired median change confidence range required result
B/op 82016 65616 −20% (−16400) −16400 to −16400 < 0 PASSED

AsciiString UTF-8 write plus three equal appends, unpooled heap, 64 KiB · 10 sample pairs

metric baseline candidate paired median change confidence range required result
ns/op 65776 51562 −21.4% (−14054) −16260 to −12667 < 0 PASSED
B/op 327776 262224 −20% (−65552) −65552 to −65552 < 0 PASSED

AsciiString UTF-8 write, default adaptive allocator, 1 KiB · 10 sample pairs

metric baseline candidate paired median change confidence range required result
ns/op 178.8 106.7 −39.6% (−70.76) −78.84 to −65.2 < 0 PASSED

AsciiString UTF-8 write, default adaptive allocator, 4 KiB · 10 sample pairs

metric baseline candidate paired median change confidence range required result
ns/op 294.4 181.5 −36.4% (−107.1) −130.8 to −98.67 < 0 PASSED

AsciiString UTF-8 write, default adaptive allocator, 16 KiB · 10 sample pairs

metric baseline candidate paired median change confidence range required result
ns/op 1018 517.5 −49.7% (−505.7) −554.7 to −484.8 < 0 PASSED

AsciiString UTF-8 write, default adaptive allocator, 64 KiB · 10 sample pairs

metric baseline candidate paired median change confidence range required result
ns/op 3531 1853 −49% (−1730) −1912 to −1565 < 0 PASSED

Checks: 1 of 1 passed. Verification: no defect found.

Timeline