MqttEncoder optimisation proposal
Munoon opened this issue · comments
Current MqttEncoder
make a lot of class casts, which can be improved with the methods, that returns concreete type.
For example, this code:
May be refactored as follow:
switch (message.fixedHeader().messageType()) {
case CONNECT:
return encodeConnectMessage(ctx, message.asConnectMessage());
Where default implementation of asConnectMessage
method throws ClassCastException
while MqttConnectMessage
overrides this method and return this
.
A simple benchmark shows, that such refactoring sligtly improves the performance:
Benchmark Mode Cnt Score Error Units
MqttEncoderBench.doEncode thrpt 20 407587,038 ± 21522,217 ops/s
MqttEncoderBench.doEncodeWithTypeMethods thrpt 20 419308,953 ± 10095,722 ops/s
Benchmark code
package io.netty.handler.codec.mqtt;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.microbench.channel.EmbeddedChannelHandlerContext;
import io.netty.microbench.util.AbstractMicrobenchmark;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
@State(Scope.Benchmark)
public class MqttEncoderBench extends AbstractMicrobenchmark {
MqttMessage[] messages;
ChannelHandlerContext context;
@Setup
public void initData() {
messages = new MqttMessage[] {
MqttMessageBuilders.connect().clientId("testClientId").build(),
MqttMessageBuilders.connAck().returnCode(MqttConnectReturnCode.CONNECTION_ACCEPTED).build(),
MqttMessageBuilders.subscribe().messageId(1).addSubscription(MqttQoS.AT_MOST_ONCE, "/test").build(),
MqttMessageBuilders.subAck().packetId(1).addGrantedQos(MqttQoS.AT_MOST_ONCE).build(),
MqttMessageBuilders.publish().topicName("/test").payload(Unpooled.EMPTY_BUFFER).qos(MqttQoS.AT_LEAST_ONCE).build(),
MqttMessageBuilders.disconnect().build(),
MqttMessage.PINGREQ,
MqttMessage.PINGRESP
};
ByteBufAllocator alloc = UnpooledByteBufAllocator.DEFAULT;
ChannelHandler handler = new MqttDecoder();
EmbeddedChannel channel = new EmbeddedChannel();
context = new EmbeddedChannelHandlerContext(alloc, handler, channel) {
@Override
protected void handleException(Throwable t) {
t.printStackTrace();
}
};
}
@Benchmark
public void doEncode(Blackhole blackhole) {
for (MqttMessage message : messages) {
ByteBuf byteBuf = MqttEncoder.doEncode(context, message);
byteBuf.release();
blackhole.consume(byteBuf);
}
}
@Benchmark
public void doEncodeWithTypeMethods(Blackhole blackhole) {
for (MqttMessage message : messages) {
ByteBuf byteBuf = MqttEncoder.doEncodeTypeMethod(context, message);
byteBuf.release();
blackhole.consume(byteBuf);
}
}
}
If this proposal look good for you, I'd be happy to create a pull request.
I think what we have now is more clean and also the perf win is not huge.