From 99f361ca77e52b5e95fea4444317ccfc94f4545a Mon Sep 17 00:00:00 2001 From: md_5 Date: Sun, 18 Aug 2013 20:24:29 +1000 Subject: [PATCH] Instead of storing packets about to be passed on as a byte array, store them as a Netty buffer, which is likely to be pooled, direct and manually memory managed leading to increased performance and less GC strain. In order to ensure no resources are leaked, we free them at the end of each handle cycle if they have not been passed to a channel for writing. In initial profiles this now causes encryption to be one of the most intensive parts of BungeeCord, however in depth profiling snapshots may provide further routes for optimization. --- .../main/java/net/md_5/bungee/EntityMap.java | 41 +++++++------------ .../java/net/md_5/bungee/UserConnection.java | 5 ++- .../bungee/connection/DownstreamBridge.java | 7 ++-- .../bungee/connection/UpstreamBridge.java | 7 ++-- .../net/md_5/bungee/netty/ChannelWrapper.java | 9 +++- .../net/md_5/bungee/netty/HandlerBoss.java | 24 ++++++----- .../net/md_5/bungee/netty/PacketDecoder.java | 17 +------- .../net/md_5/bungee/netty/PacketHandler.java | 2 +- .../net/md_5/bungee/netty/PacketWrapper.java | 19 ++++++--- .../net/md_5/bungee/netty/PipelineUtils.java | 3 -- 10 files changed, 65 insertions(+), 69 deletions(-) diff --git a/proxy/src/main/java/net/md_5/bungee/EntityMap.java b/proxy/src/main/java/net/md_5/bungee/EntityMap.java index a1df4ece..679bbafa 100644 --- a/proxy/src/main/java/net/md_5/bungee/EntityMap.java +++ b/proxy/src/main/java/net/md_5/bungee/EntityMap.java @@ -1,5 +1,7 @@ package net.md_5.bungee; +import io.netty.buffer.ByteBuf; + /** * Class to rewrite integers within packets. */ @@ -117,20 +119,20 @@ public class EntityMap }; } - public static void rewrite(byte[] packet, int oldId, int newId) + public static void rewrite(ByteBuf packet, int oldId, int newId) { - int packetId = packet[0] & 0xFF; + int packetId = packet.getUnsignedByte( 0 ); if ( packetId == 0x1D ) { // bulk entity - for ( int pos = 2; pos < packet.length; pos += 4 ) + for ( int pos = 2; pos < packet.readableBytes(); pos += 4 ) { - int readId = readInt( packet, pos ); + int readId = packet.getInt( pos ); if ( readId == oldId ) { - setInt( packet, pos, newId ); + packet.setInt( pos, newId ); } else if ( readId == newId ) { - setInt( packet, pos, oldId ); + packet.setInt( pos, oldId ); } } } else @@ -140,41 +142,28 @@ public class EntityMap { for ( int pos : idArray ) { - int readId = readInt( packet, pos ); + int readId = packet.getInt( pos ); if ( readId == oldId ) { - setInt( packet, pos, newId ); + packet.setInt( pos, newId ); } else if ( readId == newId ) { - setInt( packet, pos, oldId ); + packet.setInt( pos, oldId ); } } } } if ( packetId == 0x17 ) { - int type = packet[5] & 0xFF; + int type = packet.getUnsignedByte( 5 ); if ( type == 60 || type == 90 ) { - int index20 = readInt( packet, 20 ); - if ( packet.length > 24 && index20 == oldId ) + int index20 = packet.getInt( 20 ); + if ( packet.readableBytes() > 24 && index20 == oldId ) { - setInt( packet, 20, newId ); + packet.setInt( 20, newId ); } } } } - - private static void setInt(byte[] buf, int pos, int i) - { - buf[pos] = (byte) ( i >> 24 ); - buf[pos + 1] = (byte) ( i >> 16 ); - buf[pos + 2] = (byte) ( i >> 8 ); - buf[pos + 3] = (byte) i; - } - - private static int readInt(byte[] buf, int pos) - { - return ( ( ( buf[pos] & 0xFF ) << 24 ) | ( ( buf[pos + 1] & 0xFF ) << 16 ) | ( ( buf[pos + 2] & 0xFF ) << 8 ) | buf[pos + 3] & 0xFF ); - } } diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java index 584feab2..a1105a79 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -30,6 +30,7 @@ import net.md_5.bungee.api.tab.TabListHandler; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.netty.PacketWrapper; import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.protocol.packet.DefinedPacket; import net.md_5.bungee.protocol.packet.Packet3Chat; @@ -126,9 +127,9 @@ public final class UserConnection implements ProxiedPlayer this.tabList = tabList; } - public void sendPacket(byte[] b) + public void sendPacket(PacketWrapper packet) { - ch.write( b ); + ch.write( packet ); } @Deprecated diff --git a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java index 75a4fd9a..09465577 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java @@ -21,6 +21,7 @@ import net.md_5.bungee.api.score.Scoreboard; import net.md_5.bungee.api.score.Team; import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.PacketHandler; +import net.md_5.bungee.netty.PacketWrapper; import net.md_5.bungee.protocol.packet.Packet0KeepAlive; import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem; import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective; @@ -70,12 +71,12 @@ public class DownstreamBridge extends PacketHandler } @Override - public void handle(byte[] buf) throws Exception + public void handle(PacketWrapper packet) throws Exception { if ( !server.isObsolete() ) { - EntityMap.rewrite( buf, con.getServerEntityId(), con.getClientEntityId() ); - con.sendPacket( buf ); + EntityMap.rewrite( packet.buf, con.getServerEntityId(), con.getClientEntityId() ); + con.sendPacket( packet ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java index 60d6741e..f6d38116 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java @@ -10,6 +10,7 @@ import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PluginMessageEvent; import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.PacketHandler; +import net.md_5.bungee.netty.PacketWrapper; import net.md_5.bungee.protocol.packet.Packet0KeepAlive; import net.md_5.bungee.protocol.packet.Packet3Chat; import net.md_5.bungee.protocol.packet.PacketCCSettings; @@ -53,12 +54,12 @@ public class UpstreamBridge extends PacketHandler } @Override - public void handle(byte[] buf) throws Exception + public void handle(PacketWrapper packet) throws Exception { - EntityMap.rewrite( buf, con.getClientEntityId(), con.getServerEntityId() ); + EntityMap.rewrite( packet.buf, con.getClientEntityId(), con.getServerEntityId() ); if ( con.getServer() != null ) { - con.getServer().getCh().write( buf ); + con.getServer().getCh().write( packet ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java index 9e5e8504..e16ae233 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java @@ -22,7 +22,14 @@ public class ChannelWrapper { if ( !closed ) { - ch.write( packet ); + if ( packet instanceof PacketWrapper ) + { + ( (PacketWrapper) packet ).setReleased( true ); + ch.write( ( (PacketWrapper) packet ).buf ); + } else + { + ch.write( packet ); + } ch.flush(); } } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java index 6ddaee3c..3160a501 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java @@ -62,23 +62,27 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter { if ( handler != null ) { - if ( msg instanceof PacketWrapper ) + PacketWrapper packet = (PacketWrapper) msg; + boolean sendPacket = true; + try { - boolean sendPacket = true; - try + if ( packet.packet != null ) { - ( (PacketWrapper) msg ).packet.handle( handler ); - } catch ( CancelSendSignal ex ) - { - sendPacket = false; + try + { + packet.packet.handle( handler ); + } catch ( CancelSendSignal ex ) + { + sendPacket = false; + } } if ( sendPacket ) { - handler.handle( ( (PacketWrapper) msg ).buf ); + handler.handle( packet ); } - } else + } finally { - handler.handle( (byte[]) msg ); + packet.trySingleRelease(); } } } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java b/proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java index 670a7089..47043da4 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java @@ -40,24 +40,11 @@ public class PacketDecoder extends ReplayingDecoder // If we got this far, it means we have formed a packet, so lets grab the end index int endIndex = in.readerIndex(); // Allocate a buffer big enough for all bytes we have read - byte[] buf = new byte[ endIndex - startIndex ]; - // Go back to start index - in.readerIndex( startIndex ); - // Drain all the bytes into our buffer - in.readBytes( buf, 0, buf.length ); - // Jump back to the end of this packet - in.readerIndex( endIndex ); + ByteBuf buf = in.copy( startIndex, endIndex - startIndex ); // Checkpoint our state incase we don't have enough data for another packet checkpoint(); - // Store our decoded message - if ( packet != null ) - { - out.add( new PacketWrapper( packet, buf ) ); - } else - { - out.add( buf ); - } + out.add( new PacketWrapper( packet, buf ) ); } } } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PacketHandler.java b/proxy/src/main/java/net/md_5/bungee/netty/PacketHandler.java index 1440f18c..ca4684ff 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/PacketHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/PacketHandler.java @@ -10,7 +10,7 @@ public abstract class PacketHandler extends net.md_5.bungee.protocol.packet.Abst { } - public void handle(byte[] buf) throws Exception + public void handle(PacketWrapper packet) throws Exception { } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PacketWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/PacketWrapper.java index ed25aa97..b61cfd04 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/PacketWrapper.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/PacketWrapper.java @@ -1,16 +1,25 @@ package net.md_5.bungee.netty; +import io.netty.buffer.ByteBuf; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import net.md_5.bungee.protocol.packet.DefinedPacket; +@RequiredArgsConstructor public class PacketWrapper { - DefinedPacket packet; - byte[] buf; + public final DefinedPacket packet; + public final ByteBuf buf; + @Setter + private boolean released; - public PacketWrapper(DefinedPacket packet, byte[] buf) + public void trySingleRelease() { - this.packet = packet; - this.buf = buf; + if ( !released ) + { + buf.release(); + released = true; + } } } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java index 423e49cb..b98f4df0 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java @@ -50,11 +50,9 @@ public class PipelineUtils }; public static final Base BASE = new Base(); private static final DefinedPacketEncoder packetEncoder = new DefinedPacketEncoder(); - private static final ByteArrayEncoder arrayEncoder = new ByteArrayEncoder(); public static String TIMEOUT_HANDLER = "timeout"; public static String PACKET_DECODE_HANDLER = "packet-decoder"; public static String PACKET_ENCODE_HANDLER = "packet-encoder"; - public static String ARRAY_ENCODE_HANDLER = "array-encoder"; public static String BOSS_HANDLER = "inbound-boss"; public static String ENCRYPT_HANDLER = "encrypt"; public static String DECRYPT_HANDLER = "decrypt"; @@ -76,7 +74,6 @@ public class PipelineUtils ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) ); ch.pipeline().addLast( PACKET_DECODE_HANDLER, new PacketDecoder( Vanilla.getInstance() ) ); ch.pipeline().addLast( PACKET_ENCODE_HANDLER, packetEncoder ); - ch.pipeline().addLast( ARRAY_ENCODE_HANDLER, arrayEncoder ); ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() ); } };