Do not use a pseudo random for tree traversal

Motivation:

If we make allocateRun/SubpageSimple() always try the left node first and make allocateRun/Subpage() always tries the right node first,  it is more likely that allocateRun/Subpage() will find a node with ST_UNUSED sooner.

Modifications:

- Make allocateRunSimple() and allocateSubpageSimple() always try the left node first.
- Make allocateRun() and allocateSubpage() always try the right node first.
- Remove randome

Result:

We get the same performance without using random numbers.
This commit is contained in:
Trustin Lee 2014-05-30 11:23:46 +09:00
parent e5ed69241b
commit ea3dac0753

View File

@ -20,11 +20,7 @@ final class PoolChunk<T> {
private static final int ST_UNUSED = 0; private static final int ST_UNUSED = 0;
private static final int ST_BRANCH = 1; private static final int ST_BRANCH = 1;
private static final int ST_ALLOCATED = 2; private static final int ST_ALLOCATED = 2;
private static final int ST_ALLOCATED_SUBPAGE = ST_ALLOCATED | 1; private static final int ST_ALLOCATED_SUBPAGE = 3;
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
final PoolArena<T> arena; final PoolArena<T> arena;
final T memory; final T memory;
@ -40,8 +36,6 @@ final class PoolChunk<T> {
private final int chunkSize; private final int chunkSize;
private final int maxSubpageAllocs; private final int maxSubpageAllocs;
private long random = (System.nanoTime() ^ multiplier) & mask;
private int freeBytes; private int freeBytes;
PoolChunkList<T> parent; PoolChunkList<T> parent;
@ -123,22 +117,13 @@ final class PoolChunk<T> {
case ST_UNUSED: case ST_UNUSED:
return allocateRunSimple(normCapacity, curIdx, val); return allocateRunSimple(normCapacity, curIdx, val);
case ST_BRANCH: case ST_BRANCH:
// Try the right node first because it is more likely to be ST_UNUSED.
// It is because allocateRunSimple() always chooses the left node.
final int nextIdxLeft = curIdx << 1; final int nextIdxLeft = curIdx << 1;
final int nextValLeft = memoryMap[nextIdxLeft];
final boolean recurseLeft;
switch (nextValLeft & 3) {
case ST_UNUSED:
return allocateRunSimple(normCapacity, nextIdxLeft, nextValLeft);
case ST_BRANCH:
recurseLeft = true;
break;
default:
recurseLeft = false;
}
final int nextIdxRight = nextIdxLeft ^ 1; final int nextIdxRight = nextIdxLeft ^ 1;
final int nextValRight = memoryMap[nextIdxRight]; final int nextValRight = memoryMap[nextIdxRight];
final boolean recurseRight; final boolean recurseRight;
switch (nextValRight & 3) { switch (nextValRight & 3) {
case ST_UNUSED: case ST_UNUSED:
return allocateRunSimple(normCapacity, nextIdxRight, nextValRight); return allocateRunSimple(normCapacity, nextIdxRight, nextValRight);
@ -149,15 +134,28 @@ final class PoolChunk<T> {
recurseRight = false; recurseRight = false;
} }
if (recurseLeft) { final int nextValLeft = memoryMap[nextIdxLeft];
long res = branchRun(normCapacity, nextIdxLeft); final boolean recurseLeft;
switch (nextValLeft & 3) {
case ST_UNUSED:
return allocateRunSimple(normCapacity, nextIdxLeft, nextValLeft);
case ST_BRANCH:
recurseLeft = true;
break;
default:
recurseLeft = false;
}
if (recurseRight) {
long res = branchRun(normCapacity, nextIdxRight);
if (res > 0) { if (res > 0) {
return res; return res;
} }
} }
if (recurseRight) { if (recurseLeft) {
return branchRun(normCapacity, nextIdxRight); return branchRun(normCapacity, nextIdxLeft);
} }
} }
@ -193,7 +191,7 @@ final class PoolChunk<T> {
return curIdx; return curIdx;
} }
int nextIdx = curIdx << 1 ^ nextRandom(); int nextIdx = curIdx << 1;
int unusedIdx = nextIdx ^ 1; int unusedIdx = nextIdx ^ 1;
memoryMap[curIdx] = val & ~3 | ST_BRANCH; memoryMap[curIdx] = val & ~3 | ST_BRANCH;
@ -207,22 +205,22 @@ final class PoolChunk<T> {
} }
private long allocateSubpage(int normCapacity, int curIdx, int val) { private long allocateSubpage(int normCapacity, int curIdx, int val) {
int state = val & 3; switch (val & 3) {
if (state == ST_BRANCH) { case ST_UNUSED:
int nextIdx = curIdx << 1 ^ nextRandom(); return allocateSubpageSimple(normCapacity, curIdx, val);
long res = branchSubpage(normCapacity, nextIdx); case ST_BRANCH:
// Try the right node first because it is more likely to be ST_UNUSED.
// It is because allocateSubpageSimple() always chooses the left node.
final int nextIdxLeft = curIdx << 1;
final int nextIdxRight = nextIdxLeft ^ 1;
long res = branchSubpage(normCapacity, nextIdxRight);
if (res > 0) { if (res > 0) {
return res; return res;
} }
return branchSubpage(normCapacity, nextIdx ^ 1); return branchSubpage(normCapacity, nextIdxLeft);
} case ST_ALLOCATED_SUBPAGE:
if (state == ST_UNUSED) {
return allocateSubpageSimple(normCapacity, curIdx, val);
}
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 (normCapacity != elemSize) { if (normCapacity != elemSize) {
@ -253,7 +251,7 @@ final class PoolChunk<T> {
return subpage.allocate(); return subpage.allocate();
} }
int nextIdx = curIdx << 1 ^ nextRandom(); int nextIdx = curIdx << 1;
int unusedIdx = nextIdx ^ 1; int unusedIdx = nextIdx ^ 1;
memoryMap[curIdx] = val & ~3 | ST_BRANCH; memoryMap[curIdx] = val & ~3 | ST_BRANCH;
@ -363,11 +361,6 @@ final class PoolChunk<T> {
return memoryMapIdx - maxSubpageAllocs; return memoryMapIdx - maxSubpageAllocs;
} }
private int nextRandom() {
random = random * multiplier + addend & mask;
return (int) (random >>> 47) & 1;
}
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append("Chunk("); buf.append("Chunk(");