Use interval instead of mask comparison for Recycler ratio (#9748)

Motivation

The recycling ratio is currently implemented by comparing with a masked
count. The mask operation is not free and also not necessary.

Modification

Change the count(s) to just iterate over the corresponding interval,
which requires only a comparison and no mask.

Also make "first time recycle" behaviour consistent and revert change to
RecyclerTest made in #9727.

Result

Less recycling overhead
This commit is contained in:
Nick Hill 2019-11-04 02:24:20 -08:00 committed by Norman Maurer
parent f0e1628426
commit d3011d4f5b
2 changed files with 20 additions and 15 deletions

View File

@ -103,14 +103,14 @@ public abstract class Recycler<T> {
private final int maxCapacityPerThread;
private final int maxSharedCapacityFactor;
private final int ratioMask;
private final int interval;
private final int maxDelayedQueuesPerThread;
private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() {
@Override
protected Stack<T> initialValue() {
return new Stack<>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
ratioMask, maxDelayedQueuesPerThread);
interval, maxDelayedQueuesPerThread);
}
@Override
@ -138,7 +138,7 @@ public abstract class Recycler<T> {
protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor,
int ratio, int maxDelayedQueuesPerThread) {
ratioMask = safeFindNextPositivePowerOfTwo(ratio) - 1;
interval = safeFindNextPositivePowerOfTwo(ratio);
if (maxCapacityPerThread <= 0) {
this.maxCapacityPerThread = 0;
this.maxSharedCapacityFactor = 1;
@ -310,13 +310,13 @@ public abstract class Recycler<T> {
// pointer to another queue of delayed items for the same stack
private WeakOrderQueue next;
private final int id = ID_GENERATOR.getAndIncrement();
private final int ratioMask;
private final int interval;
private int handleRecycleCount;
private WeakOrderQueue() {
super(null);
head = new Head(null);
ratioMask = 0;
interval = 0;
}
private WeakOrderQueue(Stack<?> stack, Thread thread) {
@ -328,7 +328,8 @@ public abstract class Recycler<T> {
// Stack itself GCed.
head = new Head(stack.availableSharedCapacity);
head.link = tail;
ratioMask = stack.ratioMask;
interval = stack.interval;
handleRecycleCount = interval; // Start at interval so the first one will be recycled.
}
static WeakOrderQueue newQueue(Stack<?> stack, Thread thread) {
@ -364,10 +365,12 @@ public abstract class Recycler<T> {
// While we also enforce the recycling ratio one we transfer objects from the WeakOrderQueue to the Stack
// we better should enforce it as well early. Missing to do so may let the WeakOrderQueue grow very fast
// without control if the Stack
if ((++handleRecycleCount & ratioMask) != 0) {
if (handleRecycleCount < interval) {
handleRecycleCount++;
// Drop the item to prevent recycling to aggressive.
return;
}
handleRecycleCount = 0;
Link tail = this.tail;
int writeIndex;
@ -482,21 +485,22 @@ public abstract class Recycler<T> {
private final int maxDelayedQueues;
private final int maxCapacity;
private final int ratioMask;
private final int interval;
DefaultHandle<?>[] elements;
int size;
private int handleRecycleCount = -1; // Start with -1 so the first one will be recycled.
private int handleRecycleCount;
private WeakOrderQueue cursor, prev;
private volatile WeakOrderQueue head;
Stack(Recycler<T> parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor,
int ratioMask, int maxDelayedQueues) {
int interval, int maxDelayedQueues) {
this.parent = parent;
threadRef = new WeakReference<>(thread);
this.maxCapacity = maxCapacity;
availableSharedCapacity = new AtomicInteger(max(maxCapacity / maxSharedCapacityFactor, LINK_CAPACITY));
elements = new DefaultHandle[min(INITIAL_CAPACITY, maxCapacity)];
this.ratioMask = ratioMask;
this.interval = interval;
handleRecycleCount = interval; // Start at interval so the first one will be recycled.
this.maxDelayedQueues = maxDelayedQueues;
}
@ -686,10 +690,12 @@ public abstract class Recycler<T> {
boolean dropHandle(DefaultHandle<?> handle) {
if (!handle.hasBeenRecycled) {
if ((++handleRecycleCount & ratioMask) != 0) {
if (handleRecycleCount < interval) {
handleRecycleCount++;
// Drop the object.
return true;
}
handleRecycleCount = 0;
handle.hasBeenRecycled = true;
}
return false;

View File

@ -174,9 +174,8 @@ public class RecyclerTest {
thread.start();
thread.join();
// As we use a ratioMask of 2 we should see o2 as the first object that could recycled from a different thread.
assertSame(recycler.get(), o2);
assertNotSame(recycler.get(), o);
assertSame(recycler.get(), o);
assertNotSame(recycler.get(), o2);
}
@Test