Skip to content

Commit 884fb77

Browse files
authored
Merge pull request #582 from busy-spin/bugfix/issue-576-perf-regression
Benchmarking code to check for performance regressions
2 parents 6a68735 + e0fa235 commit 884fb77

File tree

8 files changed

+327
-5
lines changed

8 files changed

+327
-5
lines changed

pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
<module>quickfixj-examples</module>
6363
<module>quickfixj-all</module>
6464
<module>quickfixj-distribution</module>
65+
<module>quickfixj-perf-test</module>
6566
</modules>
6667

6768
<properties>
@@ -109,6 +110,7 @@
109110
<org.quickfixj.orchestra.tools.version>1.0.2</org.quickfixj.orchestra.tools.version>
110111
<proxool.version>0.9.1</proxool.version>
111112
<jaxen.version>2.0.0</jaxen.version>
113+
<jmh.version>1.36</jmh.version>
112114
</properties>
113115

114116
<dependencyManagement>

quickfixj-distribution/pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,11 @@
122122
<artifactId>quickfixj-messages-fix40</artifactId>
123123
<version>${project.version}</version>
124124
</dependency>
125-
<dependency>
126-
<groupId>com.cloudhopper.proxool</groupId>
127-
<artifactId>proxool</artifactId>
128-
<optional>true</optional>
129-
</dependency>
125+
<dependency>
126+
<groupId>org.quickfixj</groupId>
127+
<artifactId>quickfixj-perf-test</artifactId>
128+
<version>${project.version}</version>
129+
</dependency>
130130
<dependency>
131131
<groupId>com.sleepycat</groupId>
132132
<artifactId>je</artifactId>

