Use FastThreadLocal for skipFlagsCache

Related: #3113

Motivation:

Because ChannelHandlerContext is most often instantiated by an I/O
thread, we can rely on thread-local variables to keep the skipFlags
cache, which should be faster than partitioned synchronized variable.

Modifications:

Use FastThreadLocal for skipFlagsCache instead of partitioned
synchronized map.

Result:

Less contention
This commit is contained in:
Trustin Lee 2014-11-20 17:57:09 +09:00
parent 9465db25ba
commit 2183c129d1

View File

@ -22,6 +22,7 @@ import io.netty.util.AttributeKey;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.ResourceLeakHint;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.concurrent.PausableEventExecutor;
import io.netty.util.internal.OneTimeTask;
import io.netty.util.internal.PlatformDependent;
@ -81,20 +82,20 @@ abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, R
MASK_FLUSH;
/**
* Cache the result of the costly generation of {@link #skipFlags} in the partitioned synchronized
* {@link WeakHashMap}.
* Cache the result of the costly generation of {@link #skipFlags} in a thread-local {@link WeakHashMap}.
*/
@SuppressWarnings("unchecked")
private static final WeakHashMap<Class<?>, Integer>[] skipFlagsCache =
new WeakHashMap[Runtime.getRuntime().availableProcessors()];
private static final FastThreadLocal<WeakHashMap<Class<?>, Integer>> skipFlagsCache =
new FastThreadLocal<WeakHashMap<Class<?>, Integer>>() {
@Override
protected WeakHashMap<Class<?>, Integer> initialValue() throws Exception {
return new WeakHashMap<Class<?>, Integer>();
}
};
private static final AtomicReferenceFieldUpdater<AbstractChannelHandlerContext, PausableChannelEventExecutor>
WRAPPED_EVENTEXECUTOR_UPDATER;
static {
for (int i = 0; i < skipFlagsCache.length; i ++) {
skipFlagsCache[i] = new WeakHashMap<Class<?>, Integer>();
}
AtomicReferenceFieldUpdater<AbstractChannelHandlerContext, PausableChannelEventExecutor> updater =
PlatformDependent.newAtomicReferenceFieldUpdater(
AbstractChannelHandlerContext.class, "wrappedEventLoop");
@ -111,18 +112,15 @@ abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, R
* Otherwise, it delegates to {@link #skipFlags0(Class)} to get it.
*/
static int skipFlags(ChannelHandler handler) {
WeakHashMap<Class<?>, Integer> cache =
skipFlagsCache[(int) (Thread.currentThread().getId() % skipFlagsCache.length)];
WeakHashMap<Class<?>, Integer> cache = skipFlagsCache.get();
Class<? extends ChannelHandler> handlerType = handler.getClass();
int flagsVal;
synchronized (cache) {
Integer flags = cache.get(handlerType);
if (flags != null) {
flagsVal = flags;
} else {
flagsVal = skipFlags0(handlerType);
cache.put(handlerType, Integer.valueOf(flagsVal));
}
Integer flags = cache.get(handlerType);
if (flags != null) {
flagsVal = flags;
} else {
flagsVal = skipFlags0(handlerType);
cache.put(handlerType, Integer.valueOf(flagsVal));
}
return flagsVal;
@ -236,6 +234,7 @@ abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, R
/**
* Wrapped {@link EventLoop} and {@link ChannelHandlerInvoker} to support {@link Channel#deregister()}.
*/
@SuppressWarnings("UnusedDeclaration")
private volatile PausableChannelEventExecutor wrappedEventLoop;
AbstractChannelHandlerContext(