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.

This commit is contained in:
md_5 2013-08-18 20:24:29 +10:00
parent 738ed99d54
commit 99f361ca77
10 changed files with 65 additions and 69 deletions

View File

@ -1,5 +1,7 @@
package net.md_5.bungee; package net.md_5.bungee;
import io.netty.buffer.ByteBuf;
/** /**
* Class to rewrite integers within packets. * 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 ) if ( packetId == 0x1D )
{ // bulk entity { // 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 ) if ( readId == oldId )
{ {
setInt( packet, pos, newId ); packet.setInt( pos, newId );
} else if ( readId == newId ) } else if ( readId == newId )
{ {
setInt( packet, pos, oldId ); packet.setInt( pos, oldId );
} }
} }
} else } else
@ -140,41 +142,28 @@ public class EntityMap
{ {
for ( int pos : idArray ) for ( int pos : idArray )
{ {
int readId = readInt( packet, pos ); int readId = packet.getInt( pos );
if ( readId == oldId ) if ( readId == oldId )
{ {
setInt( packet, pos, newId ); packet.setInt( pos, newId );
} else if ( readId == newId ) } else if ( readId == newId )
{ {
setInt( packet, pos, oldId ); packet.setInt( pos, oldId );
} }
} }
} }
} }
if ( packetId == 0x17 ) if ( packetId == 0x17 )
{ {
int type = packet[5] & 0xFF; int type = packet.getUnsignedByte( 5 );
if ( type == 60 || type == 90 ) if ( type == 60 || type == 90 )
{ {
int index20 = readInt( packet, 20 ); int index20 = packet.getInt( 20 );
if ( packet.length > 24 && index20 == oldId ) 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 );
}
} }

View File

@ -30,6 +30,7 @@ import net.md_5.bungee.api.tab.TabListHandler;
import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.HandlerBoss; 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.netty.PipelineUtils;
import net.md_5.bungee.protocol.packet.DefinedPacket; import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.protocol.packet.Packet3Chat; import net.md_5.bungee.protocol.packet.Packet3Chat;
@ -126,9 +127,9 @@ public final class UserConnection implements ProxiedPlayer
this.tabList = tabList; this.tabList = tabList;
} }
public void sendPacket(byte[] b) public void sendPacket(PacketWrapper packet)
{ {
ch.write( b ); ch.write( packet );
} }
@Deprecated @Deprecated

View File

@ -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.api.score.Team;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.PacketHandler; 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.Packet0KeepAlive;
import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem; import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem;
import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective; import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
@ -70,12 +71,12 @@ public class DownstreamBridge extends PacketHandler
} }
@Override @Override
public void handle(byte[] buf) throws Exception public void handle(PacketWrapper packet) throws Exception
{ {
if ( !server.isObsolete() ) if ( !server.isObsolete() )
{ {
EntityMap.rewrite( buf, con.getServerEntityId(), con.getClientEntityId() ); EntityMap.rewrite( packet.buf, con.getServerEntityId(), con.getClientEntityId() );
con.sendPacket( buf ); con.sendPacket( packet );
} }
} }

View File

@ -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.api.event.PluginMessageEvent;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.PacketHandler; 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.Packet0KeepAlive;
import net.md_5.bungee.protocol.packet.Packet3Chat; import net.md_5.bungee.protocol.packet.Packet3Chat;
import net.md_5.bungee.protocol.packet.PacketCCSettings; import net.md_5.bungee.protocol.packet.PacketCCSettings;
@ -53,12 +54,12 @@ public class UpstreamBridge extends PacketHandler
} }
@Override @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 ) if ( con.getServer() != null )
{ {
con.getServer().getCh().write( buf ); con.getServer().getCh().write( packet );
} }
} }

View File

@ -22,7 +22,14 @@ public class ChannelWrapper
{ {
if ( !closed ) if ( !closed )
{ {
ch.write( packet ); if ( packet instanceof PacketWrapper )
{
( (PacketWrapper) packet ).setReleased( true );
ch.write( ( (PacketWrapper) packet ).buf );
} else
{
ch.write( packet );
}
ch.flush(); ch.flush();
} }
} }

View File

@ -62,23 +62,27 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter
{ {
if ( handler != null ) if ( handler != null )
{ {
if ( msg instanceof PacketWrapper ) PacketWrapper packet = (PacketWrapper) msg;
boolean sendPacket = true;
try
{ {
boolean sendPacket = true; if ( packet.packet != null )
try
{ {
( (PacketWrapper) msg ).packet.handle( handler ); try
} catch ( CancelSendSignal ex ) {
{ packet.packet.handle( handler );
sendPacket = false; } catch ( CancelSendSignal ex )
{
sendPacket = false;
}
} }
if ( sendPacket ) if ( sendPacket )
{ {
handler.handle( ( (PacketWrapper) msg ).buf ); handler.handle( packet );
} }
} else } finally
{ {
handler.handle( (byte[]) msg ); packet.trySingleRelease();
} }
} }
} }

View File

@ -40,24 +40,11 @@ public class PacketDecoder extends ReplayingDecoder<Void>
// If we got this far, it means we have formed a packet, so lets grab the end index // If we got this far, it means we have formed a packet, so lets grab the end index
int endIndex = in.readerIndex(); int endIndex = in.readerIndex();
// Allocate a buffer big enough for all bytes we have read // Allocate a buffer big enough for all bytes we have read
byte[] buf = new byte[ endIndex - startIndex ]; ByteBuf buf = in.copy( startIndex, 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 );
// Checkpoint our state incase we don't have enough data for another packet // Checkpoint our state incase we don't have enough data for another packet
checkpoint(); checkpoint();
// Store our decoded message // Store our decoded message
if ( packet != null ) out.add( new PacketWrapper( packet, buf ) );
{
out.add( new PacketWrapper( packet, buf ) );
} else
{
out.add( buf );
}
} }
} }
} }

View File

@ -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
{ {
} }

View File

@ -1,16 +1,25 @@
package net.md_5.bungee.netty; 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; import net.md_5.bungee.protocol.packet.DefinedPacket;
@RequiredArgsConstructor
public class PacketWrapper public class PacketWrapper
{ {
DefinedPacket packet; public final DefinedPacket packet;
byte[] buf; public final ByteBuf buf;
@Setter
private boolean released;
public PacketWrapper(DefinedPacket packet, byte[] buf) public void trySingleRelease()
{ {
this.packet = packet; if ( !released )
this.buf = buf; {
buf.release();
released = true;
}
} }
} }

View File

@ -50,11 +50,9 @@ public class PipelineUtils
}; };
public static final Base BASE = new Base(); public static final Base BASE = new Base();
private static final DefinedPacketEncoder packetEncoder = new DefinedPacketEncoder(); private static final DefinedPacketEncoder packetEncoder = new DefinedPacketEncoder();
private static final ByteArrayEncoder arrayEncoder = new ByteArrayEncoder();
public static String TIMEOUT_HANDLER = "timeout"; public static String TIMEOUT_HANDLER = "timeout";
public static String PACKET_DECODE_HANDLER = "packet-decoder"; public static String PACKET_DECODE_HANDLER = "packet-decoder";
public static String PACKET_ENCODE_HANDLER = "packet-encoder"; 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 BOSS_HANDLER = "inbound-boss";
public static String ENCRYPT_HANDLER = "encrypt"; public static String ENCRYPT_HANDLER = "encrypt";
public static String DECRYPT_HANDLER = "decrypt"; 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( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) );
ch.pipeline().addLast( PACKET_DECODE_HANDLER, new PacketDecoder( Vanilla.getInstance() ) ); ch.pipeline().addLast( PACKET_DECODE_HANDLER, new PacketDecoder( Vanilla.getInstance() ) );
ch.pipeline().addLast( PACKET_ENCODE_HANDLER, packetEncoder ); ch.pipeline().addLast( PACKET_ENCODE_HANDLER, packetEncoder );
ch.pipeline().addLast( ARRAY_ENCODE_HANDLER, arrayEncoder );
ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() ); ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );
} }
}; };