Return the new buffer's capacity is same with the requested capacity

- Rename capacity variables to reqCapacity or normCapacity to distinguish if its the request capacity or the normalized capacity
- Do not reallocate on ByteBuf.capacity(int) if reallocation is unnecessary; just update the index range.
- Revert the workaround in DefaultChannelHandlerContext
This commit is contained in:
Trustin Lee 2012-12-19 16:48:53 +09:00
parent b6e83dff4f
commit 0e017db89a
5 changed files with 86 additions and 57 deletions

View File

@ -83,23 +83,23 @@ abstract class PoolArena<T> {
return new Deque[size]; return new Deque[size];
} }
PooledByteBuf<T> allocate(PoolThreadCache cache, int minCapacity, int maxCapacity) { PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
PooledByteBuf<T> buf = newByteBuf(maxCapacity); PooledByteBuf<T> buf = newByteBuf(maxCapacity);
allocate(cache, buf, minCapacity); allocate(cache, buf, reqCapacity);
return buf; return buf;
} }
private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, int minCapacity) { private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
final int capacity = normalizeCapacity(minCapacity); final int normCapacity = normalizeCapacity(reqCapacity);
if ((capacity & subpageOverflowMask) == 0) { // capacity < pageSize if ((normCapacity & subpageOverflowMask) == 0) { // capacity < pageSize
int tableIdx; int tableIdx;
Deque<PoolSubpage<T>>[] table; Deque<PoolSubpage<T>>[] table;
if ((capacity & 0xFFFFFE00) == 0) { // < 512 if ((normCapacity & 0xFFFFFE00) == 0) { // < 512
tableIdx = capacity >>> 4; tableIdx = normCapacity >>> 4;
table = tinySubpagePools; table = tinySubpagePools;
} else { } else {
tableIdx = 0; tableIdx = 0;
int i = capacity >>> 10; int i = normCapacity >>> 10;
while (i != 0) { while (i != 0) {
i >>>= 1; i >>>= 1;
tableIdx ++; tableIdx ++;
@ -115,7 +115,7 @@ abstract class PoolArena<T> {
break; break;
} }
if (!s.doNotDestroy || s.elemSize != capacity) { if (!s.doNotDestroy || s.elemSize != normCapacity) {
// The subpage has been destroyed or being used for different element size. // The subpage has been destroyed or being used for different element size.
subpages.removeFirst(); subpages.removeFirst();
continue; continue;
@ -125,28 +125,28 @@ abstract class PoolArena<T> {
if (handle < 0) { if (handle < 0) {
subpages.removeFirst(); subpages.removeFirst();
} else { } else {
s.chunk.initBufWithSubpage(buf, handle); s.chunk.initBufWithSubpage(buf, handle, reqCapacity);
return; return;
} }
} }
} }
} }
allocateNormal(buf, capacity); allocateNormal(buf, reqCapacity, normCapacity);
} }
private synchronized void allocateNormal(PooledByteBuf<T> buf, int capacity) { private synchronized void allocateNormal(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {
if (q050.allocate(buf, capacity) || q025.allocate(buf, capacity) || if (q050.allocate(buf, reqCapacity, normCapacity) || q025.allocate(buf, reqCapacity, normCapacity) ||
q000.allocate(buf, capacity) || qInit.allocate(buf, capacity) || q000.allocate(buf, reqCapacity, normCapacity) || qInit.allocate(buf, reqCapacity, normCapacity) ||
q075.allocate(buf, capacity)) { q075.allocate(buf, reqCapacity, normCapacity)) {
return; return;
} }
// Add a new chunk. // Add a new chunk.
PoolChunk<T> c = newChunk(pageSize, maxOrder, pageShifts, chunkSize); PoolChunk<T> c = newChunk(pageSize, maxOrder, pageShifts, chunkSize);
long handle = c.allocate(capacity); long handle = c.allocate(normCapacity);
assert handle > 0; assert handle > 0;
c.initBuf(buf, handle); c.initBuf(buf, handle, reqCapacity);
qInit.add(c); qInit.add(c);
} }
@ -174,26 +174,26 @@ abstract class PoolArena<T> {
table[tableIdx].addFirst(subpage); table[tableIdx].addFirst(subpage);
} }
private int normalizeCapacity(int capacity) { private int normalizeCapacity(int reqCapacity) {
if (capacity < 0 || capacity > chunkSize) { if (reqCapacity < 0 || reqCapacity > chunkSize) {
throw new IllegalArgumentException("capacity: " + capacity + " (expected: 0-" + chunkSize + ')'); throw new IllegalArgumentException("capacity: " + reqCapacity + " (expected: 0-" + chunkSize + ')');
} }
if ((capacity & 0xFFFFFE00) != 0) { // >= 512 if ((reqCapacity & 0xFFFFFE00) != 0) { // >= 512
// Doubled // Doubled
int normalizedCapacity = 512; int normalizedCapacity = 512;
while (normalizedCapacity < capacity) { while (normalizedCapacity < reqCapacity) {
normalizedCapacity <<= 1; normalizedCapacity <<= 1;
} }
return normalizedCapacity; return normalizedCapacity;
} }
// Quantum-spaced // Quantum-spaced
if ((capacity & 15) == 0) { if ((reqCapacity & 15) == 0) {
return capacity; return reqCapacity;
} }
return (capacity & ~15) + 16; return (reqCapacity & ~15) + 16;
} }
void reallocate(PooledByteBuf<T> buf, int newCapacity, boolean freeOldMemory) { void reallocate(PooledByteBuf<T> buf, int newCapacity, boolean freeOldMemory) {

View File

@ -93,16 +93,16 @@ final class PoolChunk<T> {
return 100 - freePercentage; return 100 - freePercentage;
} }
long allocate(int capacity) { long allocate(int normCapacity) {
int firstVal = memoryMap[1]; int firstVal = memoryMap[1];
if ((capacity & subpageOverflowMask) != 0) { // >= pageSize if ((normCapacity & subpageOverflowMask) != 0) { // >= pageSize
return allocateRun(capacity, 1, firstVal); return allocateRun(normCapacity, 1, firstVal);
} else { } else {
return allocateSubpage(capacity, 1, firstVal); return allocateSubpage(normCapacity, 1, firstVal);
} }
} }
private long allocateRun(int capacity, int curIdx, int val) { private long allocateRun(int normCapacity, int curIdx, int val) {
for (;;) { for (;;) {
if ((val & ST_ALLOCATED) != 0) { // state == ST_ALLOCATED || state == ST_ALLOCATED_SUBPAGE if ((val & ST_ALLOCATED) != 0) { // state == ST_ALLOCATED || state == ST_ALLOCATED_SUBPAGE
return -1; return -1;
@ -110,7 +110,7 @@ final class PoolChunk<T> {
if ((val & ST_BRANCH) != 0) { // state == ST_BRANCH if ((val & ST_BRANCH) != 0) { // state == ST_BRANCH
int nextIdx = curIdx << 1 ^ nextRandom(); int nextIdx = curIdx << 1 ^ nextRandom();
long res = allocateRun(capacity, nextIdx, memoryMap[nextIdx]); long res = allocateRun(normCapacity, nextIdx, memoryMap[nextIdx]);
if (res > 0) { if (res > 0) {
return res; return res;
} }
@ -121,18 +121,18 @@ final class PoolChunk<T> {
} }
// state == ST_UNUSED // state == ST_UNUSED
return allocateRunSimple(capacity, curIdx, val); return allocateRunSimple(normCapacity, curIdx, val);
} }
} }
private long allocateRunSimple(int capacity, int curIdx, int val) { private long allocateRunSimple(int normCapacity, int curIdx, int val) {
int runLength = runLength(val); int runLength = runLength(val);
if (capacity > runLength) { if (normCapacity > runLength) {
return -1; return -1;
} }
for (;;) { for (;;) {
if (capacity == runLength) { if (normCapacity == runLength) {
// Found the run that fits. // Found the run that fits.
// Note that capacity has been normalized already, so we don't need to deal with // Note that capacity has been normalized already, so we don't need to deal with
// the values that are not power of 2. // the values that are not power of 2.
@ -154,26 +154,26 @@ final class PoolChunk<T> {
} }
} }
private long allocateSubpage(int capacity, int curIdx, int val) { private long allocateSubpage(int normCapacity, int curIdx, int val) {
int state = val & 3; int state = val & 3;
if (state == ST_BRANCH) { if (state == ST_BRANCH) {
int nextIdx = curIdx << 1 ^ nextRandom(); int nextIdx = curIdx << 1 ^ nextRandom();
long res = branchSubpage(capacity, nextIdx); long res = branchSubpage(normCapacity, nextIdx);
if (res > 0) { if (res > 0) {
return res; return res;
} }
return branchSubpage(capacity, nextIdx ^ 1); return branchSubpage(normCapacity, nextIdx ^ 1);
} }
if (state == ST_UNUSED) { if (state == ST_UNUSED) {
return allocateSubpageSimple(capacity, curIdx, val); return allocateSubpageSimple(normCapacity, curIdx, val);
} }
if (state == ST_ALLOCATED_SUBPAGE) { if (state == ST_ALLOCATED_SUBPAGE) {
PoolSubpage<T> subpage = subpages[subpageIdx(curIdx)]; PoolSubpage<T> subpage = subpages[subpageIdx(curIdx)];
int elemSize = subpage.elemSize; int elemSize = subpage.elemSize;
if (capacity != elemSize) { if (normCapacity != elemSize) {
return -1; return -1;
} }
@ -183,7 +183,7 @@ final class PoolChunk<T> {
return -1; return -1;
} }
private long allocateSubpageSimple(int capacity, int curIdx, int val) { private long allocateSubpageSimple(int normCapacity, int curIdx, int val) {
int runLength = runLength(val); int runLength = runLength(val);
for (;;) { for (;;) {
if (runLength == pageSize) { if (runLength == pageSize) {
@ -193,10 +193,10 @@ final class PoolChunk<T> {
int subpageIdx = subpageIdx(curIdx); int subpageIdx = subpageIdx(curIdx);
PoolSubpage<T> subpage = subpages[subpageIdx]; PoolSubpage<T> subpage = subpages[subpageIdx];
if (subpage == null) { if (subpage == null) {
subpage = new PoolSubpage<T>(this, curIdx, runOffset(val), pageSize, capacity); subpage = new PoolSubpage<T>(this, curIdx, runOffset(val), pageSize, normCapacity);
subpages[subpageIdx] = subpage; subpages[subpageIdx] = subpage;
} else { } else {
subpage.init(capacity); subpage.init(normCapacity);
} }
arena.addSubpage(subpage); arena.addSubpage(subpage);
return subpage.allocate(); return subpage.allocate();
@ -215,10 +215,10 @@ final class PoolChunk<T> {
} }
} }
private long branchSubpage(int capacity, int nextIdx) { private long branchSubpage(int normCapacity, int nextIdx) {
int nextVal = memoryMap[nextIdx]; int nextVal = memoryMap[nextIdx];
if ((nextVal & 3) != ST_ALLOCATED) { if ((nextVal & 3) != ST_ALLOCATED) {
return allocateSubpage(capacity, nextIdx, nextVal); return allocateSubpage(normCapacity, nextIdx, nextVal);
} }
return -1; return -1;
} }
@ -260,23 +260,23 @@ final class PoolChunk<T> {
} }
} }
void initBuf(PooledByteBuf<T> buf, long handle) { void initBuf(PooledByteBuf<T> buf, long handle, int reqCapacity) {
int memoryMapIdx = (int) handle; int memoryMapIdx = (int) handle;
int bitmapIdx = (int) (handle >>> 32); int bitmapIdx = (int) (handle >>> 32);
if (bitmapIdx == 0) { if (bitmapIdx == 0) {
int val = memoryMap[memoryMapIdx]; int val = memoryMap[memoryMapIdx];
assert (val & 3) == ST_ALLOCATED : String.valueOf(val & 3); assert (val & 3) == ST_ALLOCATED : String.valueOf(val & 3);
buf.init(this, handle, memory, runOffset(val), runLength(val)); buf.init(this, handle, memory, runOffset(val), reqCapacity, runLength(val));
} else { } else {
initBufWithSubpage(buf, handle, bitmapIdx); initBufWithSubpage(buf, handle, bitmapIdx, reqCapacity);
} }
} }
void initBufWithSubpage(PooledByteBuf<T> buf, long handle) { void initBufWithSubpage(PooledByteBuf<T> buf, long handle, int reqCapacity) {
initBufWithSubpage(buf, handle, (int) (handle >>> 32)); initBufWithSubpage(buf, handle, (int) (handle >>> 32), reqCapacity);
} }
private void initBufWithSubpage(PooledByteBuf<T> buf, long handle, int bitmapIdx) { private void initBufWithSubpage(PooledByteBuf<T> buf, long handle, int bitmapIdx, int reqCapacity) {
assert bitmapIdx != 0; assert bitmapIdx != 0;
int memoryMapIdx = (int) handle; int memoryMapIdx = (int) handle;
@ -285,10 +285,11 @@ final class PoolChunk<T> {
PoolSubpage<T> subpage = subpages[subpageIdx(memoryMapIdx)]; PoolSubpage<T> subpage = subpages[subpageIdx(memoryMapIdx)];
assert subpage.doNotDestroy; assert subpage.doNotDestroy;
assert reqCapacity <= subpage.elemSize;
buf.init( buf.init(
this, handle, memory, this, handle, memory,
runOffset(val) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize, subpage.elemSize); runOffset(val) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize, reqCapacity, subpage.elemSize);
} }
private static int parentIdx(int memoryMapIdx) { private static int parentIdx(int memoryMapIdx) {

View File

@ -38,20 +38,20 @@ final class PoolChunkList<T> {
this.maxUsage = maxUsage; this.maxUsage = maxUsage;
} }
boolean allocate(PooledByteBuf<T> buf, int capacity) { boolean allocate(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {
if (head == null) { if (head == null) {
return false; return false;
} }
for (PoolChunk<T> cur = head;;) { for (PoolChunk<T> cur = head;;) {
long handle = cur.allocate(capacity); long handle = cur.allocate(normCapacity);
if (handle < 0) { if (handle < 0) {
cur = cur.next; cur = cur.next;
if (cur == null) { if (cur == null) {
return false; return false;
} }
} else { } else {
cur.initBuf(buf, handle); cur.initBuf(buf, handle, reqCapacity);
if (cur.usage() >= maxUsage) { if (cur.usage() >= maxUsage) {
remove(cur); remove(cur);
nextList.add(cur); nextList.add(cur);

View File

@ -28,6 +28,7 @@ abstract class PooledByteBuf<T> extends AbstractByteBuf {
protected T memory; protected T memory;
protected int offset; protected int offset;
protected int length; protected int length;
private int maxLength;
private ByteBuffer tmpNioBuf; private ByteBuffer tmpNioBuf;
private Queue<Allocation<T>> suspendedDeallocations; private Queue<Allocation<T>> suspendedDeallocations;
@ -36,7 +37,7 @@ abstract class PooledByteBuf<T> extends AbstractByteBuf {
super(maxCapacity); super(maxCapacity);
} }
final void init(PoolChunk<T> chunk, long handle, T memory, int offset, int length) { final void init(PoolChunk<T> chunk, long handle, T memory, int offset, int length, int maxLength) {
assert handle >= 0; assert handle >= 0;
assert memory != null; assert memory != null;
@ -45,6 +46,7 @@ abstract class PooledByteBuf<T> extends AbstractByteBuf {
this.memory = memory; this.memory = memory;
this.offset = offset; this.offset = offset;
this.length = length; this.length = length;
this.maxLength = maxLength;
setIndex(0, 0); setIndex(0, 0);
tmpNioBuf = null; tmpNioBuf = null;
} }
@ -57,6 +59,32 @@ abstract class PooledByteBuf<T> extends AbstractByteBuf {
@Override @Override
public final ByteBuf capacity(int newCapacity) { public final ByteBuf capacity(int newCapacity) {
checkUnfreed(); checkUnfreed();
// If the request capacity does not require reallocation, just update the length of the memory.
if (newCapacity > length) {
if (newCapacity <= maxLength) {
length = newCapacity;
return this;
}
} else if (newCapacity < length) {
if (newCapacity > maxLength >>> 1) {
if (maxLength <= 512) {
if (newCapacity > maxLength - 16) {
length = newCapacity;
setIndex(Math.min(readerIndex(), newCapacity), Math.min(writerIndex(), newCapacity));
return this;
}
} else { // > 512 (i.e. >= 1024)
length = newCapacity;
setIndex(Math.min(readerIndex(), newCapacity), Math.min(writerIndex(), newCapacity));
return this;
}
}
} else {
return this;
}
// Reallocation required.
if (suspendedDeallocations == null) { if (suspendedDeallocations == null) {
chunk.arena.reallocate(this, newCapacity, true); chunk.arena.reallocate(this, newCapacity, true);
} else { } else {

View File

@ -1241,7 +1241,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
data = ctx.alloc().buffer(dataLen, dataLen); data = ctx.alloc().buffer(dataLen, dataLen);
} }
byteBuf.readBytes(data, dataLen).discardSomeReadBytes(); byteBuf.readBytes(data).discardSomeReadBytes();
exchangeBuf.add(data); exchangeBuf.add(data);
} }