Skip to content

Commit e9ee608

Browse files
authored
Merge pull request #332 from graalvm/move-jmh-binary-tree-demo
[GR-60094] Review and Move jmh/benchmark-binary-tree demo under native-image/benchmark category.
2 parents abf1c5f + 1040de1 commit e9ee608

File tree

12 files changed

+438
-459
lines changed

12 files changed

+438
-459
lines changed
Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,45 @@
1-
name: jmh/benchmark-binary-tree
1+
name: native-image/benchmark/jmh/binary-tree
22
on:
33
push:
44
paths:
5-
- 'jmh/benchmark-binary-tree/**'
6-
- '.github/workflows/jmh-binary-tree.yml'
5+
- 'native-image/benchmark/jmh/binary-tree/**'
6+
- '.github/workflows/native-image-jmh-binary-tree.yml'
77
pull_request:
88
paths:
9-
- 'jmh/benchmark-binary-tree/**'
10-
- '.github/workflows/jmh-binary-tree.yml'
9+
- 'native-image/benchmark/jmh/binary-tree/**'
10+
- '.github/workflows/native-image-jmh-binary-tree.yml'
1111
schedule:
1212
- cron: "0 0 1 * *" # run every month
1313
workflow_dispatch:
1414
permissions:
1515
contents: read
1616
jobs:
1717
build:
18-
name: GraalVM Binary Tree Benchmark
18+
name: JMH Binary Tree Benchmark
1919
runs-on: ubuntu-latest
2020
timeout-minutes: 45
21+
strategy:
22+
matrix:
23+
java-version: ['21', '24-ea']
2124
steps:
2225
- uses: actions/checkout@v4
2326
- uses: graalvm/setup-graalvm@v1
2427
with:
25-
java-version: '21.0.2'
28+
java-version: ${{ matrix.java-version }}
2629
distribution: 'graalvm'
2730
github-token: ${{ secrets.GITHUB_TOKEN }}
2831
- name: Build and Test Java Code
2932
run: |
30-
cd jmh/benchmark-binary-tree
31-
# build the Java code
33+
cd native-image/benchmark/jmh/binary-tree
34+
# Build the Java code
3235
./mvnw --no-transfer-progress clean package
33-
# build the native exe
36+
# Build the native executable
3437
./mvnw --no-transfer-progress -Pnative -DskipNativeTests package
3538
# Could just replace with a check that the binary exists?
3639
./target/benchmark-binary-tree
37-
# build and run the instrumented native exe
40+
# Build and run the instrumented native executable
3841
./mvnw --no-transfer-progress -Pinstrumented -DskipNativeTests package
3942
./target/benchmark-binary-tree-instr
40-
# build and run the optimised native exe
43+
# Build and run the optimized native executable
4144
./mvnw --no-transfer-progress -Poptimised -DskipNativeTests package
4245
./target/benchmark-binary-tree-opt

jmh/benchmark-binary-tree/README.md

Lines changed: 0 additions & 110 deletions
This file was deleted.

