Add option to configure recycler delayed queue drop ratio (#10251) (#10255)

Motivation

- Recycler stack and delayed queue drop ratio can only be configured with
the same value. The overall drop ratio is ratio^2.

- #10251 shows that enable drop in `WeakOrderQueue` may introduce
performance degradation. Though the final reason is not clear now,
it would be better to add option to configure delayed queue drop ratio separately.

Modification

- "io.netty.recycler.delayedQueue.ratio" as the drop ratio of delayed queue
- default "delayedQueue.ratio" is same as "ratio"

Results

Able to configure recycler delayed queue drop ratio separately
This commit is contained in:
louxiu 2020-05-07 21:08:35 +08:00 committed by GitHub
parent 731d33070f
commit 909e7c9c29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 14 deletions

View File

@ -57,6 +57,7 @@ public abstract class Recycler<T> {
private static final int MAX_DELAYED_QUEUES_PER_THREAD; private static final int MAX_DELAYED_QUEUES_PER_THREAD;
private static final int LINK_CAPACITY; private static final int LINK_CAPACITY;
private static final int RATIO; private static final int RATIO;
private static final int DELAYED_QUEUE_RATIO;
static { static {
// In the future, we might have different maxCapacity for different object types. // In the future, we might have different maxCapacity for different object types.
@ -86,6 +87,7 @@ public abstract class Recycler<T> {
// This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation // This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation
// bursts. // bursts.
RATIO = max(0, SystemPropertyUtil.getInt("io.netty.recycler.ratio", 8)); RATIO = max(0, SystemPropertyUtil.getInt("io.netty.recycler.ratio", 8));
DELAYED_QUEUE_RATIO = max(0, SystemPropertyUtil.getInt("io.netty.recycler.delayedQueue.ratio", RATIO));
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
if (DEFAULT_MAX_CAPACITY_PER_THREAD == 0) { if (DEFAULT_MAX_CAPACITY_PER_THREAD == 0) {
@ -93,11 +95,13 @@ public abstract class Recycler<T> {
logger.debug("-Dio.netty.recycler.maxSharedCapacityFactor: disabled"); logger.debug("-Dio.netty.recycler.maxSharedCapacityFactor: disabled");
logger.debug("-Dio.netty.recycler.linkCapacity: disabled"); logger.debug("-Dio.netty.recycler.linkCapacity: disabled");
logger.debug("-Dio.netty.recycler.ratio: disabled"); logger.debug("-Dio.netty.recycler.ratio: disabled");
logger.debug("-Dio.netty.recycler.delayedQueue.ratio: disabled");
} else { } else {
logger.debug("-Dio.netty.recycler.maxCapacityPerThread: {}", DEFAULT_MAX_CAPACITY_PER_THREAD); logger.debug("-Dio.netty.recycler.maxCapacityPerThread: {}", DEFAULT_MAX_CAPACITY_PER_THREAD);
logger.debug("-Dio.netty.recycler.maxSharedCapacityFactor: {}", MAX_SHARED_CAPACITY_FACTOR); logger.debug("-Dio.netty.recycler.maxSharedCapacityFactor: {}", MAX_SHARED_CAPACITY_FACTOR);
logger.debug("-Dio.netty.recycler.linkCapacity: {}", LINK_CAPACITY); logger.debug("-Dio.netty.recycler.linkCapacity: {}", LINK_CAPACITY);
logger.debug("-Dio.netty.recycler.ratio: {}", RATIO); logger.debug("-Dio.netty.recycler.ratio: {}", RATIO);
logger.debug("-Dio.netty.recycler.delayedQueue.ratio: {}", DELAYED_QUEUE_RATIO);
} }
} }
@ -108,12 +112,13 @@ public abstract class Recycler<T> {
private final int maxSharedCapacityFactor; private final int maxSharedCapacityFactor;
private final int interval; private final int interval;
private final int maxDelayedQueuesPerThread; private final int maxDelayedQueuesPerThread;
private final int delayedQueueInterval;
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<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor, return new Stack<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
interval, maxDelayedQueuesPerThread); interval, maxDelayedQueuesPerThread, delayedQueueInterval);
} }
@Override @Override
@ -141,7 +146,14 @@ 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) {
this(maxCapacityPerThread, maxSharedCapacityFactor, ratio, maxDelayedQueuesPerThread,
DELAYED_QUEUE_RATIO);
}
protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor,
int ratio, int maxDelayedQueuesPerThread, int delayedQueueRatio) {
interval = max(0, ratio); interval = max(0, ratio);
delayedQueueInterval = max(0, delayedQueueRatio);
if (maxCapacityPerThread <= 0) { if (maxCapacityPerThread <= 0) {
this.maxCapacityPerThread = 0; this.maxCapacityPerThread = 0;
this.maxSharedCapacityFactor = 1; this.maxSharedCapacityFactor = 1;
@ -331,7 +343,7 @@ 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;
interval = stack.interval; interval = stack.delayedQueueInterval;
handleRecycleCount = interval; // Start at interval so the first one will be recycled. handleRecycleCount = interval; // Start at interval so the first one will be recycled.
} }
@ -489,6 +501,7 @@ public abstract class Recycler<T> {
private final int maxCapacity; private final int maxCapacity;
private final int interval; private final int interval;
private final int delayedQueueInterval;
DefaultHandle<?>[] elements; DefaultHandle<?>[] elements;
int size; int size;
private int handleRecycleCount; private int handleRecycleCount;
@ -496,13 +509,14 @@ public abstract class Recycler<T> {
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 interval, int maxDelayedQueues) { int interval, int maxDelayedQueues, int delayedQueueInterval) {
this.parent = parent; this.parent = parent;
threadRef = new WeakReference<Thread>(thread); threadRef = new WeakReference<Thread>(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.interval = interval; this.interval = interval;
this.delayedQueueInterval = delayedQueueInterval;
handleRecycleCount = interval; // Start at interval so the first one will be recycled. handleRecycleCount = interval; // Start at interval so the first one will be recycled.
this.maxDelayedQueues = maxDelayedQueues; this.maxDelayedQueues = maxDelayedQueues;
} }

View File

@ -27,13 +27,14 @@ import static org.junit.Assert.*;
public class RecyclerTest { public class RecyclerTest {
private static Recycler<HandledObject> newRecycler(int maxCapacityPerThread) { private static Recycler<HandledObject> newRecycler(int maxCapacityPerThread) {
return newRecycler(maxCapacityPerThread, 2, 8, 2); return newRecycler(maxCapacityPerThread, 2, 8, 2, 8);
} }
private static Recycler<HandledObject> newRecycler(int maxCapacityPerThread, int maxSharedCapacityFactor, private static Recycler<HandledObject> newRecycler(int maxCapacityPerThread, int maxSharedCapacityFactor,
int ratio, int maxDelayedQueuesPerThread) { int ratio, int maxDelayedQueuesPerThread,
int delayedQueueRatio) {
return new Recycler<HandledObject>(maxCapacityPerThread, maxSharedCapacityFactor, ratio, return new Recycler<HandledObject>(maxCapacityPerThread, maxSharedCapacityFactor, ratio,
maxDelayedQueuesPerThread) { maxDelayedQueuesPerThread, delayedQueueRatio) {
@Override @Override
protected HandledObject newObject( protected HandledObject newObject(
Recycler.Handle<HandledObject> handle) { Recycler.Handle<HandledObject> handle) {
@ -141,7 +142,7 @@ public class RecyclerTest {
@Test @Test
public void testRecycleDisableDrop() { public void testRecycleDisableDrop() {
Recycler<HandledObject> recycler = newRecycler(1024, 2, 0, 2); Recycler<HandledObject> recycler = newRecycler(1024, 2, 0, 2, 0);
HandledObject object = recycler.get(); HandledObject object = recycler.get();
object.recycle(); object.recycle();
HandledObject object2 = recycler.get(); HandledObject object2 = recycler.get();
@ -152,6 +153,27 @@ public class RecyclerTest {
object3.recycle(); object3.recycle();
} }
@Test
public void testRecycleDisableDelayedQueueDrop() throws Exception {
final Recycler<HandledObject> recycler = newRecycler(1024, 2, 1, 2, 0);
final HandledObject o = recycler.get();
final HandledObject o2 = recycler.get();
final HandledObject o3 = recycler.get();
final Thread thread = new Thread() {
@Override
public void run() {
o.recycle();
o2.recycle();
o3.recycle();
}
};
thread.start();
thread.join();
// In reverse order
assertSame(o3, recycler.get());
assertSame(o, recycler.get());
}
/** /**
* Test to make sure bug #2848 never happens again * Test to make sure bug #2848 never happens again
* https://github.com/netty/netty/issues/2848 * https://github.com/netty/netty/issues/2848
@ -184,13 +206,7 @@ public class RecyclerTest {
@Test @Test
public void testRecycleAtDifferentThread() throws Exception { public void testRecycleAtDifferentThread() throws Exception {
final Recycler<HandledObject> recycler = new Recycler<HandledObject>(256, 10, 2, 10) { final Recycler<HandledObject> recycler = newRecycler(256, 10, 2, 10, 2);
@Override
protected HandledObject newObject(Recycler.Handle<HandledObject> handle) {
return new HandledObject(handle);
}
};
final HandledObject o = recycler.get(); final HandledObject o = recycler.get();
final HandledObject o2 = recycler.get(); final HandledObject o2 = recycler.get();