Skip to content

Commit

Permalink
Support composite buffer creation without array alloc and copy
Browse files Browse the repository at this point in the history
Motivation:

Unpooled.unmodifiableBuffer() is currently used to efficiently write
arrays of ByteBufs via FixedCompositeByteBuf, but involves an allocation
and content-copy of the provided ByteBuf array which in many (most?)
cases shouldn't be necessary.

Modifications:

Modify the internal FixedCompositeByteBuf class to support wrapping the
provided ByteBuf array directly. Control this behaviour with a
constructor flag and expose the "unsafe" version via a new
Unpooled.wrappedUnmodifiableBuffer(ByteBuf...) method.

Result:

Less garbage on IO paths. I would guess pretty much all existing usage
of unmodifiableBuffer() could use the copy-free version but assume it's
not safe to change its default behaviour.
  • Loading branch information
njhill authored and normanmaurer committed Jun 27, 2018
1 parent 06f3574 commit f164759
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 23 deletions.
32 changes: 10 additions & 22 deletions buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ final class FixedCompositeByteBuf extends AbstractReferenceCountedByteBuf {
private final int capacity;
private final ByteBufAllocator allocator;
private final ByteOrder order;
private final Object[] buffers;
private final ByteBuf[] buffers;
private final boolean direct;

FixedCompositeByteBuf(ByteBufAllocator allocator, ByteBuf... buffers) {
Expand All @@ -52,8 +52,7 @@ final class FixedCompositeByteBuf extends AbstractReferenceCountedByteBuf {
direct = false;
} else {
ByteBuf b = buffers[0];
this.buffers = new Object[buffers.length];
this.buffers[0] = b;
this.buffers = buffers;
boolean direct = true;
int nioBufferCount = b.nioBufferCount();
int capacity = b.readableBytes();
Expand All @@ -68,7 +67,6 @@ final class FixedCompositeByteBuf extends AbstractReferenceCountedByteBuf {
if (!b.isDirect()) {
direct = false;
}
this.buffers[i] = b;
}
this.nioBufferCount = nioBufferCount;
this.capacity = capacity;
Expand Down Expand Up @@ -232,20 +230,14 @@ private Component findComponent(int index) {
int readable = 0;
for (int i = 0 ; i < buffers.length; i++) {
Component comp = null;
ByteBuf b;
Object obj = buffers[i];
boolean isBuffer;
if (obj instanceof ByteBuf) {
b = (ByteBuf) obj;
isBuffer = true;
} else {
comp = (Component) obj;
ByteBuf b = buffers[i];
if (b instanceof Component) {
comp = (Component) b;
b = comp.buf;
isBuffer = false;
}
readable += b.readableBytes();
if (index < readable) {
if (isBuffer) {
if (comp == null) {
// Create a new component and store it in the array so it not create a new object
// on the next access.
comp = new Component(i, readable - b.readableBytes(), b);
Expand All @@ -261,11 +253,8 @@ private Component findComponent(int index) {
* Return the {@link ByteBuf} stored at the given index of the array.
*/
private ByteBuf buffer(int i) {
Object obj = buffers[i];
if (obj instanceof ByteBuf) {
return (ByteBuf) obj;
}
return ((Component) obj).buf;
ByteBuf b = buffers[i];
return b instanceof Component ? ((Component) b).buf : b;
}

@Override
Expand Down Expand Up @@ -684,17 +673,16 @@ public String toString() {
return result + ", components=" + buffers.length + ')';
}

private static final class Component {
private static final class Component extends WrappedByteBuf {
private final int index;
private final int offset;
private final ByteBuf buf;
private final int endOffset;

Component(int index, int offset, ByteBuf buf) {
super(buf);
this.index = index;
this.offset = offset;
endOffset = offset + buf.readableBytes();
this.buf = buf;
}
}
}
27 changes: 26 additions & 1 deletion buffer/src/main/java/io/netty/buffer/Unpooled.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


Expand Down Expand Up @@ -883,7 +884,31 @@ public static ByteBuf unreleasableBuffer(ByteBuf buf) {
* not try to slice the given {@link ByteBuf}s to reduce GC-Pressure.
*/
public static ByteBuf unmodifiableBuffer(ByteBuf... buffers) {
return new FixedCompositeByteBuf(ALLOC, buffers);
return wrappedUnmodifiableBuffer(true, buffers);
}

/**
* Wrap the given {@link ByteBuf}s in an unmodifiable {@link ByteBuf}. Be aware the returned {@link ByteBuf} will
* not try to slice the given {@link ByteBuf}s to reduce GC-Pressure.
*
* The returned {@link ByteBuf} wraps the provided array directly, and so should not be subsequently modified.
*/
public static ByteBuf wrappedUnmodifiableBuffer(ByteBuf... buffers) {
return wrappedUnmodifiableBuffer(false, buffers);
}

private static ByteBuf wrappedUnmodifiableBuffer(boolean copy, ByteBuf... buffers) {
switch (buffers.length) {
case 0:
return EMPTY_BUFFER;
case 1:
return buffers[0].asReadOnly();
default:
if (copy) {
buffers = Arrays.copyOf(buffers, buffers.length, ByteBuf[].class);
}
return new FixedCompositeByteBuf(ALLOC, buffers);
}
}

private Unpooled() {
Expand Down

0 comments on commit f164759

Please sign in to comment.