Repository: penberg/falcon Branch: master Commit: 5ee923330896 Files: 24 Total size: 30.2 KB Directory structure: gitextract__2rp4aw1/ ├── .gitignore ├── LICENSE ├── README.md ├── falcon/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── falcon/ │ │ └── fix/ │ │ ├── ByteString.java │ │ ├── Field.java │ │ ├── Message.java │ │ ├── MessageType.java │ │ ├── MessageTypes.java │ │ ├── ParseException.java │ │ ├── ParseFailedException.java │ │ ├── PartialParseException.java │ │ ├── Protocol.java │ │ ├── Session.java │ │ ├── Tags.java │ │ ├── Version.java │ │ ├── Versions.java │ │ └── package-info.java │ └── test/ │ └── java/ │ └── falcon/ │ └── fix/ │ ├── MemoryMeterTest.java │ └── ProtocolTest.java ├── falcon-perf-test/ │ ├── bin/ │ │ └── falcon-perf-test │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── falcon/ │ └── fix/ │ └── perf/ │ └── ClientPerfTest.java └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ target ================================================ FILE: LICENSE ================================================ Copyright (C) 2013 Pekka Enberg Falcon is distributed under the 2-clause BSD license: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ # Falcon Falcon is a high performance, low latency FIX engine for the JVM. It provides an API that enables FIX connectivity for both buy side and sell side applications such as trading systems and order management systems. The engine is designed to avoid heap allocations on the TX and RX paths to avoid GC pauses that are disastrous for low-latency applications. The engine is packed with other optimizations such as avoiding querying the system clock for every message and open-coding formatting and parsing functions where the JRE allocates memory implicitly. Falcon is able to achieve 8 µs RTT on when running the latency tester client and server on the same machine. ## Features * Zero-copy, non-blocking, low-latency NIO networking * Low heap allocation rate in the FIX engine core * Small memory footprint for session and message data structures ## Example An example application that sends 100000 ``NewOrderSingle`` messages looks like this: ```java import static java.net.StandardSocketOptions.*; import java.nio.channels.*; import java.net.*; import java.nio.*; import static falcon.fix.MessageTypes.*; import static falcon.fix.Versions.*; import static falcon.fix.Tags.*; import falcon.fix.*; public class Example { public static void main(String[] args) throws Exception { SocketChannel socket = connect("localhost", 7070); Session session = new Session(socket, FIX_4_2, "HERMES", "INET"); session.updateTime(); session.send(new Message.Builder(Logon) .add(new Field(EncryptMethod, "0" )) .add(new Field(HeartBtInt, "30")) .build()); Message newOrder = new Message.Builder(NewOrderSingle) .add(new Field(EncryptMethod, "0" )) .add(new Field(HeartBtInt, "30")) .build(); for (int i = 0; i < 100000; i++) { if ((i % 10000) == 0) { session.updateTime(); } session.send(newOrder); } session.updateTime(); session.send(new Message.Builder(Logout).build()); socket.close(); } private static SocketChannel connect(String host, int port) throws Exception { InetSocketAddress addr = new InetSocketAddress(host, port); SocketChannel socket = SocketChannel.open(); socket.configureBlocking(false); socket.setOption(TCP_NODELAY, true); socket.connect(addr); socket.finishConnect(); return socket; } } ``` ## Performance The FIX engine has been measured to have 8 µs RTT for a loopback ping-pong test where client sends a ``NewOrderSingle`` message and waits for an ``ExecutionReport`` message to arrive. The numbers include the time spent in Linux TCP/IP stack and the loopback device. To reproduce the results, first download and build [Libtrading]. Then start the FIX performance test server: ``` $ taskset -c 0 tools/fix/fix_server -m 1 -p 7070 ``` Finally, run the Falcon latency tests: ``` $ ./falcon-perf-test/bin/falcon-perf-test 1000000 87693.5 messages/second min/avg/max = 9.8/11.4/19935.3 µs Percentiles: 1.00%: 10.15 µs 5.00%: 10.51 µs 10.00%: 10.61 µs 50.00%: 11.12 µs 90.00%: 11.90 µs 95.00%: 13.27 µs 99.00%: 14.53 µs ``` [Libtrading]: https://github.com/libtrading/libtrading ## License Copyright © 2013-2015 Pekka Enberg and contributors Falcon is distributed under the 2-clause BSD license. ================================================ FILE: falcon/pom.xml ================================================ 4.0.0 falcon falcon-parent 0.1.0-master-SNAPSHOT falcon falcon Falcon com.github.stephenc jamm test junit junit test org.apache.maven.plugins maven-dependency-plugin copy-dependencies generate-test-resources copy com.github.stephenc jamm jar ${project.build.directory} jamm.jar org.apache.maven.plugins maven-surefire-plugin -javaagent:${project.build.directory}/jamm.jar ================================================ FILE: falcon/src/main/java/falcon/fix/ByteString.java ================================================ package falcon.fix; import java.nio.ByteBuffer; public final class ByteString { final byte[] data; /** * Returns a new byte string that contains a copy of len bytes from buf. */ public static ByteString of(ByteBuffer buf, int len) { byte[] data = new byte[len]; buf.get(data); return new ByteString(data); } public ByteString(byte[] data) { this.data = data; } } ================================================ FILE: falcon/src/main/java/falcon/fix/Field.java ================================================ package falcon.fix; import java.nio.ByteBuffer; /** * A field is composed of a tag and value pair. */ public class Field { private int tag; private Object value; public Field(int tag, Object value) { this.tag = tag; this.value = value; } public int tag() { return tag; } public Object value() { return value; } } ================================================ FILE: falcon/src/main/java/falcon/fix/Message.java ================================================ package falcon.fix; import static falcon.fix.Tags.*; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; /** * A message is composed of number of fields, grouped in a header, body, and a * trailer. */ public class Message { private List fields; private MessageType type; /** * Constructs a message of specified type and fields. * * @param type Type of this message. * @param fields Fields for this message. * * The constructed message has a header and a trailer, and the specified fields. */ public Message(MessageType type, List fields) { this.type = type; this.fields = fields; } public MessageType type() { return type; } public List fields() { return fields; } /** * Message builder. */ public static class Builder { private List fields = new ArrayList<>(); private MessageType type; public Builder(MessageType type) { this.type = type; } public Builder add(Field field) { fields.add(field); return this; } public Message build() { return new Message(type, fields); } } } ================================================ FILE: falcon/src/main/java/falcon/fix/MessageType.java ================================================ package falcon.fix; /** * A message type specifies which fields a message contains. */ public class MessageType { private String value; public MessageType(String value) { this.value = value; } public String value() { return value; } } ================================================ FILE: falcon/src/main/java/falcon/fix/MessageTypes.java ================================================ package falcon.fix; public class MessageTypes { public static final MessageType Heartbeat = new MessageType("0"); public static final MessageType TestRequest = new MessageType("1"); public static final MessageType ResendRequest = new MessageType("2"); public static final MessageType Reject = new MessageType("3"); public static final MessageType SequenceReset = new MessageType("4"); public static final MessageType Logout = new MessageType("5"); public static final MessageType ExecutionReport= new MessageType("8"); public static final MessageType Logon = new MessageType("A"); public static final MessageType NewOrderSingle = new MessageType("D"); } ================================================ FILE: falcon/src/main/java/falcon/fix/ParseException.java ================================================ package falcon.fix; public class ParseException extends Exception { public ParseException() { } public ParseException(String message) { super(message); } } ================================================ FILE: falcon/src/main/java/falcon/fix/ParseFailedException.java ================================================ package falcon.fix; public class ParseFailedException extends ParseException { public ParseFailedException(String message) { super(message); } } ================================================ FILE: falcon/src/main/java/falcon/fix/PartialParseException.java ================================================ package falcon.fix; public class PartialParseException extends ParseException { } ================================================ FILE: falcon/src/main/java/falcon/fix/Protocol.java ================================================ package falcon.fix; import static falcon.fix.MessageTypes.*; import java.nio.ByteBuffer; /** * On-wire protocol parsing and formatting. */ public class Protocol { /* * Maximum message header size in bytes. */ public static final int MAX_HEADER_SIZE = 64; /* * Maximum message body size in bytes. */ public static final int MAX_BODY_SIZE = 4096; /** * Format field to on-wire format. */ public static void format(ByteBuffer buf, int tag, byte[] value) { writeInt(buf, tag); buf.put((byte) '='); buf.put(value); buf.put((byte) 0x01); } /** * Format field to on-wire format. */ public static void formatInt(ByteBuffer buf, int tag, int value) { writeInt(buf, tag); buf.put((byte) '='); writeInt(buf, value); buf.put((byte) 0x01); } public static void formatString(ByteBuffer buf, int tag, String value) { writeInt(buf, tag); buf.put((byte) '='); for (int i = 0; i < value.length(); i++) { buf.put((byte) value.charAt(i)); } buf.put((byte) 0x01); } public static void formatCheckSum(ByteBuffer buf, int tag, int value) { writeInt(buf, tag); buf.put((byte) '='); if (value < 10) { buf.put((byte) '0'); } if (value < 100) { buf.put((byte) '0'); } writeInt(buf, value); buf.put((byte) 0x01); } public static void writeInt(ByteBuffer buf, int n) { if (n < 0) { buf.put((byte) '-'); } n = Math.abs(n); int start = buf.position(); do { buf.put((byte)('0' + n % 10)); n /= 10; } while (n > 0); int end = buf.position(); int i = start; int j = end - 1; while (i < j) { byte tmp = buf.get(i); buf.put(i, buf.get(j)); buf.put(j, tmp); i++; j--; } } public static void match(ByteBuffer buf, int tag) throws ParseException { matchTag(buf, tag); while (buf.get() != (byte)0x01) ;; } public static int matchInt(ByteBuffer buf, int tag) throws ParseException { matchTag(buf, tag); return parseInt(buf, (byte)0x01); } public static int parseInt(ByteBuffer buf, byte delimiter) { int sign = 1; if (buf.get(buf.position()) == (byte)'-') { buf.get(); sign = -1; } int result = 0; for (;;) { byte ch = buf.get(); if (ch == delimiter) { break; } result *= 10; result += (byte)ch - (byte)'0'; } return sign * result; } public static ByteString parseString(ByteBuffer buf, byte delimiter) { int start = buf.position(); for (;;) { byte ch = buf.get(); if (ch == delimiter) { break; } } int end = buf.position() - 1; buf.position(start); return ByteString.of(buf, end-start); } public static MessageType matchMsgType(ByteBuffer buf) throws ParseException { matchTag(buf, Tags.MsgType); MessageType result = null; switch (buf.get()) { case (byte)'0': result = Heartbeat; break; case (byte)'1': result = TestRequest; break; case (byte)'2': result = ResendRequest; break; case (byte)'3': result = Reject; break; case (byte)'4': result = SequenceReset; break; case (byte)'5': result = Logout; break; case (byte)'8': result = ExecutionReport; break; case (byte)'A': result = Logon; break; case (byte)'D': result = NewOrderSingle; break; case (byte)0x01: throw new ParseFailedException("Invalid MsgType (35)"); default: throw new ParseFailedException("Tag specified without a value"); } if (buf.get() != (byte)0x01) { throw new ParseFailedException("Invalid MsgType (35)"); } return result; } public static void matchTag(ByteBuffer buf, int tag) throws ParseException { int actual = parseInt(buf, (byte)'='); if (actual != tag) { throw new ParseFailedException("Required tag missing"); } } } ================================================ FILE: falcon/src/main/java/falcon/fix/Session.java ================================================ package falcon.fix; import static falcon.fix.Tags.*; import java.nio.channels.*; import java.text.*; import java.util.*; import java.nio.*; /** * A session is bi-directional stream of messages between two parties where * message ordering is guaranteed with monotonically increasing message * sequence numbers. */ public class Session { private ByteBuffer headBuf = ByteBuffer.allocate(Protocol.MAX_HEADER_SIZE); private ByteBuffer bodyBuf = ByteBuffer.allocate(Protocol.MAX_BODY_SIZE); private ByteBuffer rxBuf = ByteBuffer.allocate(Protocol.MAX_HEADER_SIZE + Protocol.MAX_BODY_SIZE); private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HH:mm:ss.SSS"); private SocketChannel socket; private String senderCompId; private String targetCompId; private Version version; private int sequence; private String now; public Session(SocketChannel socket, Version version, String senderCompId, String targetCompId) { this.socket = socket; this.version = version; this.senderCompId = senderCompId; this.targetCompId = targetCompId; } public void updateTime() { now = dateFormat.format(new Date()); } public void send(Message msg) throws Exception { Protocol.formatString(bodyBuf, MsgType, msg.type().value()); Protocol.formatString(bodyBuf, SenderCompID, senderCompId); Protocol.formatString(bodyBuf, TargetCompID, targetCompId); Protocol.formatInt(bodyBuf, MsgSeqNum, sequence++); Protocol.formatString(bodyBuf, SendingTime, now); List fields = msg.fields(); for (int i = 0; i < fields.size(); i++) { Field field = fields.get(i); Object value = field.value(); if (value instanceof String) { Protocol.formatString(bodyBuf, field.tag(), (String) field.value()); } else if (value instanceof Integer) { Protocol.formatInt(bodyBuf, field.tag(), ((Integer) field.value()).intValue()); } else { throw new IllegalStateException(); } } Protocol.format(headBuf, BeginString, version.value()); Protocol.formatInt(headBuf, BodyLength, bodyBuf.position()); Protocol.formatCheckSum(bodyBuf, CheckSum, (sum(headBuf) + sum(bodyBuf)) % 256); headBuf.flip(); bodyBuf.flip(); while (headBuf.remaining() > 0) { socket.write(headBuf); } while (bodyBuf.remaining() > 0) { socket.write(bodyBuf); } headBuf.clear(); bodyBuf.clear(); } public Message recv() throws Exception { int count = socket.read(rxBuf); if (count <= 0) { return null; } rxBuf.flip(); rxBuf.mark(); List fields = new ArrayList(); MessageType msgType = null; try { Protocol.match(rxBuf, BeginString); int bodyLen = Protocol.matchInt(rxBuf, BodyLength); int msgTypeOffset = rxBuf.position(); msgType = Protocol.matchMsgType(rxBuf); int checksumOffset = msgTypeOffset + bodyLen; rxBuf.position(checksumOffset); int checksumActual = sum(rxBuf) % 256; int checksumExpected = Protocol.matchInt(rxBuf, CheckSum); if (checksumExpected != checksumActual) { throw new RuntimeException(String.format("Invalid checksum: expected %d, got: %d", checksumExpected, checksumActual)); } int endOffset = rxBuf.position(); rxBuf.position(msgTypeOffset); while (rxBuf.position() < checksumOffset) { int tag = Protocol.parseInt(rxBuf, (byte)'='); ByteString value = Protocol.parseString(rxBuf, (byte)0x01); fields.add(new Field(tag, value)); } rxBuf.position(endOffset); } catch (PartialParseException e) { rxBuf.reset(); return null; } catch (ParseFailedException e) { throw new RuntimeException("Garbled message", e); } rxBuf.compact(); return new Message(msgType, fields); } private static int sum(ByteBuffer buf) { return sum(buf, 0, buf.position()); } private static int sum(ByteBuffer buf, int start, int end) { int result = 0; for (int i = start; i < end; i++) result += buf.get(i); return result; } } ================================================ FILE: falcon/src/main/java/falcon/fix/Tags.java ================================================ package falcon.fix; public class Tags { public static final int BeginString = 8; public static final int BodyLength = 9; public static final int CheckSum = 10; public static final int MsgSeqNum = 34; public static final int MsgType = 35; public static final int SenderCompID = 49; public static final int SendingTime = 52; public static final int TargetCompID = 56; public static final int EncryptMethod = 98; public static final int HeartBtInt = 108; } ================================================ FILE: falcon/src/main/java/falcon/fix/Version.java ================================================ package falcon.fix; /** * A version specifies which base standard is followed in a session. * * Session version is specified in the BeginString tag in message * header. Although not supported by the standard, many dialects are a * combination of two or more versions. BeginString thus only represents * the base specification and message types and tags are typically specified in * proprietary trading venue specifications. Session level semantics usually * follow the specified base standard, but not always. */ public class Version { private byte[] value; public Version(String value) { this(value.getBytes()); } public Version(byte[] value) { this.value = value; } public byte[] value() { return value; } } ================================================ FILE: falcon/src/main/java/falcon/fix/Versions.java ================================================ package falcon.fix; public class Versions { /** * FIX 4.1 based sessions. */ public static Version FIX_4_1 = new Version("FIX.4.1"); /** * FIX 4.2 based sessions. */ public static Version FIX_4_2 = new Version("FIX.4.2"); /** * FIX 4.3 based sessions. */ public static Version FIX_4_3 = new Version("FIX.4.3"); /** * FIX 4.4 based sessions. */ public static Version FIX_4_4 = new Version("FIX.4.4"); } ================================================ FILE: falcon/src/main/java/falcon/fix/package-info.java ================================================ /** * Falcon FIX core classes. */ package falcon.fix; ================================================ FILE: falcon/src/test/java/falcon/fix/MemoryMeterTest.java ================================================ package falcon.fix; import static falcon.fix.MessageTypes.*; import static falcon.fix.Versions.*; import static falcon.fix.Tags.*; import org.github.jamm.MemoryMeter; import java.nio.channels.*; import org.junit.Test; public class MemoryMeterTest { private MemoryMeter meter = new MemoryMeter(); @Test public void footprint() throws Exception { Session session = new Session(SocketChannel.open(), FIX_4_2, "HERMES", "INET"); measure(session); Message msg = new Message.Builder(Logon) .add(new Field(EncryptMethod, "0" )) .add(new Field(HeartBtInt, "30")) .build(); measure(msg); Field field = new Field(EncryptMethod, "0" ); measure(field); } private void measure(Object obj) { System.out.printf("Memory footprint of '%s':\n\n", obj.getClass().getName()); System.out.printf(" Size (shallow): %d bytes\n", meter.measure(obj)); System.out.printf(" Size (deep) : %d bytes\n", meter.measureDeep(obj)); System.out.printf(" Child objects : %d\n", meter.countChildren(obj)); System.out.printf("\n"); } } ================================================ FILE: falcon/src/test/java/falcon/fix/ProtocolTest.java ================================================ package falcon.fix; import static org.junit.Assert.*; import java.nio.charset.*; import org.junit.Test; import java.nio.*; public class ProtocolTest { @Test public void writeInt() throws Exception { assertEquals("-123", writeInt(-123)); assertEquals( "0", writeInt(0)); assertEquals( "123", writeInt(123)); } private static String writeInt(int n) throws Exception { ByteBuffer buf = ByteBuffer.allocate(128); Protocol.writeInt(buf, n); buf.flip(); byte[] bytes = new byte[buf.remaining()]; buf.get(bytes); return new String(bytes, "ASCII"); } @Test public void parseInt() throws Exception { assertEquals(-123, parseInt("-123\1")); assertEquals( 0, parseInt("0\1")); assertEquals( 123, parseInt("123\1")); } private static int parseInt(String s) throws Exception { Charset charset = Charset.forName("UTF-8"); CharsetEncoder encoder = charset.newEncoder(); ByteBuffer buf = encoder.encode(CharBuffer.wrap(s)); return Protocol.parseInt(buf, (byte)0x01); } } ================================================ FILE: falcon-perf-test/bin/falcon-perf-test ================================================ #!/bin/sh if [ -z "$JAVACMD" ] ; then if [ -z "$JAVA_HOME" ] ; then JAVACMD='java' else JAVACMD="$JAVA_HOME/bin/java" fi fi java_class=falcon.fix.perf.ClientPerfTest JAVA_OPTS="-verbose:gc -cp falcon-perf-test/target/falcon-perf-test-0.1.0-master-SNAPSHOT-jar-with-dependencies.jar" $JAVACMD $JAVA_OPTS $java_class $@ ================================================ FILE: falcon-perf-test/pom.xml ================================================ 4.0.0 falcon falcon-parent 0.1.0-master-SNAPSHOT falcon falcon-perf-test Falcon Performance Test falcon falcon ${project.version} org.hdrhistogram HdrHistogram 2.1.4 org.apache.maven.plugins maven-jar-plugin falcon.fix.perf.ClientPerfTest org.apache.maven.plugins maven-assembly-plugin falcon-jar package single jar-with-dependencies falcon.fix.perf.ClientPerfTest ================================================ FILE: falcon-perf-test/src/main/java/falcon/fix/perf/ClientPerfTest.java ================================================ package falcon.fix.perf; import static java.net.StandardSocketOptions.TCP_NODELAY; import static falcon.fix.MessageTypes.*; import static falcon.fix.Versions.*; import org.HdrHistogram.Histogram; import static falcon.fix.Tags.*; import java.nio.channels.*; import java.net.*; import java.nio.*; import falcon.fix.*; public class ClientPerfTest { public static void main(String[] args) throws Exception { if (args.length != 1) { System.out.printf(" usage: %s \n", ClientPerfTest.class.getSimpleName()); System.exit(1); } int iterations = Integer.parseInt(args[0]); SocketChannel socket = connect("localhost", 7070); Session session = new Session(socket, FIX_4_2, "HERMES", "INET"); session.updateTime(); session.send(new Message.Builder(Logon) .add(new Field(EncryptMethod, "0" )) .add(new Field(HeartBtInt, "30")) .build()); Message newOrder = new Message.Builder(NewOrderSingle) .add(new Field(EncryptMethod, "0" )) .add(new Field(HeartBtInt, "30")) .build(); long duration = 0; long min = Long.MAX_VALUE; long max = 0; Histogram histogram = new Histogram(3); for (int i = 0; i < iterations; i++) { long iterationStart = System.nanoTime(); if ((i % 10000) == 0) { session.updateTime(); } session.send(newOrder); for (;;) { Message msg = session.recv(); if (msg != null) { break; } } long iterationEnd = System.nanoTime(); long iterationTime = iterationEnd - iterationStart; duration += iterationTime; histogram.recordValue(iterationTime); min = Math.min(min, iterationTime); max = Math.max(max, iterationTime); } long end = System.nanoTime(); session.updateTime(); session.send(new Message.Builder(Logout).build()); socket.close(); double avg = (double)duration / (double)iterations; double seconds = (double)duration / 1000000000.0; System.out.printf("%f seconds\n", seconds); System.out.printf("%.1f messages/second\n", (double)iterations/seconds); System.out.printf("min/avg/max = %.1f/%.1f/%.1f µs\n", (double)min / 1000.0, (double)avg / 1000.0, (double)max / 1000.0); System.out.printf("Percentiles:\n"); System.out.printf(" 1.00%%: %.2f µs\n", nanosToMicros(histogram.getValueAtPercentile( 1.00))); System.out.printf(" 5.00%%: %.2f µs\n", nanosToMicros(histogram.getValueAtPercentile( 5.00))); System.out.printf(" 10.00%%: %.2f µs\n", nanosToMicros(histogram.getValueAtPercentile( 10.00))); System.out.printf(" 50.00%%: %.2f µs\n", nanosToMicros(histogram.getValueAtPercentile( 50.00))); System.out.printf(" 90.00%%: %.2f µs\n", nanosToMicros(histogram.getValueAtPercentile( 90.00))); System.out.printf(" 95.00%%: %.2f µs\n", nanosToMicros(histogram.getValueAtPercentile( 95.00))); System.out.printf(" 99.00%%: %.2f µs\n", nanosToMicros(histogram.getValueAtPercentile( 99.00))); } private static SocketChannel connect(String host, int port) throws Exception { InetSocketAddress addr = new InetSocketAddress(host, port); SocketChannel socket = SocketChannel.open(); socket.configureBlocking(false); socket.setOption(TCP_NODELAY, true); socket.connect(addr); while (!socket.finishConnect()); return socket; } private static double nanosToMicros(double nano) { return nano / 1000.0; } } ================================================ FILE: pom.xml ================================================ 4.0.0 falcon falcon-parent Falcon (parent) pom 0.1.0-master-SNAPSHOT http://github.com/penberg/falcon/ FIX engine for the JVM penberg Pekka Enberg penberg@iki.fi The Apache Software License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo UTF-8 scm:git:git@github.com:penberg/falcon.git scm:git:git@github.com:penberg/falcon.git git@github.com:penberg/falcon.git falcon falcon-perf-test com.github.stephenc jamm 0.2.5 junit junit 4.10 org.apache.maven.plugins maven-compiler-plugin 3.1 org.apache.maven.plugins maven-dependency-plugin 2.8 org.apache.maven.plugins maven-surefire-plugin 2.16 org.apache.maven.plugins maven-jar-plugin 2.4 org.apache-maven-plugins maven-assembly-plugin 2.4 org.apache.maven.plugins maven-compiler-plugin 1.7 1.7 ${project.build.sourceEncoding}