Copies from read-only heap ByteBuffer to direct ByteBuf can avoid stealth ByteBuf allocation and additional copies

Motivation:

Read-only heap ByteBuffer doesn't expose array: the existent method to perform copies to direct ByteBuf involves the creation of a (maybe pooled) additional heap ByteBuf instance and copy

Modifications:

To avoid stressing the allocator with additional (and stealth) heap ByteBuf allocations is provided a method to perform copies using the (pooled) internal NIO buffer

Result:

Copies from read-only heap ByteBuffer to direct ByteBuf won't create any intermediate ByteBuf
This commit is contained in:
Francesco Nigro 2018-02-21 07:49:52 +01:00 committed by Norman Maurer
parent a53f716ca1
commit ed46c4ed00
2 changed files with 127 additions and 11 deletions

View File

@ -538,34 +538,48 @@ final class UnsafeByteBufUtil {
}
static void setBytes(AbstractByteBuf buf, long addr, int index, ByteBuffer src) {
buf.checkIndex(index, src.remaining());
int length = src.remaining();
final int length = src.remaining();
if (length == 0) {
return;
}
if (src.isDirect()) {
buf.checkIndex(index, length);
// Copy from direct memory
long srcAddress = PlatformDependent.directBufferAddress(src);
PlatformDependent.copyMemory(srcAddress + src.position(), addr, src.remaining());
PlatformDependent.copyMemory(srcAddress + src.position(), addr, length);
src.position(src.position() + length);
} else if (src.hasArray()) {
buf.checkIndex(index, length);
// Copy from array
PlatformDependent.copyMemory(src.array(), src.arrayOffset() + src.position(), addr, length);
src.position(src.position() + length);
} else {
ByteBuf tmpBuf = buf.alloc().heapBuffer(length);
try {
byte[] tmp = tmpBuf.array();
src.get(tmp, tmpBuf.arrayOffset(), length); // moves the src position too
PlatformDependent.copyMemory(tmp, tmpBuf.arrayOffset(), addr, length);
} finally {
tmpBuf.release();
if (length < 8) {
setSingleBytes(buf, addr, index, src, length);
} else {
//no need to checkIndex: internalNioBuffer is already taking care of it
assert buf.nioBufferCount() == 1;
final ByteBuffer internalBuffer = buf.internalNioBuffer(index, length);
internalBuffer.put(src);
}
}
}
private static void setSingleBytes(final AbstractByteBuf buf, final long addr, final int index,
final ByteBuffer src, final int length) {
buf.checkIndex(index, length);
final int srcPosition = src.position();
final int srcLimit = src.limit();
long dstAddr = addr;
for (int srcIndex = srcPosition; srcIndex < srcLimit; srcIndex++) {
final byte value = src.get(srcIndex);
PlatformDependent.putByte(dstAddr, value);
dstAddr++;
}
src.position(srcLimit);
}
static void getBytes(AbstractByteBuf buf, long addr, int index, OutputStream out, int length) throws IOException {
buf.checkIndex(index, length);
if (length != 0) {

View File

@ -0,0 +1,102 @@
/*
* Copyright 2018 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.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.microbench.util.AbstractMicrobenchmark;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.TearDown;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class ByteBufCopyBenchmark extends AbstractMicrobenchmark {
static {
System.setProperty("io.netty.buffer.bytebuf.checkAccessible", "false");
}
@Param({"7", "36", "128", "512" })
private int size;
@Param({"true", "false" })
private boolean directByteBuff;
@Param({"true", "false" })
private boolean directByteBuffer;
@Param({"false", "true" })
private boolean readonlyByteBuffer;
@Param({"true", "false" })
private boolean pooledByteBuf;
@Param({"true", "false" })
private boolean alignedCopyByteBuffer;
@Param({"true", "false" })
private boolean alignedCopyByteBuf;
@Param({"true", "false" })
private boolean nativeOrderByteBuffer;
private ByteBuffer byteBuffer;
private ByteBuf buffer;
private int index;
@Setup
public void setup() {
final int requiredByteBufSize = alignedCopyByteBuf ? size : size + 1;
final int requiredByteBufferSize = alignedCopyByteBuffer ? size : size + 1;
byteBuffer = directByteBuffer ?
ByteBuffer.allocateDirect(requiredByteBufferSize) :
ByteBuffer.allocate(requiredByteBufferSize);
if (pooledByteBuf) {
buffer = directByteBuff ?
PooledByteBufAllocator.DEFAULT.directBuffer(requiredByteBufSize, requiredByteBufSize) :
PooledByteBufAllocator.DEFAULT.heapBuffer(requiredByteBufSize, requiredByteBufSize);
} else {
buffer = directByteBuff ?
Unpooled.directBuffer(requiredByteBufSize, requiredByteBufSize) :
Unpooled.buffer(requiredByteBufSize, requiredByteBufSize);
}
if (!alignedCopyByteBuffer) {
byteBuffer.position(1);
byteBuffer = byteBuffer.slice();
}
if (readonlyByteBuffer) {
byteBuffer = byteBuffer.asReadOnlyBuffer();
}
final ByteOrder byteBufferOrder;
if (!nativeOrderByteBuffer) {
byteBufferOrder = ByteOrder.LITTLE_ENDIAN == ByteOrder.nativeOrder() ?
ByteOrder.BIG_ENDIAN :
ByteOrder.LITTLE_ENDIAN;
} else {
byteBufferOrder = ByteOrder.nativeOrder();
}
byteBuffer.order(byteBufferOrder);
index = alignedCopyByteBuf ? 0 : 1;
}
@Benchmark
public ByteBuf setBytes() {
byteBuffer.clear();
return buffer.setBytes(index, byteBuffer);
}
@TearDown
public void tearDown() {
buffer.release();
}
}