To test the performance of CoralFIX + CoralReactor we have developed a simple test with a fix server and a fix client. The client connects to the server, the standard FIX handshake through LOGON messages takes place and the client proceeds to send (i.e. push) 5 million FIX messages to the server as fast as it can. Then the server receives and processes all the messages calculating the throughput. So we are are measuring the one-way throughput over loopback. For a latency benchmark instead you can check this article. Below we present the throughput results and the source code.
NOTE: If you want to check the benchmark of just the CoralFIX parser without any network I/O code (i.e. without CoralReactor) you can check this article.
The Results
Messages: 5,000,000 (one-way) Throughput: 279,314 msgs/sec
The machine used for the benchmarks was an Intel i7 (4 x 3.5GHz) Ubuntu box overclocked to 4.5GHz.
The Source Code
The server:
package com.coralblocks.coralfix.bench.throughput; import com.coralblocks.coralfix.FixMessage; import com.coralblocks.coralfix.server.FixApplicationServer; import com.coralblocks.coralreactor.client.Client; import com.coralblocks.coralreactor.nio.NioReactor; import com.coralblocks.coralreactor.server.Server; import com.coralblocks.coralreactor.util.Configuration; import com.coralblocks.coralreactor.util.MapConfiguration; public class FixThroughputServer extends FixApplicationServer { // java -Xms6g -Xmx8g -server -verbose:gc -Xbootclasspath/p:../CoralReactor-boot-jdk7/target/coralreactor-boot-jdk7.jar -cp target/coralfix-all.jar:lib/jna-3.5.1.jar -DpersistSequences=false -DnioReactorProcToBind=2 com.coralblocks.coralfix.bench.throughput.FixThroughputServer private long start; private long msgCount; public FixThroughputServer(NioReactor nio, int port, Configuration config) { super(nio, port, config); } @Override protected void handleFixApplicationMessage(Client client, FixMessage fixMsg, boolean possDupe) { msgCount++; } @Override protected void handleConnectionEstablished(Client client) { msgCount = 0; } @Override protected void handleConnectionOpened(Client client) { start = System.nanoTime(); } @Override protected void handleConnectionTerminated(Client client) { if (msgCount > 0) { long totalTime = System.nanoTime() - start; long latency = totalTime / msgCount; long ops = msgCount * 1000000000L / totalTime; System.out.println("Done receiving messages! messagesReceived=" + msgCount + " avgLatencyPerMsg=" + latency + " nanos throughput=" + ops + " msgs/sec"); } } public static void main(String[] args) { NioReactor nio = NioReactor.create(); MapConfiguration config = new MapConfiguration(); config.add("fixVersion", 44); Server server = new FixThroughputServer(nio, 45451, config); server.open(); nio.start(); } }
The client:
package com.coralblocks.coralfix.bench.throughput; import java.nio.channels.SelectionKey; import java.nio.channels.WritableByteChannel; import com.coralblocks.coralbits.util.SystemUtils; import com.coralblocks.coralfix.FixConstants; import com.coralblocks.coralfix.FixMessage; import com.coralblocks.coralfix.FixTags; import com.coralblocks.coralfix.client.FixApplicationClient; import com.coralblocks.coralreactor.client.Client; import com.coralblocks.coralreactor.nio.NioReactor; import com.coralblocks.coralreactor.util.Configuration; import com.coralblocks.coralreactor.util.MapConfiguration; public class FixThroughputClient extends FixApplicationClient { // java -Xms6g -Xmx8g -server -verbose:gc -Xbootclasspath/p:../CoralReactor-boot-jdk7/target/coralreactor-boot-jdk7.jar -cp target/coralfix-all.jar:lib/jna-3.5.1.jar -DpersistSequences=false -DnioReactorProcToBind=3 com.coralblocks.coralfix.bench.throughput.FixThroughputClient private final int messagesToSend; private final int batchSize; private int msgCount; private long start; public FixThroughputClient(NioReactor nio, String host, int port, Configuration config) { super(nio, host, port, config); this.messagesToSend = config.getInt("messagesToSend"); this.batchSize = config.getInt("batchSize"); } @Override protected void handleConnectionEstablished() { msgCount = 0; } @Override protected void handleConnectionOpened() { start = System.nanoTime(); addInterestOp(SelectionKey.OP_WRITE); } @Override public void onWrite(WritableByteChannel channel) { for(int i = 0; i < batchSize; i++) { if (msgCount < messagesToSend) { FixMessage fixOutMsg = getOutFixMessage(FixConstants.MsgTypes.ExecutionReport); fixOutMsg.addTimestamp(FixTags.TransactTime, nio.currentTimeMillis()); fixOutMsg.add(FixTags.Account, "01234567"); fixOutMsg.add(FixTags.OrderQty, 50); fixOutMsg.add(FixTags.Price, 400.5); fixOutMsg.add(FixTags.ClOrdID, "4"); fixOutMsg.add(FixTags.HandlInst, '1'); fixOutMsg.add(FixTags.OrdType, '2'); fixOutMsg.add(FixTags.Side, '1'); fixOutMsg.add(FixTags.Symbol, "OC"); fixOutMsg.add(FixTags.Text, "NIGEL"); fixOutMsg.add(FixTags.TimeInForce, '0'); fixOutMsg.add(FixTags.SecurityDesc, "AOZ3 C02000"); fixOutMsg.add(FixTags.SecurityType, 'O'); if (!send(fixOutMsg)) { // underlying kernel write socket buffer is full // sending too fast and/or the other side is lagging return; // abort and wait for OP_WRITE } msgCount++; } else { if (hasPendingDataToFlush()) { flush(); } else { printResults(); disconnect(); return; } } } } private void printResults() { long totalTime = System.nanoTime() - start; long latency = totalTime / msgCount; long ops = msgCount * 1000000000L / totalTime; System.out.println("Done sending messages! messagesSent=" + msgCount + " avgLatencyPerMsg=" + latency + " nanos throughput=" + ops + " msgs/sec"); } public static void main(String[] args) { NioReactor nio = NioReactor.create(); int messagesToSend = SystemUtils.getInt("messagesToSend", 5000000); int batchSize = SystemUtils.getInt("batchSize", 128); MapConfiguration config = new MapConfiguration(); config.add("fixVersion", 44); config.add("senderComp", "testSenderCompID"); config.add("messagesToSend", messagesToSend); config.add("batchSize", batchSize); Client client = new FixThroughputClient(nio, "localhost", 45451, config); client.open(); nio.start(); } }
Conclusion
CoralFIX + CoralReactor can easily process more than 250,000 FIX messages per second.