Replace the sun.nio.ch.SelectorImpl.selectedKeys with faster one
- Yield much less garbage - Slight performance gain (1~2%)
This commit is contained in:
parent
c8ca329932
commit
3ce9ab2e72
@ -26,6 +26,7 @@ import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
@ -51,6 +52,8 @@ public final class NioEventLoop extends SingleThreadEventLoop {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioEventLoop.class);
|
||||
|
||||
private static final int CLEANUP_INTERVAL = 256; // XXX Hard-coded value, but won't need customization.
|
||||
private static final boolean DISABLE_KEYSET_OPTIMIZATION =
|
||||
SystemPropertyUtil.getBoolean("io.netty.noKeySetOptimization", false);
|
||||
|
||||
private static final int MIN_PREMATURE_SELECTOR_RETURNS = 3;
|
||||
private static final int SELECTOR_AUTO_REBUILD_THRESHOLD;
|
||||
@ -81,6 +84,7 @@ public final class NioEventLoop extends SingleThreadEventLoop {
|
||||
SELECTOR_AUTO_REBUILD_THRESHOLD = selectorAutoRebuildThreshold;
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("io.netty.noKeySetOptimization: {}", DISABLE_KEYSET_OPTIMIZATION);
|
||||
logger.debug("io.netty.selectorAutoRebuildThreshold: {}", SELECTOR_AUTO_REBUILD_THRESHOLD);
|
||||
}
|
||||
}
|
||||
@ -89,6 +93,7 @@ public final class NioEventLoop extends SingleThreadEventLoop {
|
||||
* The NIO {@link Selector}.
|
||||
*/
|
||||
Selector selector;
|
||||
private SelectedSelectionKeySet selectedKeys;
|
||||
|
||||
private final SelectorProvider provider;
|
||||
|
||||
@ -115,11 +120,40 @@ public final class NioEventLoop extends SingleThreadEventLoop {
|
||||
}
|
||||
|
||||
private Selector openSelector() {
|
||||
final Selector selector;
|
||||
try {
|
||||
return provider.openSelector();
|
||||
selector = provider.openSelector();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException("failed to open a new selector", e);
|
||||
}
|
||||
|
||||
if (DISABLE_KEYSET_OPTIMIZATION) {
|
||||
return selector;
|
||||
}
|
||||
|
||||
try {
|
||||
SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
|
||||
|
||||
Class<?> selectorImplClass =
|
||||
Class.forName("sun.nio.ch.SelectorImpl", false, ClassLoader.getSystemClassLoader());
|
||||
selectorImplClass.isAssignableFrom(selector.getClass());
|
||||
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
|
||||
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
|
||||
|
||||
selectedKeysField.setAccessible(true);
|
||||
publicSelectedKeysField.setAccessible(true);
|
||||
|
||||
selectedKeysField.set(selector, selectedKeySet);
|
||||
publicSelectedKeysField.set(selector, selectedKeySet);
|
||||
|
||||
selectedKeys = selectedKeySet;
|
||||
logger.trace("Instrumented an optimized java.util.Set into: {}", selector);
|
||||
} catch (Throwable t) {
|
||||
selectedKeys = null;
|
||||
logger.trace("Failed to instrument an optimized java.util.Set into: {}", selector, t);
|
||||
}
|
||||
|
||||
return selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -319,7 +353,12 @@ public final class NioEventLoop extends SingleThreadEventLoop {
|
||||
cancelledKeys = 0;
|
||||
|
||||
final long ioStartTime = System.nanoTime();
|
||||
processSelectedKeys();
|
||||
needsToSelectAgain = false;
|
||||
if (selectedKeys != null) {
|
||||
processSelectedKeysOptimized(selectedKeys.flip());
|
||||
} else {
|
||||
processSelectedKeysPlain(selector.selectedKeys());
|
||||
}
|
||||
final long ioTime = System.nanoTime() - ioStartTime;
|
||||
|
||||
final int ioRatio = this.ioRatio;
|
||||
@ -372,9 +411,7 @@ public final class NioEventLoop extends SingleThreadEventLoop {
|
||||
return task;
|
||||
}
|
||||
|
||||
private void processSelectedKeys() {
|
||||
needsToSelectAgain = false;
|
||||
Set<SelectionKey> selectedKeys = selector.selectedKeys();
|
||||
private void processSelectedKeysPlain(Set<SelectionKey> selectedKeys) {
|
||||
// check if the set is empty and if so just return to not create garbage by
|
||||
// creating a new Iterator every time even if there is nothing to process.
|
||||
// See https://github.com/netty/netty/issues/597
|
||||
@ -414,6 +451,29 @@ public final class NioEventLoop extends SingleThreadEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
|
||||
for (int i = 0;; i ++) {
|
||||
final SelectionKey k = selectedKeys[i];
|
||||
if (k == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
final Object a = k.attachment();
|
||||
|
||||
if (a instanceof AbstractNioChannel) {
|
||||
processSelectedKey(k, (AbstractNioChannel) a);
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
|
||||
processSelectedKey(k, task);
|
||||
}
|
||||
|
||||
if (needsToSelectAgain) {
|
||||
selectAgain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
|
||||
final NioUnsafe unsafe = ch.unsafe();
|
||||
if (!k.isValid()) {
|
||||
@ -583,6 +643,7 @@ public final class NioEventLoop extends SingleThreadEventLoop {
|
||||
selectCnt);
|
||||
|
||||
rebuildSelector();
|
||||
selector = this.selector;
|
||||
|
||||
// Select again to populate selectedKeys.
|
||||
selector.selectNow();
|
||||
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.channel.nio;
|
||||
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
final class SelectedSelectionKeySet extends AbstractSet<SelectionKey> {
|
||||
|
||||
private SelectionKey[] keysA;
|
||||
private int keysASize;
|
||||
private SelectionKey[] keysB;
|
||||
private int keysBSize;
|
||||
private boolean isA = true;
|
||||
|
||||
SelectedSelectionKeySet() {
|
||||
keysA = new SelectionKey[1024];
|
||||
keysB = keysA.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(SelectionKey o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isA) {
|
||||
int size = keysASize;
|
||||
keysA[size ++] = o;
|
||||
keysASize = size;
|
||||
if (size == keysA.length) {
|
||||
doubleCapacityA();
|
||||
}
|
||||
} else {
|
||||
int size = keysBSize;
|
||||
keysB[size ++] = o;
|
||||
keysBSize = size;
|
||||
if (size == keysB.length) {
|
||||
doubleCapacityB();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doubleCapacityA() {
|
||||
SelectionKey[] newKeysA = new SelectionKey[keysA.length << 1];
|
||||
System.arraycopy(keysA, 0, newKeysA, 0, keysASize);
|
||||
keysA = newKeysA;
|
||||
}
|
||||
|
||||
private void doubleCapacityB() {
|
||||
SelectionKey[] newKeysB = new SelectionKey[keysB.length << 1];
|
||||
System.arraycopy(keysB, 0, newKeysB, 0, keysBSize);
|
||||
keysB = newKeysB;
|
||||
}
|
||||
|
||||
SelectionKey[] flip() {
|
||||
if (isA) {
|
||||
isA = false;
|
||||
keysA[keysASize] = null;
|
||||
keysBSize = 0;
|
||||
return keysA;
|
||||
} else {
|
||||
isA = true;
|
||||
keysB[keysBSize] = null;
|
||||
keysASize = 0;
|
||||
return keysB;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
if (isA) {
|
||||
return keysASize;
|
||||
} else {
|
||||
return keysBSize;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<SelectionKey> iterator() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user