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:
parent
f0e1628426
commit
d3011d4f5b
|
@ -103,14 +103,14 @@ public abstract class Recycler<T> {
|
||||||
|
|
||||||
private final int maxCapacityPerThread;
|
private final int maxCapacityPerThread;
|
||||||
private final int maxSharedCapacityFactor;
|
private final int maxSharedCapacityFactor;
|
||||||
private final int ratioMask;
|
private final int interval;
|
||||||
private final int maxDelayedQueuesPerThread;
|
private final int maxDelayedQueuesPerThread;
|
||||||
|
|
||||||
private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() {
|
private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() {
|
||||||
@Override
|
@Override
|
||||||
protected Stack<T> initialValue() {
|
protected Stack<T> initialValue() {
|
||||||
return new Stack<>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
|
return new Stack<>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
|
||||||
ratioMask, maxDelayedQueuesPerThread);
|
interval, maxDelayedQueuesPerThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -138,7 +138,7 @@ public abstract class Recycler<T> {
|
||||||
|
|
||||||
protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor,
|
protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor,
|
||||||
int ratio, int maxDelayedQueuesPerThread) {
|
int ratio, int maxDelayedQueuesPerThread) {
|
||||||
ratioMask = safeFindNextPositivePowerOfTwo(ratio) - 1;
|
interval = safeFindNextPositivePowerOfTwo(ratio);
|
||||||
if (maxCapacityPerThread <= 0) {
|
if (maxCapacityPerThread <= 0) {
|
||||||
this.maxCapacityPerThread = 0;
|
this.maxCapacityPerThread = 0;
|
||||||
this.maxSharedCapacityFactor = 1;
|
this.maxSharedCapacityFactor = 1;
|
||||||
|
@ -310,13 +310,13 @@ public abstract class Recycler<T> {
|
||||||
// pointer to another queue of delayed items for the same stack
|
// pointer to another queue of delayed items for the same stack
|
||||||
private WeakOrderQueue next;
|
private WeakOrderQueue next;
|
||||||
private final int id = ID_GENERATOR.getAndIncrement();
|
private final int id = ID_GENERATOR.getAndIncrement();
|
||||||
private final int ratioMask;
|
private final int interval;
|
||||||
private int handleRecycleCount;
|
private int handleRecycleCount;
|
||||||
|
|
||||||
private WeakOrderQueue() {
|
private WeakOrderQueue() {
|
||||||
super(null);
|
super(null);
|
||||||
head = new Head(null);
|
head = new Head(null);
|
||||||
ratioMask = 0;
|
interval = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WeakOrderQueue(Stack<?> stack, Thread thread) {
|
private WeakOrderQueue(Stack<?> stack, Thread thread) {
|
||||||
|
@ -328,7 +328,8 @@ public abstract class Recycler<T> {
|
||||||
// Stack itself GCed.
|
// Stack itself GCed.
|
||||||
head = new Head(stack.availableSharedCapacity);
|
head = new Head(stack.availableSharedCapacity);
|
||||||
head.link = tail;
|
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) {
|
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
|
// 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
|
// we better should enforce it as well early. Missing to do so may let the WeakOrderQueue grow very fast
|
||||||
// without control if the Stack
|
// without control if the Stack
|
||||||
if ((++handleRecycleCount & ratioMask) != 0) {
|
if (handleRecycleCount < interval) {
|
||||||
|
handleRecycleCount++;
|
||||||
// Drop the item to prevent recycling to aggressive.
|
// Drop the item to prevent recycling to aggressive.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
handleRecycleCount = 0;
|
||||||
|
|
||||||
Link tail = this.tail;
|
Link tail = this.tail;
|
||||||
int writeIndex;
|
int writeIndex;
|
||||||
|
@ -482,21 +485,22 @@ public abstract class Recycler<T> {
|
||||||
private final int maxDelayedQueues;
|
private final int maxDelayedQueues;
|
||||||
|
|
||||||
private final int maxCapacity;
|
private final int maxCapacity;
|
||||||
private final int ratioMask;
|
private final int interval;
|
||||||
DefaultHandle<?>[] elements;
|
DefaultHandle<?>[] elements;
|
||||||
int size;
|
int size;
|
||||||
private int handleRecycleCount = -1; // Start with -1 so the first one will be recycled.
|
private int handleRecycleCount;
|
||||||
private WeakOrderQueue cursor, prev;
|
private WeakOrderQueue cursor, prev;
|
||||||
private volatile WeakOrderQueue head;
|
private volatile WeakOrderQueue head;
|
||||||
|
|
||||||
Stack(Recycler<T> parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor,
|
Stack(Recycler<T> parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor,
|
||||||
int ratioMask, int maxDelayedQueues) {
|
int interval, int maxDelayedQueues) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
threadRef = new WeakReference<>(thread);
|
threadRef = new WeakReference<>(thread);
|
||||||
this.maxCapacity = maxCapacity;
|
this.maxCapacity = maxCapacity;
|
||||||
availableSharedCapacity = new AtomicInteger(max(maxCapacity / maxSharedCapacityFactor, LINK_CAPACITY));
|
availableSharedCapacity = new AtomicInteger(max(maxCapacity / maxSharedCapacityFactor, LINK_CAPACITY));
|
||||||
elements = new DefaultHandle[min(INITIAL_CAPACITY, maxCapacity)];
|
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;
|
this.maxDelayedQueues = maxDelayedQueues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,10 +690,12 @@ public abstract class Recycler<T> {
|
||||||
|
|
||||||
boolean dropHandle(DefaultHandle<?> handle) {
|
boolean dropHandle(DefaultHandle<?> handle) {
|
||||||
if (!handle.hasBeenRecycled) {
|
if (!handle.hasBeenRecycled) {
|
||||||
if ((++handleRecycleCount & ratioMask) != 0) {
|
if (handleRecycleCount < interval) {
|
||||||
|
handleRecycleCount++;
|
||||||
// Drop the object.
|
// Drop the object.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
handleRecycleCount = 0;
|
||||||
handle.hasBeenRecycled = true;
|
handle.hasBeenRecycled = true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -174,9 +174,8 @@ public class RecyclerTest {
|
||||||
thread.start();
|
thread.start();
|
||||||
thread.join();
|
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(), o);
|
||||||
assertSame(recycler.get(), o2);
|
assertNotSame(recycler.get(), o2);
|
||||||
assertNotSame(recycler.get(), o);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue
Block a user