Reduce GC produced by AbstractByteBuf.indexOf(..) implementation (#9502)

Motivation:

AbstractByteBuf.indexOf(...) currently delegates to ByteBufUtils.indexOf(...) which will create a new ByteBufProcessor on each call. This is done to reduce overhead of bounds-checks. Unfortunally while this reduces bounds checks it produces a lot of GC. We can just implement our own version in AbstractByteBuf which makes use of _getByte(...) and so does no bound checks as well but also not need to create any garbage.

Modifications:

Write optimized implementation of indexOf(...) for AbstractByteBuf

Result:

Fixes https://github.com/netty/netty/issues/9499.
This commit is contained in:
Norman Maurer 2019-08-24 13:46:28 +02:00
parent 130522be84
commit 05dd1f05d5
2 changed files with 73 additions and 2 deletions

View File

@ -1209,7 +1209,44 @@ public abstract class AbstractByteBuf extends ByteBuf {
@Override
public int indexOf(int fromIndex, int toIndex, byte value) {
return ByteBufUtil.indexOf(this, fromIndex, toIndex, value);
if (fromIndex <= toIndex) {
return firstIndexOf(fromIndex, toIndex, value);
} else {
return lastIndexOf(fromIndex, toIndex, value);
}
}
private int firstIndexOf(int fromIndex, int toIndex, byte value) {
fromIndex = Math.max(fromIndex, 0);
if (fromIndex >= toIndex || capacity() == 0) {
return -1;
}
checkIndex(fromIndex, toIndex - fromIndex);
for (int i = fromIndex; i < toIndex; i ++) {
if (_getByte(i) == value) {
return i;
}
}
return -1;
}
private int lastIndexOf(int fromIndex, int toIndex, byte value) {
fromIndex = Math.min(fromIndex, capacity());
if (fromIndex < 0 || capacity() == 0) {
return -1;
}
checkIndex(toIndex, fromIndex - toIndex);
for (int i = fromIndex - 1; i >= toIndex; i --) {
if (_getByte(i) == value) {
return i;
}
}
return -1;
}
@Override

View File

@ -2117,6 +2117,9 @@ public abstract class AbstractByteBufTest {
@Test
public void testIndexOf() {
buffer.clear();
// Ensure the buffer is completely zero'ed.
buffer.setZero(0, buffer.capacity());
buffer.writeByte((byte) 1);
buffer.writeByte((byte) 2);
buffer.writeByte((byte) 3);
@ -2127,6 +2130,38 @@ public abstract class AbstractByteBufTest {
assertEquals(-1, buffer.indexOf(4, 1, (byte) 1));
assertEquals(1, buffer.indexOf(1, 4, (byte) 2));
assertEquals(3, buffer.indexOf(4, 1, (byte) 2));
try {
buffer.indexOf(0, buffer.capacity() + 1, (byte) 0);
fail();
} catch (IndexOutOfBoundsException expected) {
// expected
}
try {
buffer.indexOf(buffer.capacity(), -1, (byte) 0);
fail();
} catch (IndexOutOfBoundsException expected) {
// expected
}
assertEquals(4, buffer.indexOf(buffer.capacity() + 1, 0, (byte) 1));
assertEquals(0, buffer.indexOf(-1, buffer.capacity(), (byte) 1));
}
@Test
public void testIndexOfReleaseBuffer() {
ByteBuf buffer = releasedBuffer();
if (buffer.capacity() != 0) {
try {
buffer.indexOf(0, 1, (byte) 1);
fail();
} catch (IllegalReferenceCountException expected) {
// expected
}
} else {
assertEquals(-1, buffer.indexOf(0, 1, (byte) 1));
}
}
@Test
@ -2553,7 +2588,6 @@ public abstract class AbstractByteBufTest {
private ByteBuf releasedBuffer() {
ByteBuf buffer = newBuffer(8);
// Clear the buffer so we are sure the reader and writer indices are 0.
// This is important as we may return a slice from newBuffer(...).
buffer.clear();