From 9b95b8ee628983e3e4434da93fffb893edff4aa2 Mon Sep 17 00:00:00 2001 From: nickhill Date: Wed, 20 Jun 2018 00:13:09 -0700 Subject: [PATCH] Reduce array allocations during CompositeByteBuf construction Motivation: Eliminate avoidable backing array reallocations when constructing composite ByteBufs from existing buffer arrays/Iterables. This also applies to the Unpooled.wrappedBuffer(...) methods. Modifications: Ensure the initial components ComponentList is sized at least as large as the provided buffer array/Iterable in the CompositeByteBuffer constructors. In single-arg Unpooled.wrappedBuffer(...) methods, set maxNumComponents to the count of provided buffers, rather than a fixed default of 16. It seems likely that most usage of these involves wrapping a list without subsequent modification, particularly since they return a ByteBuf rather than CompositeByteBuf. If a different/larger max is required there are already the wrappedBuffer(int, ...) variants. In fact the current behaviour could be considered inconsistent - if you call Unpooled.wrappedBuffer(int, ByteBuf) with a single buffer, you might expect to subsequently be able to add buffers to it (since you specified a max related to consolidation), but it will in fact return just a slice of the provided ByteBuf. Result: Fewer and smaller allocations in some cases when using CompositeByteBufs or Unpooled.wrappedBuffer(...). --- .../main/java/io/netty/buffer/CompositeByteBuf.java | 13 ++++++++----- buffer/src/main/java/io/netty/buffer/Unpooled.java | 6 +++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 7afff7e7f1..bcd5f0a564 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -61,7 +61,7 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements this.alloc = alloc; this.direct = direct; this.maxNumComponents = maxNumComponents; - components = newList(maxNumComponents); + components = newList(0, maxNumComponents); } public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf... buffers) { @@ -82,7 +82,7 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements this.alloc = alloc; this.direct = direct; this.maxNumComponents = maxNumComponents; - components = newList(maxNumComponents); + components = newList(len, maxNumComponents); addComponents0(false, 0, buffers, offset, len); consolidateIfNeeded(); @@ -100,18 +100,21 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements "maxNumComponents: " + maxNumComponents + " (expected: >= 2)"); } + int len = buffers instanceof Collection ? ((Collection) buffers).size() : 0; + this.alloc = alloc; this.direct = direct; this.maxNumComponents = maxNumComponents; - components = newList(maxNumComponents); + components = newList(len, maxNumComponents); addComponents0(false, 0, buffers); consolidateIfNeeded(); setIndex(0, capacity()); } - private static ComponentList newList(int maxNumComponents) { - return new ComponentList(Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents)); + private static ComponentList newList(int initComponents, int maxNumComponents) { + int capacityGuess = Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents); + return new ComponentList(Math.max(initComponents, capacityGuess)); } // Special constructor used by WrappedCompositeByteBuf diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index 3639a17768..8755989782 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -238,7 +238,7 @@ public final class Unpooled { * content will be visible to the returned buffer. */ public static ByteBuf wrappedBuffer(byte[]... arrays) { - return wrappedBuffer(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, arrays); + return wrappedBuffer(arrays.length, arrays); } /** @@ -249,7 +249,7 @@ public final class Unpooled { * @return The readable portion of the {@code buffers}. The caller is responsible for releasing this buffer. */ public static ByteBuf wrappedBuffer(ByteBuf... buffers) { - return wrappedBuffer(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, buffers); + return wrappedBuffer(buffers.length, buffers); } /** @@ -258,7 +258,7 @@ public final class Unpooled { * specified buffers will be visible to the returned buffer. */ public static ByteBuf wrappedBuffer(ByteBuffer... buffers) { - return wrappedBuffer(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, buffers); + return wrappedBuffer(buffers.length, buffers); } /**