Remove memory copy when checksum non heap backed ByteBuf implementations using Snappy

Motivation:

We should try to minimize memory copies whenever possible.

Modifications:

- Refactor ByteBufChecksum to work with heap and direct ByteBuf always
- Remove memory copy in Snappy by let Crc32c extend ByteBufChecksum

Result:

Less memory copies when using Snappy
This commit is contained in:
Norman Maurer 2016-07-19 14:21:47 +02:00
parent 9151739577
commit 87551fc751
4 changed files with 46 additions and 78 deletions

View File

@ -16,6 +16,7 @@
package io.netty.handler.codec.compression;
import io.netty.buffer.ByteBuf;
import io.netty.util.ByteProcessor;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.PlatformDependent;
@ -42,6 +43,14 @@ abstract class ByteBufChecksum implements Checksum {
CRC32_UPDATE_METHOD = updateByteBuffer(new CRC32());
}
private final ByteProcessor updateProcessor = new ByteProcessor() {
@Override
public boolean process(byte value) throws Exception {
update(value);
return true;
}
};
private static Method updateByteBuffer(Checksum checksum) {
if (PlatformDependent.javaVersion() >= 8) {
try {
@ -55,42 +64,6 @@ abstract class ByteBufChecksum implements Checksum {
return null;
}
protected Checksum checksum;
private ByteBufChecksum(Checksum checksum) {
this.checksum = checksum;
}
@Override
public void update(int b) {
checksum.update(b);
}
/**
* @see {@link #update(byte[], int, int)}.
*/
abstract void update(ByteBuf b, int off, int len);
/**
* Returns {@code true} if {@link ByteBuffer} is supported without memory copy.
*/
abstract boolean isSupportingByteBuffer();
@Override
public void update(byte[] b, int off, int len) {
checksum.update(b, off, len);
}
@Override
public long getValue() {
return checksum.getValue();
}
@Override
public void reset() {
checksum.reset();
}
static ByteBufChecksum wrapChecksum(Checksum checksum) {
ObjectUtil.checkNotNull(checksum, "checksum");
if (checksum instanceof Adler32 && ADLER32_UPDATE_METHOD != null) {
@ -102,7 +75,18 @@ abstract class ByteBufChecksum implements Checksum {
return new SlowByteBufChecksum(checksum);
}
private static final class ReflectiveByteBufChecksum extends ByteBufChecksum {
/**
* @see {@link #update(byte[], int, int)}.
*/
public void update(ByteBuf b, int off, int len) {
if (b.hasArray()) {
update(b.array(), b.arrayOffset() + off, len);
} else {
b.forEachByte(off, len, updateProcessor);
}
}
private static final class ReflectiveByteBufChecksum extends SlowByteBufChecksum {
private final Method method;
ReflectiveByteBufChecksum(Checksum checksum, Method method) {
@ -111,7 +95,7 @@ abstract class ByteBufChecksum implements Checksum {
}
@Override
void update(ByteBuf b, int off, int len) {
public void update(ByteBuf b, int off, int len) {
if (b.hasArray()) {
update(b.array(), b.arrayOffset() + off, len);
} else {
@ -122,37 +106,34 @@ abstract class ByteBufChecksum implements Checksum {
}
}
}
@Override
boolean isSupportingByteBuffer() {
return true;
}
}
private static final class SlowByteBufChecksum extends ByteBufChecksum {
private static class SlowByteBufChecksum extends ByteBufChecksum {
protected final Checksum checksum;
SlowByteBufChecksum(Checksum checksum) {
super(checksum);
this.checksum = checksum;
}
@Override
void update(ByteBuf b, int off, int len) {
if (b.hasArray()) {
update(b.array(), b.arrayOffset() + off, len);
} else {
ByteBuf heapBuffer = b.alloc().heapBuffer(len);
try {
heapBuffer.writeBytes(b, off, len);
update(heapBuffer.array(), heapBuffer.arrayOffset(), len);
} finally {
heapBuffer.release();
}
}
public void update(int b) {
checksum.update(b);
}
@Override
boolean isSupportingByteBuffer() {
return false;
public void update(byte[] b, int off, int len) {
checksum.update(b, off, len);
}
@Override
public long getValue() {
return checksum.getValue();
}
@Override
public void reset() {
checksum.reset();
}
}
}

View File

@ -15,8 +15,6 @@
*/
package io.netty.handler.codec.compression;
import java.util.zip.Checksum;
/**
* Implements CRC32-C as defined in:
* "Optimization of Cyclic Redundancy-CHeck Codes with 24 and 32 Parity Bits",
@ -25,7 +23,7 @@ import java.util.zip.Checksum;
* The implementation of this class has been sourced from the Appendix of RFC 3309,
* but with masking due to Java not being able to support unsigned types.
*/
class Crc32c implements Checksum {
class Crc32c extends ByteBufChecksum {
private static final int[] CRC_TABLE = {
0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
@ -105,8 +103,9 @@ class Crc32c implements Checksum {
@Override
public void update(byte[] buffer, int offset, int length) {
for (int i = offset; i < offset + length; i++) {
crc = crc32c(crc, buffer[i]);
int end = offset + length;
for (int i = offset; i < end; i++) {
update(buffer[i]);
}
}

View File

@ -223,12 +223,7 @@ public class Lz4FrameDecoder extends ByteToMessageDecoder {
uncompressed = in.retainedSlice(in.readerIndex(), decompressedLength);
break;
case BLOCK_TYPE_COMPRESSED:
uncompressed = checksum == null || checksum.isSupportingByteBuffer()
// We can allocate whatever buffer if we either not need to do checksum processing
// or if our ByteBufChecksum implementation supports ByteBuffer.
// This is needed as Checksum implementations itself only support byte[].
? ctx.alloc().buffer(decompressedLength, decompressedLength)
: ctx.alloc().heapBuffer(decompressedLength, decompressedLength);
uncompressed = ctx.alloc().buffer(decompressedLength, decompressedLength);
decompressor.decompress(CompressionUtil.safeNioBuffer(in),
uncompressed.internalNioBuffer(uncompressed.writerIndex(), decompressedLength));

View File

@ -611,14 +611,7 @@ public final class Snappy {
static int calculateChecksum(ByteBuf data, int offset, int length) {
Crc32c crc32 = new Crc32c();
try {
if (data.hasArray()) {
crc32.update(data.array(), data.arrayOffset() + offset, length);
} else {
byte[] array = new byte[length];
data.getBytes(offset, array);
crc32.update(array, 0, length);
}
crc32.update(data, offset, length);
return maskChecksum((int) crc32.getValue());
} finally {
crc32.reset();