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 Norman Maurer
parent b019ea7c54
commit 73b4179b8d
2 changed files with 44 additions and 14 deletions

View File

@ -54,6 +54,7 @@ public abstract class Recycler<T> {
private static final int MAX_DELAYED_QUEUES_PER_THREAD;
private static final int LINK_CAPACITY;
private static final int RATIO;
private static final int DELAYED_QUEUE_RATIO;
static {
// In the future, we might have different maxCapacity for different object types.
@ -83,6 +84,7 @@ public abstract class Recycler<T> {
// This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation
// bursts.
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 (DEFAULT_MAX_CAPACITY_PER_THREAD == 0) {
@ -90,11 +92,13 @@ public abstract class Recycler<T> {
logger.debug("-Dio.netty.recycler.maxSharedCapacityFactor: disabled");
logger.debug("-Dio.netty.recycler.linkCapacity: disabled");
logger.debug("-Dio.netty.recycler.ratio: disabled");
logger.debug("-Dio.netty.recycler.delayedQueue.ratio: disabled");
} else {
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.linkCapacity: {}", LINK_CAPACITY);
logger.debug("-Dio.netty.recycler.ratio: {}", RATIO);
logger.debug("-Dio.netty.recycler.delayedQueue.ratio: {}", DELAYED_QUEUE_RATIO);
}
}
@ -105,12 +109,13 @@ public abstract class Recycler<T> {
private final int maxSharedCapacityFactor;
private final int interval;
private final int maxDelayedQueuesPerThread;
private final int delayedQueueInterval;
private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() {
@Override
protected Stack<T> initialValue() {
return new Stack<>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
interval, maxDelayedQueuesPerThread);
interval, maxDelayedQueuesPerThread, delayedQueueInterval);
}
@Override
@ -138,7 +143,14 @@ public abstract class Recycler<T> {
protected Recycler(int maxCapacityPerThread, int maxSharedCapacityFactor,
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);
delayedQueueInterval = max(0, delayedQueueRatio);
if (maxCapacityPerThread <= 0) {
this.maxCapacityPerThread = 0;
this.maxSharedCapacityFactor = 1;
@ -328,7 +340,7 @@ public abstract class Recycler<T> {
// Stack itself GCed.
head = new Head(stack.availableSharedCapacity);
head.link = tail;
interval = stack.interval;
interval = stack.delayedQueueInterval;
handleRecycleCount = interval; // Start at interval so the first one will be recycled.
}
@ -486,6 +498,7 @@ public abstract class Recycler<T> {
private final int maxCapacity;
private final int interval;
private final int delayedQueueInterval;
DefaultHandle<?>[] elements;
int size;
private int handleRecycleCount;
@ -493,13 +506,14 @@ public abstract class Recycler<T> {
private volatile WeakOrderQueue head;
Stack(Recycler<T> parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor,
int interval, int maxDelayedQueues) {
int interval, int maxDelayedQueues, int delayedQueueInterval) {
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.interval = interval;
this.delayedQueueInterval = delayedQueueInterval;
handleRecycleCount = interval; // Start at interval so the first one will be recycled.
this.maxDelayedQueues = maxDelayedQueues;
}

View File

@ -27,13 +27,14 @@ import static org.junit.Assert.*;
public class RecyclerTest {
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,
int ratio, int maxDelayedQueuesPerThread) {
int ratio, int maxDelayedQueuesPerThread,
int delayedQueueRatio) {
return new Recycler<HandledObject>(maxCapacityPerThread, maxSharedCapacityFactor, ratio,
maxDelayedQueuesPerThread) {
maxDelayedQueuesPerThread, delayedQueueRatio) {
@Override
protected HandledObject newObject(
Recycler.Handle<HandledObject> handle) {
@ -130,7 +131,7 @@ public class RecyclerTest {
@Test
public void testRecycleDisableDrop() {
Recycler<HandledObject> recycler = newRecycler(1024, 2, 0, 2);
Recycler<HandledObject> recycler = newRecycler(1024, 2, 0, 2, 0);
HandledObject object = recycler.get();
object.recycle();
HandledObject object2 = recycler.get();
@ -141,6 +142,27 @@ public class RecyclerTest {
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
* https://github.com/netty/netty/issues/2848
@ -173,13 +195,7 @@ public class RecyclerTest {
@Test
public void testRecycleAtDifferentThread() throws Exception {
final Recycler<HandledObject> recycler = new Recycler<HandledObject>(256, 10, 2, 10) {
@Override
protected HandledObject newObject(Recycler.Handle<HandledObject> handle) {
return new HandledObject(handle);
}
};
final Recycler<HandledObject> recycler = newRecycler(256, 10, 2, 10, 2);
final HandledObject o = recycler.get();
final HandledObject o2 = recycler.get();