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}