Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JMH Benchmarks added. #178

Merged
merged 5 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ jobs:
strategy:
matrix:
os: [ ubuntu-20.04 ]
java: [ "8", "11", "17" ]
java: [ "8", "11", "17", "21"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v4
with:
distribution: 'temurin'
distribution: 'corretto'
java-version: ${{ matrix.java }}
cache: 'maven'
- name: Verify with Maven
run: mvn --batch-mode --errors --update-snapshots verify
- name: Run benchmarks
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kept because we haven't moved everything yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have ported everything from CL2Benchmark but not CL2NoCompileBenchmark. Not sure if it is actually an important use case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are important so I'm happy to have "Run Benchmarks" step still until these tests that check for rule building, memory usage, et all.

run: mvn test '-Dtest=Benchmarks#CL2*'
- name: Run JMH benchmarks
run: mvn exec:exec@run-jmh-benchmarks
49 changes: 49 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
<checkstyle.plugin.version>3.4.0</checkstyle.plugin.version>
<spotbugs.plugin.version>4.8.6.2</spotbugs.plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jmh.version>1.37</jmh.version>
<exec.plugin.version>3.0.0</exec.plugin.version>
</properties>

<licenses>
Expand Down Expand Up @@ -105,6 +107,20 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand All @@ -120,8 +136,40 @@
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>${exec.plugin.version}</version>
<executions>
<execution>
<id>run-jmh-benchmarks</id>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<classpathScope>test</classpathScope>
<executable>java</executable>
<arguments>
<argument>-classpath</argument>
<classpath/>
<argument>org.openjdk.jmh.Main</argument>
<argument>-gc</argument>
<argument>true</argument>
<argument>.*</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
Expand Down Expand Up @@ -162,6 +210,7 @@
<failsOnError>false</failsOnError>
<linkXRef>false</linkXRef>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<excludeGeneratedSources>true</excludeGeneratedSources>
</configuration>
<executions>
<execution>
Expand Down
37 changes: 20 additions & 17 deletions src/test/software/amazon/event/ruler/Benchmarks.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class Benchmarks {
// revised citylots with structured arrays
private static final String CITYLOTS_2 = "src/test/data/citylots2.json.gz";

private final String[] EXACT_RULES = {
public static final String[] EXACT_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"MAPBLKLOT\": [ \"1430022\" ]\n" +
Expand Down Expand Up @@ -71,7 +71,7 @@ public class Benchmarks {
};
private final int[] EXACT_MATCHES = { 1, 101, 35, 655, 1 };

private final String[] WILDCARD_RULES = {
public static final String[] WILDCARD_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"MAPBLKLOT\": [ { \"wildcard\": \"143*\" } ]\n" +
Expand Down Expand Up @@ -100,7 +100,7 @@ public class Benchmarks {
};
private final int[] WILDCARD_MATCHES = { 490, 713, 43, 2540, 1 };

private final String[] PREFIX_RULES = {
public static final String[] PREFIX_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"STREET\": [ { \"prefix\": \"AC\" } ]\n" +
Expand Down Expand Up @@ -129,7 +129,7 @@ public class Benchmarks {
};
private final int[] PREFIX_MATCHES = { 24, 442, 38, 2387, 328 };

private final String[] PREFIX_EQUALS_IGNORE_CASE_RULES = {
public static final String[] PREFIX_EQUALS_IGNORE_CASE_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"STREET\": [ { \"prefix\": { \"equals-ignore-case\": \"Ac\" } } ]\n" +
Expand Down Expand Up @@ -158,7 +158,7 @@ public class Benchmarks {
};
private final int[] PREFIX_EQUALS_IGNORE_CASE_MATCHES = { 24, 442, 38, 2387, 328 };

private final String[] SUFFIX_RULES = {
public static final String[] SUFFIX_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"STREET\": [ { \"suffix\": \"ON\" } ]\n" +
Expand Down Expand Up @@ -187,7 +187,7 @@ public class Benchmarks {
};
private final int[] SUFFIX_MATCHES = { 17921, 871, 13, 1963, 682 };

private final String[] SUFFIX_EQUALS_IGNORE_CASE_RULES = {
public static final String[] SUFFIX_EQUALS_IGNORE_CASE_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"STREET\": [ { \"suffix\": { \"equals-ignore-case\": \"oN\" } } ]\n" +
Expand Down Expand Up @@ -216,7 +216,7 @@ public class Benchmarks {
};
private final int[] SUFFIX_EQUALS_IGNORE_CASE_MATCHES = { 17921, 871, 13, 1963, 682 };

private final String[] EQUALS_IGNORE_CASE_RULES = {
public static final String[] EQUALS_IGNORE_CASE_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"STREET\": [ { \"equals-ignore-case\": \"jefferson\" } ]\n" +
Expand Down Expand Up @@ -245,7 +245,7 @@ public class Benchmarks {
};
private final int[] EQUALS_IGNORE_CASE_MATCHES = { 131, 211, 1758, 825, 116386 };

private final String[] COMPLEX_ARRAYS_RULES = {
public static final String[] COMPLEX_ARRAYS_RULES = {
"{\n" +
" \"geometry\": {\n" +
" \"type\": [ \"Polygon\" ],\n" +
Expand Down Expand Up @@ -286,7 +286,7 @@ public class Benchmarks {
};
private final int[] COMPLEX_ARRAYS_MATCHES = { 229, 2, 149444, 64368, 127484 };

private final String[] NUMERIC_RULES = {
public static final String[] NUMERIC_RULES = {
"{\n" +
" \"geometry\": {\n" +
" \"type\": [ \"Polygon\" ],\n" +
Expand Down Expand Up @@ -327,7 +327,7 @@ public class Benchmarks {
};
private final int[] NUMERIC_MATCHES = { 7, 120, 148946, 64120, 127052 };

private final String[] ANYTHING_BUT_RULES = {
public static final String[] ANYTHING_BUT_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"STREET\": [ { \"anything-but\": [ \"FULTON\" ] } ]\n" +
Expand Down Expand Up @@ -356,7 +356,7 @@ public class Benchmarks {
};
private final int[] ANYTHING_BUT_MATCHES = { 211158, 210411, 96682, 120, 210615 };

private final String[] ANYTHING_BUT_IGNORE_CASE_RULES = {
public static final String[] ANYTHING_BUT_IGNORE_CASE_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"STREET\": [ { \"anything-but\": {\"equals-ignore-case\": [ \"Fulton\" ] } } ]\n" +
Expand Down Expand Up @@ -386,7 +386,7 @@ public class Benchmarks {
private final int[] ANYTHING_BUT_IGNORE_CASE_MATCHES = { 211158, 210411, 96682, 120, 210615 };


private final String[] ANYTHING_BUT_PREFIX_RULES = {
public static final String[] ANYTHING_BUT_PREFIX_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"STREET\": [ { \"anything-but\": {\"prefix\": \"FULTO\" } } ]\n" +
Expand Down Expand Up @@ -415,7 +415,7 @@ public class Benchmarks {
};
private final int[] ANYTHING_BUT_PREFIX_MATCHES = { 211158, 210118, 96667, 120, 209091 };

private final String[] ANYTHING_BUT_SUFFIX_RULES = {
public static final String[] ANYTHING_BUT_SUFFIX_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"STREET\": [ { \"anything-but\": {\"suffix\": \"ULTON\" } } ]\n" +
Expand Down Expand Up @@ -444,7 +444,7 @@ public class Benchmarks {
};
private final int[] ANYTHING_BUT_SUFFIX_MATCHES = { 211136, 210411, 94908, 0, 209055 };

private final String[] ANYTHING_BUT_WILDCARD_RULES = {
public static final String[] ANYTHING_BUT_WILDCARD_RULES = {
"{\n" +
" \"properties\": {\n" +
" \"MAPBLKLOT\": [ { \"anything-but\": {\"wildcard\": \"143*\" } } ]\n" +
Expand Down Expand Up @@ -936,8 +936,13 @@ private String randomAscii(final int len) {
}

private void readCityLots2() {
System.out.println("Reading citylots2");
readCityLots2(citylots2);
System.out.println("Read " + citylots2.size() + " events");
}

public static void readCityLots2(List<String> citylots2) {
try {
System.out.println("Reading citylots2");
final FileInputStream fileInputStream = new FileInputStream(CITYLOTS_2);
final GZIPInputStream gzipInputStream = new GZIPInputStream(fileInputStream);
BufferedReader cl2Reader = new BufferedReader(new InputStreamReader(gzipInputStream));
Expand All @@ -948,9 +953,7 @@ private void readCityLots2() {
line = cl2Reader.readLine();
}
cl2Reader.close();
System.out.println("Read " + citylots2.size() + " events");
} catch (Exception e) {
System.out.println("Can't find, current directory " + System.getProperty("user.dir"));
throw new RuntimeException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package software.amazon.event.ruler.jmh;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OperationsPerInvocation;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Timeout;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import software.amazon.event.ruler.Machine;

import java.util.Objects;

import static java.util.concurrent.TimeUnit.SECONDS;


@BenchmarkMode(Mode.Throughput)
@Fork(value = 2, jvmArgsAppend = {
"-Xmx2g", "-Xms2g", "-XX:+AlwaysPreTouch", "-XX:+UseTransparentHugePages", "-XX:+UseSerialGC",
"-XX:-BackgroundCompilation", "-XX:CompileCommand=dontinline,com/fasterxml/*.*",
})
@Timeout(time = 90, timeUnit = SECONDS)
@Threads(2)
@OperationsPerInvocation(CityLots2State.DATASET_SIZE)
public class CityLots2JmhBenchmarks {

@Benchmark
@Warmup(iterations = 4, batchSize = 1, time = 10, timeUnit = SECONDS)
@Measurement(iterations = 5, batchSize = 1, time = 10, timeUnit = SECONDS)
public void group01Simple(MachineStateSimple machineState, CityLots2State cityLots2State, Blackhole blackhole) throws Exception {
run(machineState, cityLots2State, blackhole);
}

@Benchmark
@Warmup(iterations = 2, batchSize = 1, time = 60, timeUnit = SECONDS)
@Measurement(iterations = 3, batchSize = 1, time = 60, timeUnit = SECONDS)
public void group02Complex(MachineStateComplex machineState, CityLots2State cityLots2State, Blackhole blackhole) throws Exception {
run(machineState, cityLots2State, blackhole);
}

Comment on lines +30 to +43
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it worth splitting this by each matcher to parallelize the runs and reduce the runtime even more ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you go this route, you would be able to get away with building a HashMap of rule-matchines during setup() and then return pass the machine during load tests. It'll reduce the number of scaffolding classes for benchmarks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This grouping is done not for parallelism, parallelizing these benchmark on the same box is a bad idea because the results will be noisy.

The reason for this grouping is to have different setup for different benchmarks: for simple ones it is 40 seconds warmup + 50 seconds measurement, for complex ones it is 2 minutes warmup + 3 minutes measurement just because every call for complex benchmarks takes much longer.

We could split across multiple boxes for parallelism, but I don't think it is worth the effort and probably would be harder to collect results.

What we actually want to do regarding parallelism is to run every benchmark by 2 threads (and check that throughput is ~2x of 1 thread) to make sure that we don't have any contention or false sharing. Right now these benchmarks are single threaded and thus we don't actually know if we have an issue. I will push this change.

private void run(MachineState machineState, CityLots2State cityLots2State, Blackhole blackhole) throws Exception {
Machine machine = Objects.requireNonNull(machineState.machine);

for (String event : cityLots2State.getCityLots2()) {
blackhole.consume(machine.rulesForJSONEvent(event));
}
}
}

28 changes: 28 additions & 0 deletions src/test/software/amazon/event/ruler/jmh/CityLots2State.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package software.amazon.event.ruler.jmh;

import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import software.amazon.event.ruler.Benchmarks;

import java.util.ArrayList;
import java.util.List;

@State(Scope.Benchmark)
public class CityLots2State {

public static final int DATASET_SIZE = 213068;

private static final List<String> citylots2 = new ArrayList<>();

static {
Benchmarks.readCityLots2(citylots2);

if (citylots2.size() != DATASET_SIZE) {
throw new RuntimeException("Expected dataset size: " + DATASET_SIZE + ", actual: " + citylots2.size());
}
}

public Iterable<String> getCityLots2() {
return citylots2;
}
}
Loading