quickfixj-perf-test/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# QuickFIX/J Performance test
2+
3+
This is a [JMH](https://github.com/openjdk/jmh) benchmark module for QuickFIX/J FIX protocol implementation.
4+
5+
## How to run
6+
7+
### Using your favorite IDE
8+
9+
Performance regression classes can be individually run using your favorite IDE.
10+
11+
### Creating executable jar for the performance testing
12+
13+
Build executable jar using following maven command
14+
15+
```
16+
$ mvn clean package
17+
```
18+
19+
Use following command to run complete set of performance regression test cases
20+
21+
```
22+
$ java -jar target/quickfixj-perf-test.jar
23+
```
24+
25+
You can list available performance benchmarks using `-l` option
26+
```
27+
$ java -jar target/quickfixj-perf-test.jar -l
28+
```
29+
30+
You can run individual benchmarks by providing the class name or benchmark method name
31+
```
32+
$ java -jar target/quickfixj-perf-test.jar MessageCrackerPerfTest.crack
33+
34+
$ java -jar target/quickfixj-perf-test.jar MessageCrackerPerfTest
35+
```
36+
37+
You can change the time unit used in the benchmark test using `-tu` option
38+
Following command is an example of using micro second for describing benchmark test results.
39+
```
40+
$ java -jar target/quickfixj-perf-test.jar MessageCrackerPerfTest -tu us
41+
```
42+
43+
For more available options use `-h` option
44+
45+
```
46+
$ java -jar target/quickfixj-perf-test.jar -h
47+
```
48+
49+
#### Guideline for future performance enhancements
50+
51+
1. Check if there is already benchmark available for the code you are planning to optimize.
52+
2. If there is no benchmark code, first create a benchmark (Example `a-missing-perf-regression` branch) and make pull request to `master` branch.
53+
3. Make your performance improvement in a new branch (Example `a-perf-improvement` branch).
54+
4. When you make pull request provide comparison between current benchmark (`master`) and new benchmark values (`a-perf-improvement`)
55+
5. This means providing output of the benchmark execution in two branches in a PR comment

quickfixj-perf-test/pom.xml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
4+
<parent>
5+
<groupId>org.quickfixj</groupId>
6+
<artifactId>quickfixj-parent</artifactId>
7+
<version>3.0.0-SNAPSHOT</version>
8+
</parent>
9+
10+
<artifactId>quickfixj-perf-test</artifactId>
11+
<packaging>jar</packaging>
12+
13+
<name>QuickFIX/J Performance test</name>
14+
<description>QuickFIX/J Performance test for regression and improvements</description>
15+
<url>http://www.quickfixj.org</url>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>org.quickfixj</groupId>
20+
<artifactId>quickfixj-core</artifactId>
21+
<version>${project.version}</version>
22+
</dependency>
23+
<dependency>
24+
<groupId>org.quickfixj</groupId>
25+
<artifactId>quickfixj-messages-all</artifactId>
26+
<version>${project.version}</version>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.openjdk.jmh</groupId>
30+
<artifactId>jmh-core</artifactId>
31+
<version>${jmh.version}</version>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.openjdk.jmh</groupId>
35+
<artifactId>jmh-generator-annprocess</artifactId>
36+
<version>${jmh.version}</version>
37+
<scope>provided</scope>
38+
</dependency>
39+
</dependencies>
40+
41+
<build>
42+
<plugins>
43+
<plugin>
44+
<groupId>org.apache.maven.plugins</groupId>
45+
<artifactId>maven-shade-plugin</artifactId>
46+
<executions>
47+
<execution>
48+
<id>create-self-executing-jar</id>
49+
<phase>package</phase>
50+
<goals>
51+
<goal>shade</goal>
52+
</goals>
53+
<configuration>
54+
<finalName>${project.artifactId}</finalName>
55+
<transformers>
56+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
57+
<mainClass>org.openjdk.jmh.Main</mainClass>
58+
</transformer>
59+
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
60+
</transformers>
61+
<createDependencyReducedPom>false</createDependencyReducedPom>
62+
<shadedArtifactAttached>true</shadedArtifactAttached>
63+
<shadedClassifierName>standalone</shadedClassifierName>
64+
</configuration>
65+
</execution>
66+
</executions>
67+
</plugin>
68+
</plugins>
69+
</build>
70+
71+
</project>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.quickfixj;
2+
3+
import org.openjdk.jmh.annotations.BenchmarkMode;
4+
import org.openjdk.jmh.annotations.Fork;
5+
import org.openjdk.jmh.annotations.Measurement;
6+
import org.openjdk.jmh.annotations.Mode;
7+
import org.openjdk.jmh.annotations.OutputTimeUnit;
8+
import org.openjdk.jmh.annotations.Scope;
9+
import org.openjdk.jmh.annotations.State;
10+
import org.openjdk.jmh.annotations.Warmup;
11+
12+
import java.util.concurrent.TimeUnit;
13+
14+
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
15+
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
16+
@Fork(value = 1)
17+
@State(Scope.Benchmark)
18+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
19+
@BenchmarkMode(value = {Mode.Throughput, Mode.SampleTime})
20+
abstract class AbstractPerfTest {
21+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.quickfixj;
2+
3+
import org.openjdk.jmh.annotations.Benchmark;
4+
import org.openjdk.jmh.annotations.Setup;
5+
import org.openjdk.jmh.runner.Runner;
6+
import org.openjdk.jmh.runner.RunnerException;
7+
import org.openjdk.jmh.runner.options.Options;
8+
import org.openjdk.jmh.runner.options.OptionsBuilder;
9+
import org.quickfixj.sample.SampleQuickFixJApplication;
10+
import quickfix.ConfigError;
11+
import quickfix.DataDictionary;
12+
import quickfix.ValidationSettings;
13+
import quickfix.FieldNotFound;
14+
import quickfix.IncorrectTagValue;
15+
import quickfix.InvalidMessage;
16+
import quickfix.SessionID;
17+
import quickfix.UnsupportedMessageType;
18+
import quickfix.fix44.ExecutionReport;
19+
20+
public class MessageCrackerPerfTest extends AbstractPerfTest {
21+
22+
private SessionID sessionID;
23+
private ExecutionReport executionReport;
24+
25+
private SampleQuickFixJApplication application;
26+
27+
@Setup
28+
public void prepare() throws ConfigError, InvalidMessage {
29+
30+
String data = "8=FIX.4.4\0019=309\00135=8\00149=ASX\00156=CL1_FIX44\00134=4\001" +
31+
"52=20060324-01:05:58\00117=X-B-WOW-1494E9A0:58BD3F9D-1109\001150=D\001" +
32+
"39=0\00111=184271\00138=200\001198=1494E9A0:58BD3F9D\001526=4324\001" +
33+
"37=B-WOW-1494E9A0:58BD3F9D\00155=WOW\00154=1\001151=200\00114=0\00140=2\001" +
34+
"44=15\00159=1\0016=0\001453=3\001448=AAA35791\001447=D\001452=3\001448=8\001" +
35+
"447=D\001452=4\001448=FIX11\001447=D\001452=36\00160=20060320-03:34:29\00110=169\001";
36+
37+
sessionID = new SessionID("FIX4.4", "CL1_FIX44", "ASX");
38+
executionReport = new ExecutionReport();
39+
DataDictionary dataDictionary = new DataDictionary(MessageCrackerPerfTest.class.getClassLoader()
40+
.getResourceAsStream("FIX44.xml"));
41+
ValidationSettings validationSettings = new ValidationSettings();
42+
43+
executionReport.fromString(data, dataDictionary, validationSettings, false);
44+
45+
application = new SampleQuickFixJApplication();
46+
}
47+
48+
@Benchmark
49+
public void baseline() {
50+
}
51+
52+
@Benchmark
53+
public void crack() throws UnsupportedMessageType, IncorrectTagValue, FieldNotFound {
54+
application.fromApp(executionReport, sessionID);
55+
}
56+
57+
public static void main(String[] args) throws RunnerException {
58+
Options opt = new OptionsBuilder()
59+
.include(".*" + MessageCrackerPerfTest.class.getSimpleName() + ".*")
60+
.build();
61+
62+
new Runner(opt).run();
63+
}
64+
65+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.quickfixj;
2+
3+
import org.openjdk.jmh.annotations.Benchmark;
4+
import org.openjdk.jmh.annotations.Setup;
5+
import org.openjdk.jmh.runner.Runner;
6+
import org.openjdk.jmh.runner.RunnerException;
7+
import org.openjdk.jmh.runner.options.Options;
8+
import org.openjdk.jmh.runner.options.OptionsBuilder;
9+
import quickfix.ConfigError;
10+
import quickfix.DataDictionary;
11+
import quickfix.ValidationSettings;
12+
import quickfix.InvalidMessage;
13+
import quickfix.fix44.ExecutionReport;
14+
15+
public class MessageParsePerfTest extends AbstractPerfTest {
16+
17+
private ExecutionReport executionReport;
18+
private DataDictionary dataDictionary;
19+
private ValidationSettings validationSettings;
20+
private String data;
21+
22+
@Setup
23+
public void prepare() throws ConfigError {
24+
25+
data = "8=FIX.4.4\0019=309\00135=8\00149=ASX\00156=CL1_FIX44\00134=4\001" +
26+
"52=20060324-01:05:58\00117=X-B-WOW-1494E9A0:58BD3F9D-1109\001150=D\001" +
27+
"39=0\00111=184271\00138=200\001198=1494E9A0:58BD3F9D\001526=4324\001" +
28+
"37=B-WOW-1494E9A0:58BD3F9D\00155=WOW\00154=1\001151=200\00114=0\00140=2\001" +
29+
"44=15\00159=1\0016=0\001453=3\001448=AAA35791\001447=D\001452=3\001448=8\001" +
30+
"447=D\001452=4\001448=FIX11\001447=D\001452=36\00160=20060320-03:34:29\00110=169\001";
31+
32+
executionReport = new ExecutionReport();
33+
dataDictionary = new DataDictionary(MessageCrackerPerfTest.class.getClassLoader()
34+
.getResourceAsStream("FIX44.xml"));
35+
validationSettings = new ValidationSettings();
36+
}
37+
38+
@Benchmark
39+
public void baseline() {
40+
}
41+
42+
@Benchmark
43+
public void parse() throws InvalidMessage {
44+
executionReport.fromString(data, dataDictionary, validationSettings, false);
45+
}
46+
47+
public static void main(String[] args) throws RunnerException {
48+
Options opt = new OptionsBuilder()
49+
.include(".*" + MessageParsePerfTest.class.getSimpleName() + ".*")
50+
.build();
51+
52+
new Runner(opt).run();
53+
}
54+
55+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.quickfixj.sample;
2+
3+
import quickfix.Application;
4+
import quickfix.FieldNotFound;
5+
import quickfix.IncorrectTagValue;
6+
import quickfix.Message;
7+
import quickfix.SessionID;
8+
import quickfix.UnsupportedMessageType;
9+
import quickfix.fix44.ExecutionReport;
10+
import quickfix.fix44.MessageCracker;
11+
12+
public class SampleQuickFixJApplication extends MessageCracker implements Application {
13+
14+
@Override
15+
public void onCreate(SessionID sessionId) {
16+
17+
}
18+
19+
@Override
20+
public void onLogon(SessionID sessionId) {
21+
22+
}
23+
24+
@Override
25+
public void onLogout(SessionID sessionId) {
26+
27+
}
28+
29+
@Override
30+
public void toAdmin(Message message, SessionID sessionId) {
31+
32+
}
33+
34+
@Override
35+
public void fromAdmin(Message message, SessionID sessionId) {
36+
37+
}
38+
39+
@Override
40+
public void toApp(Message message, SessionID sessionId) {
41+
42+
}
43+
44+
@Override
45+
public void fromApp(Message message, SessionID sessionId)
46+
throws FieldNotFound, IncorrectTagValue, UnsupportedMessageType {
47+
crack(message, sessionId);
48+
}
49+
50+
@Override
51+
public void onMessage(ExecutionReport message, SessionID sessionID) {
52+
}
53+
}

0 commit comments

Comments
 (0)