.claude/skills/jmh-benchmarks/SKILL.md
Expert guidance for writing Java microbenchmarks using JMH (Java Microbenchmark Harness). Use this skill when writing performance benchmarks, measuring method execution time, comparing algorithm implementations, profiling code performance, or debugging benchmark issues. Trigger keywords include "jmh", "benchmark", "microbenchmark", "performance test", "@Benchmark", "throughput", "warmup", "blackhole", "measure performance".
npx skillsauth add aehrc/pathling jmh-benchmarksInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
JMH is the official OpenJDK harness for building reliable Java microbenchmarks. It handles warmup, JIT compilation, dead code elimination, and statistical analysis automatically.
Generate a new benchmark project using the Maven archetype:
mvn archetype:generate \
-DinteractiveMode=false \
-DarchetypeGroupId=org.openjdk.jmh \
-DarchetypeArtifactId=jmh-java-benchmark-archetype \
-DgroupId=org.example \
-DartifactId=my-benchmarks \
-Dversion=1.0-SNAPSHOT
Build and run:
mvn clean verify
java -jar target/benchmarks.jar
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(1)
public class MyBenchmark {
private String data;
@Setup
public void setup() {
data = "test-data";
}
@Benchmark
public int measureLength() {
return data.length();
}
}
| Mode | Description |
| --------------------- | ------------------------------------------------- |
| Mode.Throughput | Operations per second (default) |
| Mode.AverageTime | Average time per operation |
| Mode.SampleTime | Samples execution time distribution (percentiles) |
| Mode.SingleShotTime | Single invocation time (cold start) |
| Mode.All | Run all modes |
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
| Scope | Description |
| ----------------- | ----------------------------------------------- |
| Scope.Thread | One instance per thread (no contention) |
| Scope.Benchmark | Shared across all threads (contention possible) |
| Scope.Group | Shared within thread group |
@State(Scope.Thread)
public class MyState {
// Non-final fields for computation inputs
public int x = 42;
}
@State(Scope.Thread)
public class MyBenchmark {
private List<String> data;
@Setup(Level.Trial) // Once per benchmark run
public void setupTrial() { }
@Setup(Level.Iteration) // Before each iteration
public void setupIteration() { }
@Setup(Level.Invocation) // Before each method call (expensive!)
public void setupInvocation() { }
@TearDown(Level.Trial)
public void tearDown() { }
}
The JVM eliminates code whose results are unused. Always return or consume results:
// WRONG: Result unused, JVM eliminates the call
@Benchmark
public void wrong() {
compute(x);
}
// CORRECT: Return the result
@Benchmark
public int correct() {
return compute(x);
}
When a method produces multiple values, use Blackhole to consume them:
@Benchmark
public void multipleResults(Blackhole bh) {
bh.consume(compute(x));
bh.consume(compute(y));
}
Never use final fields or literal values as inputs. The JVM precomputes results for predictable inputs:
@State(Scope.Thread)
public class MyState {
// WRONG: final enables constant folding
public final int x = 42;
// CORRECT: non-final prevents optimisation
public int x = 42;
}
@Benchmark
public int wrong() {
return compute(42); // Literal folded at compile time
}
@Benchmark
public int correct(MyState state) {
return compute(state.x); // Runtime value
}
Never write manual loops. JMH handles iteration and loop optimisations distort results:
// WRONG: Loop unrolling distorts measurements
@Benchmark
public int wrong() {
int sum = 0;
for (int i = 0; i < 1000; i++) {
sum += compute(i);
}
return sum;
}
// CORRECT: Single operation, let JMH iterate
@Benchmark
public int correct(MyState state) {
return compute(state.i);
}
Test across multiple configurations:
@State(Scope.Benchmark)
public class ParamBenchmark {
@Param({"10", "100", "1000"})
public int size;
@Param({"ArrayList", "LinkedList"})
public String listType;
private List<Integer> list;
@Setup
public void setup() {
list = listType.equals("ArrayList")
? new ArrayList<>()
: new LinkedList<>();
for (int i = 0; i < size; i++) {
list.add(i);
}
}
@Benchmark
public int iterate() {
int sum = 0;
for (int x : list) sum += x;
return sum;
}
}
Override parameters from command line:
java -jar benchmarks.jar -p size=50,500 -p listType=ArrayList
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS)
@Fork(value = 2, jvmArgs = {"-Xms2G", "-Xmx2G"})
@Threads(4)
@Timeout(time = 30, timeUnit = TimeUnit.SECONDS)
Run benchmarks from code (useful for IDE integration):
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(MyBenchmark.class.getSimpleName())
.forks(1)
.warmupIterations(3)
.measurementIterations(5)
.mode(Mode.AverageTime)
.timeUnit(TimeUnit.NANOSECONDS)
.build();
new Runner(opt).run();
}
# List available benchmarks
java -jar benchmarks.jar -l
# Run specific benchmark
java -jar benchmarks.jar MyBenchmark
# Configure execution
java -jar benchmarks.jar -f 2 -wi 5 -i 10 -t 4
# Output formats
java -jar benchmarks.jar -rf json -rff results.json
java -jar benchmarks.jar -rf csv -rff results.csv
# Profilers
java -jar benchmarks.jar -prof gc
java -jar benchmarks.jar -prof stack
java -jar benchmarks.jar -prof perfasm # Linux only
# Help
java -jar benchmarks.jar -h
For adding to an existing project (archetype preferred for new projects):
<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.37</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.37</version>
<scope>provided</scope>
</dependency>
</dependencies>
Configure the annotation processor in the compiler plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.37</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
tools
Expert guidance for using WireMock in Java applications for HTTP API mocking and testing. Use this skill when the user asks to mock HTTP APIs, create API stubs, test REST clients, simulate network faults, verify HTTP requests, or integrate WireMock with Spring Boot. Trigger keywords include "wiremock", "mock http", "stub api", "http mock", "api testing", "rest mock", "simulate fault", "verify request", "spring boot wiremock".
documentation
Expert guidance for implementing SQL on FHIR v2 ViewDefinitions and operations to create portable, tabular projections of FHIR data. Use this skill when the user asks to create ViewDefinitions, flatten FHIR resources into tables, write FHIRPath expressions for data extraction, implement forEach/forEachOrNull/repeat patterns for unnesting, create where clauses for filtering, use constants in view definitions, combine data with unionAll, execute ViewDefinitions with $run or $export operations, or implement SQL on FHIR server capabilities. Trigger keywords include "ViewDefinition", "SQL on FHIR", "flatten FHIR", "tabular FHIR", "FHIR to SQL", "FHIR analytics", "FHIRPath columns", "unnest FHIR", "$viewdefinition-run", "$export", "view runner", "repeat", "recursive", "QuestionnaireResponse".
development
Expert guidance for working with the Apache Spark Catalyst query optimisation framework. Use this skill when working with Spark SQL internals, creating custom expressions, implementing query optimisations, working with logical/physical plans, or extending Catalyst. Trigger keywords include "catalyst", "spark sql", "expression", "logical plan", "physical plan", "tree node", "query optimisation", "rule executor", "analyzer", "optimizer", "code generation".
development
Expert guidance for using the SonarCloud API to interact with code quality analysis, projects, issues, quality gates, and metrics. Use this skill when making API calls to SonarCloud, automating code quality workflows, retrieving analysis results, managing projects programmatically, or integrating SonarCloud with CI/CD pipelines. Trigger keywords include "SonarCloud", "SonarCloud API", "code quality API", "SonarQube Cloud", "quality gate", "code analysis API", "SonarCloud measures", "SonarCloud issues".