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:
parent
14e856ac72
commit
da2aba5742
@ -1263,7 +1263,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
|
||||
|
@ -2125,6 +2125,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);
|
||||
@ -2135,6 +2138,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
|
||||
@ -2570,7 +2605,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();
|
||||
|
Loading…
Reference in New Issue
Block a user