From 4c287d4e27179997822bf490946382d073120a88 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 8 Oct 2015 14:49:31 +0200 Subject: [PATCH] Added SlicedAbstractByteBuf that can provide fast-path for _get* and _set* methods Motivation: SlicedByteBuf can be used for any ByteBuf implementations and so can not do any optimizations that could be done when AbstractByteBuf is sliced. Modifications: - Add SlicedAbstractByteBuf that can eliminate range and reference count checks for _get* and _set* methods. Result: Faster SlicedByteBuf implementations for AbstractByteBuf sub-classes. --- .../java/io/netty/buffer/AbstractByteBuf.java | 2 +- .../netty/buffer/SlicedAbstractByteBuf.java | 81 +++++++++++++++++++ .../java/io/netty/buffer/SlicedByteBuf.java | 61 +++++++------- .../buffer/SlicedByteBufBenchmark.java | 76 +++++++++++++++++ 4 files changed, 192 insertions(+), 28 deletions(-) create mode 100644 buffer/src/main/java/io/netty/buffer/SlicedAbstractByteBuf.java create mode 100644 microbench/src/test/java/io/netty/microbench/buffer/SlicedByteBufBenchmark.java diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 91a9c9f66f..fb9ad6dba7 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -927,7 +927,7 @@ public abstract class AbstractByteBuf extends ByteBuf { @Override public ByteBuf slice(int index, int length) { - return new SlicedByteBuf(this, index, length); + return new SlicedAbstractByteBuf(this, index, length); } @Override diff --git a/buffer/src/main/java/io/netty/buffer/SlicedAbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/SlicedAbstractByteBuf.java new file mode 100644 index 0000000000..8ff68ff675 --- /dev/null +++ b/buffer/src/main/java/io/netty/buffer/SlicedAbstractByteBuf.java @@ -0,0 +1,81 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.buffer; + +/** + * A special {@link SlicedByteBuf} that can make optimizations because it knows the sliced buffer is of type + * {@link AbstractByteBuf}. + */ +final class SlicedAbstractByteBuf extends SlicedByteBuf { + + SlicedAbstractByteBuf(AbstractByteBuf buffer, int index, int length) { + super(buffer, index, length); + } + + @Override + protected byte _getByte(int index) { + return unwrap0()._getByte(idx(index)); + } + + @Override + protected short _getShort(int index) { + return unwrap0()._getShort(idx(index)); + } + + @Override + protected int _getUnsignedMedium(int index) { + return unwrap0()._getUnsignedMedium(idx(index)); + } + + @Override + protected int _getInt(int index) { + return unwrap0()._getInt(idx(index)); + } + + @Override + protected long _getLong(int index) { + return unwrap0()._getLong(idx(index)); + } + + @Override + protected void _setByte(int index, int value) { + unwrap0()._setByte(idx(index), value); + } + + @Override + protected void _setShort(int index, int value) { + unwrap0()._setShort(idx(index), value); + } + + @Override + protected void _setMedium(int index, int value) { + unwrap0()._setMedium(idx(index), value); + } + + @Override + protected void _setInt(int index, int value) { + unwrap0()._setInt(idx(index), value); + } + + @Override + protected void _setLong(int index, long value) { + unwrap0()._setLong(idx(index), value); + } + + private AbstractByteBuf unwrap0() { + return (AbstractByteBuf) unwrap(); + } +} diff --git a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java index 7683e13bc2..73d1eb0716 100644 --- a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java @@ -101,7 +101,7 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { @Override public int arrayOffset() { - return buffer.arrayOffset() + adjustment; + return idx(buffer.arrayOffset()); } @Override @@ -116,27 +116,27 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { @Override protected byte _getByte(int index) { - return buffer.getByte(index + adjustment); + return buffer.getByte(idx(index)); } @Override protected short _getShort(int index) { - return buffer.getShort(index + adjustment); + return buffer.getShort(idx(index)); } @Override protected int _getUnsignedMedium(int index) { - return buffer.getUnsignedMedium(index + adjustment); + return buffer.getUnsignedMedium(idx(index)); } @Override protected int _getInt(int index) { - return buffer.getInt(index + adjustment); + return buffer.getInt(idx(index)); } @Override protected long _getLong(int index) { - return buffer.getLong(index + adjustment); + return buffer.getLong(idx(index)); } @Override @@ -149,7 +149,7 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { @Override public ByteBuf copy(int index, int length) { checkIndex(index, length); - return buffer.copy(index + adjustment, length); + return buffer.copy(idx(index), length); } @Override @@ -158,99 +158,99 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { if (length == 0) { return Unpooled.EMPTY_BUFFER; } - return buffer.slice(index + adjustment, length); + return buffer.slice(idx(index), length); } @Override public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) { checkIndex(index, length); - buffer.getBytes(index + adjustment, dst, dstIndex, length); + buffer.getBytes(idx(index), dst, dstIndex, length); return this; } @Override public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) { checkIndex(index, length); - buffer.getBytes(index + adjustment, dst, dstIndex, length); + buffer.getBytes(idx(index), dst, dstIndex, length); return this; } @Override public ByteBuf getBytes(int index, ByteBuffer dst) { checkIndex(index, dst.remaining()); - buffer.getBytes(index + adjustment, dst); + buffer.getBytes(idx(index), dst); return this; } @Override protected void _setByte(int index, int value) { - buffer.setByte(index + adjustment, value); + buffer.setByte(idx(index), value); } @Override protected void _setShort(int index, int value) { - buffer.setShort(index + adjustment, value); + buffer.setShort(idx(index), value); } @Override protected void _setMedium(int index, int value) { - buffer.setMedium(index + adjustment, value); + buffer.setMedium(idx(index), value); } @Override protected void _setInt(int index, int value) { - buffer.setInt(index + adjustment, value); + buffer.setInt(idx(index), value); } @Override protected void _setLong(int index, long value) { - buffer.setLong(index + adjustment, value); + buffer.setLong(idx(index), value); } @Override public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) { checkIndex(index, length); - buffer.setBytes(index + adjustment, src, srcIndex, length); + buffer.setBytes(idx(index), src, srcIndex, length); return this; } @Override public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) { checkIndex(index, length); - buffer.setBytes(index + adjustment, src, srcIndex, length); + buffer.setBytes(idx(index), src, srcIndex, length); return this; } @Override public ByteBuf setBytes(int index, ByteBuffer src) { checkIndex(index, src.remaining()); - buffer.setBytes(index + adjustment, src); + buffer.setBytes(idx(index), src); return this; } @Override public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException { checkIndex(index, length); - buffer.getBytes(index + adjustment, out, length); + buffer.getBytes(idx(index), out, length); return this; } @Override public int getBytes(int index, GatheringByteChannel out, int length) throws IOException { checkIndex(index, length); - return buffer.getBytes(index + adjustment, out, length); + return buffer.getBytes(idx(index), out, length); } @Override public int setBytes(int index, InputStream in, int length) throws IOException { checkIndex(index, length); - return buffer.setBytes(index + adjustment, in, length); + return buffer.setBytes(idx(index), in, length); } @Override public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException { checkIndex(index, length); - return buffer.setBytes(index + adjustment, in, length); + return buffer.setBytes(idx(index), in, length); } @Override @@ -261,13 +261,13 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { @Override public ByteBuffer nioBuffer(int index, int length) { checkIndex(index, length); - return buffer.nioBuffer(index + adjustment, length); + return buffer.nioBuffer(idx(index), length); } @Override public ByteBuffer[] nioBuffers(int index, int length) { checkIndex(index, length); - return buffer.nioBuffers(index + adjustment, length); + return buffer.nioBuffers(idx(index), length); } @Override @@ -278,7 +278,7 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { @Override public int forEachByte(int index, int length, ByteProcessor processor) { - int ret = buffer.forEachByte(index + adjustment, length, processor); + int ret = buffer.forEachByte(idx(index), length, processor); if (ret >= adjustment) { return ret - adjustment; } else { @@ -288,11 +288,18 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { @Override public int forEachByteDesc(int index, int length, ByteProcessor processor) { - int ret = buffer.forEachByteDesc(index + adjustment, length, processor); + int ret = buffer.forEachByteDesc(idx(index), length, processor); if (ret >= adjustment) { return ret - adjustment; } else { return -1; } } + + /** + * Returns the index with the needed adjustment. + */ + final int idx(int index) { + return index + adjustment; + } } diff --git a/microbench/src/test/java/io/netty/microbench/buffer/SlicedByteBufBenchmark.java b/microbench/src/test/java/io/netty/microbench/buffer/SlicedByteBufBenchmark.java new file mode 100644 index 0000000000..bcc83dc6ed --- /dev/null +++ b/microbench/src/test/java/io/netty/microbench/buffer/SlicedByteBufBenchmark.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.microbench.buffer; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.SlicedByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.microbench.util.AbstractMicrobenchmark; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + + +@State(Scope.Benchmark) +@Warmup(iterations = 10) +@Measurement(iterations = 25) +public class SlicedByteBufBenchmark extends AbstractMicrobenchmark { + + private ByteBuf slicedByteBuf; + private ByteBuf slicedAbstractByteBuf; + private String ascii; + + @Setup + public void setup() { + // Use buffer sizes that will also allow to write UTF-8 without grow the buffer + ByteBuf buffer = Unpooled.buffer(512).retain(); + slicedByteBuf = new SlicedByteBuf(buffer, 0, 256); + slicedAbstractByteBuf = buffer.slice(0, 256); + + if (slicedByteBuf.getClass() == slicedAbstractByteBuf.getClass()) { + throw new IllegalStateException(); + } + + StringBuilder asciiSequence = new StringBuilder(128); + for (int i = 0; i < 128; i++) { + asciiSequence.append('a'); + } + ascii = asciiSequence.toString(); + } + + @TearDown + public void tearDown() { + slicedByteBuf.release(); + slicedAbstractByteBuf.release(); + } + + @Benchmark + public void writeAsciiStringSlice() { + slicedByteBuf.resetWriterIndex(); + ByteBufUtil.writeAscii(slicedByteBuf, ascii); + } + + @Benchmark + public void writeAsciiStringSliceAbstract() { + slicedAbstractByteBuf.resetWriterIndex(); + ByteBufUtil.writeAscii(slicedAbstractByteBuf, ascii); + } +}