From bac2e3a6d2398e976f47aac0501331d364ed8222 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 21 May 2015 22:21:22 +0200 Subject: [PATCH] Reduce calls to System.nanoTime() and object creation in IdleStateHandler. Related to [#3808] Motivation: Calling System.nanoTime() for each channelRead(...) is very expensive. See [#3808] for more detailed description. Also we always do extra work for each write and read even if read or write idle states should not be handled. Modifications: - Move System.nanoTime() call to channelReadComplete(...). - Reuse ChannelFutureListener for writes - Only add ChannelFutureListener to writes if write and all idle states should be handled. - Only call System.nanoTime() for reads if idle state events for read and all states should be handled. Result: Less overhead when using the IdleStateHandler. --- .../handler/timeout/IdleStateHandler.java | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java index ec6def749d..0c78d1fd9f 100644 --- a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java @@ -97,6 +97,15 @@ import java.util.concurrent.TimeUnit; public class IdleStateHandler extends ChannelDuplexHandler { private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1); + // Not create a new ChannelFutureListener per write operation to reduce GC pressure. + private final ChannelFutureListener writeListener = new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + lastWriteTime = System.nanoTime(); + firstWriterIdleEvent = firstAllIdleEvent = true; + } + }; + private final long readerIdleTimeNanos; private final long writerIdleTimeNanos; private final long allIdleTimeNanos; @@ -113,6 +122,7 @@ public class IdleStateHandler extends ChannelDuplexHandler { private boolean firstAllIdleEvent = true; private volatile int state; // 0 - none, 1 - initialized, 2 - destroyed + private volatile boolean reading; /** * Creates a new instance firing {@link IdleStateEvent}s. @@ -249,23 +259,28 @@ public class IdleStateHandler extends ChannelDuplexHandler { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - lastReadTime = System.nanoTime(); - firstReaderIdleEvent = firstAllIdleEvent = true; + if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) { + reading = true; + firstReaderIdleEvent = firstAllIdleEvent = true; + } ctx.fireChannelRead(msg); } + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) { + lastReadTime = System.nanoTime(); + reading = false; + } + ctx.fireChannelReadComplete(); + } + @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { // Allow writing with void promise if handler is only configured for read timeout events. if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) { ChannelPromise unvoid = promise.unvoid(); - unvoid.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - lastWriteTime = System.nanoTime(); - firstWriterIdleEvent = firstAllIdleEvent = true; - } - }); + unvoid.addListener(writeListener); ctx.write(msg, unvoid); } else { ctx.write(msg, promise); @@ -342,9 +357,11 @@ public class IdleStateHandler extends ChannelDuplexHandler { return; } - long currentTime = System.nanoTime(); - long lastReadTime = IdleStateHandler.this.lastReadTime; - long nextDelay = readerIdleTimeNanos - (currentTime - lastReadTime); + long nextDelay = readerIdleTimeNanos; + if (!reading) { + nextDelay -= System.nanoTime() - lastReadTime; + } + if (nextDelay <= 0) { // Reader is idle - set a new timeout and notify the callback. readerIdleTimeout = @@ -382,9 +399,8 @@ public class IdleStateHandler extends ChannelDuplexHandler { return; } - long currentTime = System.nanoTime(); long lastWriteTime = IdleStateHandler.this.lastWriteTime; - long nextDelay = writerIdleTimeNanos - (currentTime - lastWriteTime); + long nextDelay = writerIdleTimeNanos - (System.nanoTime() - lastWriteTime); if (nextDelay <= 0) { // Writer is idle - set a new timeout and notify the callback. writerIdleTimeout = ctx.executor().schedule( @@ -422,9 +438,10 @@ public class IdleStateHandler extends ChannelDuplexHandler { return; } - long currentTime = System.nanoTime(); - long lastIoTime = Math.max(lastReadTime, lastWriteTime); - long nextDelay = allIdleTimeNanos - (currentTime - lastIoTime); + long nextDelay = allIdleTimeNanos; + if (!reading) { + nextDelay -= System.nanoTime() - Math.max(lastReadTime, lastWriteTime); + } if (nextDelay <= 0) { // Both reader and writer are idle - set a new timeout and // notify the callback.