jmh/benchmark-binary-tree/.mvn/wrapper/maven-wrapper.properties renamed to native-image/benchmark/jmh/binary-tree/.mvn/wrapper/maven-wrapper.properties

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
# to you under the Apache License, Version 2.0 (the
66
# "License"); you may not use this file except in compliance
77
# with the License. You may obtain a copy of the License at
8-
#
8+
#
99
# http://www.apache.org/licenses/LICENSE-2.0
10-
#
10+
#
1111
# Unless required by applicable law or agreed to in writing,
1212
# software distributed under the License is distributed on an
1313
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
1414
# KIND, either express or implied. See the License for the
1515
# specific language governing permissions and limitations
1616
# under the License.
17-
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
18-
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
17+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
18+
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Binary Tree Benchmark
2+
3+
This demo shows how to run a Java Microbenchmark Harness (JMH) benchmark as a native executable.
4+
5+
To build a native executable version of this benchmark you need to run the [Tracing Agent](https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/) to supply the reflection configuration to the `native-image` builder.
6+
This has already been done for you to save time and the generated configuration can be found in _src/main/resources/META-INF/native-image/_.
7+
8+
> **Note:** To generate the configuration yourself, ensure that the JMH `fork` parameter is set to `0`, which can be performed from the command line using the option `-f 0`. It can also be achieved within the code by using the `@Fork` annotation.
9+
10+
## Important Notes on Using JMH with GraalVM Native Image
11+
12+
To make a JMH benchmark run as a native image, you can not fork the benchmark process in the same way as JMH does when running on the JVM.
13+
When running on HotSpot, JMH will fork a new JVM for each benchmark to ensure there is no interference in the measurements for each benchmark.
14+
This forking process is not possible with GraalVM Native Image and you should consider the following guidance when building JMH benchmarks that are meant to be run as native executables:
15+
* You should only include a single benchmark in each native executable.
16+
* You need to annotate the benchmark with `@Fork(0)` to ensure that the benchmark is not forked.
17+
* If you want to profile the benchmark to generate an optimized version of it, you should, obviously, ignore the benchmark results whilst profiling.
18+
19+
## Preparation
20+
21+
1. Download and install the GraalVM JDK using [SDKMAN!](https://sdkman.io/). For other installation options, visit the [Downloads page](https://www.graalvm.org/downloads/).
22+
```bash
23+
sdk install java 21.0.5-graal
24+
```
25+
26+
2. Download or clone the repository and navigate into the _/native-image/benchmark/jmh/binary-tree_ directory:
27+
```bash
28+
git clone https://github.com/graalvm/graalvm-demos
29+
```
30+
```bash
31+
cd graalvm-demos/native-image/benchmark/jmh/binary-tree
32+
```
33+
34+
## Build and Run the Benchmark on HotSpot
35+
36+
To build and run the benchmark on HotSpot, run the following Maven command:
37+
```shell
38+
./mvnw clean package exec:exec
39+
```
40+
41+
Note that within the _pom.xml_ file there are instructions to explicitly turn off the Graal JIT compiler using the option `-XX:-UseJVMCICompiler`.
42+
This means that benchmark will run using the C2 JIT compiler.
43+
44+
The application runs the benchmark and displays the results to the terminal.
45+
**The final result is the most significant.**
46+
You should see the result similar to this:
47+
```shell
48+
Benchmark (binaryTreesN) Mode Cnt Score Error Units
49+
BinaryTrees.bench 14 thrpt 6 348.923 ± 21.343 ops/s
50+
```
51+
52+
## Build and Run the Benchmark from a Native Executable
53+
54+
Now build a native executable using Native Image.
55+
This demo uses Oracle GraalVM Native Image, however, if you are using GraalVM Community, you may see lower figures for throughput.
56+
57+
1. Build a native executable, run the following command:
58+
```shell
59+
./mvnw package -Pnative
60+
```
61+
2. Run the benchmark from a native executable:
62+
```shell
63+
./target/benchmark-binary-tree
64+
```
65+
You should see similar results:
66+
```shell
67+
Benchmark (binaryTreesN) Mode Cnt Score Error Units
68+
BinaryTrees.bench 14 thrpt 6 282.335 ± 5.644 ops/s
69+
```
70+
71+
## Optimize the Benchmark for Throughput
72+
73+
You can improve the performance of this benchmark by applying [Profile-Guided Optimization (PGO)](https://www.graalvm.org/reference-manual/native-image/optimizations-and-performance/PGO/).
74+
75+
> PGO is available with Oracle GraalVM Native Image.
76+
77+
First, you will need to build an instrumented version of this native benchmark that contains extra code to trace the execution of the program and to profile it.
78+
Therefore, it will run slower than the previous version.
79+
After execution finishes, a profile file, _default.iprof_, is generated in the root directory.
80+
This file contains profiling information about the application and will be used to build a more efficient native executable.
81+
82+
1. To build the instrumented version of the native executable, run the following command:
83+
```shell
84+
./mvnw package -Pinstrumented
85+
```
86+
87+
2. Then run it to generate the profile file:
88+
```shell
89+
./target/benchmark-binary-tree-instr
90+
```
91+
92+
3. Now that you have the profiles, build and run the optimized version of this native benchmark:
93+
```shell
94+
./mvnw package -Poptimised
95+
```
96+
```shell
97+
./target/benchmark-binary-tree-opt
98+
```
99+
You should see similar results:
100+
```shell
101+
Benchmark (binaryTreesN) Mode Cnt Score Error Units
102+
BinaryTrees.bench 14 thrpt 6 311.630 ± 3.630 ops/s
103+
```
104+
105+
## Your Mileage May Vary
106+
107+
The results you see will vary depending on the hardware you are running on.
108+
The results above are from a 2019 MacBook Pro, i7, 32 GB RAM running on Oracle GraalVM for JDK 21.0.5.

0 commit comments

Comments
 (0)