Skip to content
Open
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
55 changes: 55 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

JMH microbenchmark suite (Java 21) comparing performance of common Java idioms — logging styles,
collections, strings, BigInteger/BigDecimal, exceptions vs null-checks, streams vs loops, etc.
No production/application code: every class is a benchmark. The original focus (per README) was
showing the value of `Supplier`-based log4j2 logging; the repo has since grown many unrelated
benchmark families.

## Commands

```bash
./gradlew jmh # run ALL benchmarks (uses the me.champeau.jmh plugin)
./gradlew jmhJar # build the runnable JMH uber-jar
```

Run a SINGLE benchmark — two ways:

```bash
# 1. Each benchmark class has a main() that calls BenchmarkUtils.runBenchmark(...).
# Run that main from the IDE, or via the jmh jar:
./gradlew jmhJar
java -jar build/libs/*-jmh.jar LoggerBenchmarks # regex include, matches class simple name

# 2. Gradle plugin include filter (regex on fully-qualified benchmark name):
./gradlew jmh -Pjmh.includes='.*LoggerBenchmarks.*'
```

There are no unit tests, no lint step. `org.gradle.daemon=false` is set in `gradle.properties`.

## Architecture / conventions

- All sources live under `src/jmh/java/com/patotski/performance/`, NOT `src/main`. The JMH plugin
compiles the `jmh` source set; `jmhAnnotationProcessor` generates the `BenchmarkList` — without it
you get a "missing /META-INF/BenchmarkList" error (see comment in `build.gradle`).
- One package per topic (`logging`, `collections`, `string`, `bigint`, `bigdec`, `array`, `enums`,
`exception`, `random`, `lombok`, `map`, `streams`). Add a new benchmark by creating a class in the
matching (or new) package.
- Each benchmark class is self-contained and carries its own JMH annotations at class level
(`@BenchmarkMode`, `@Warmup`, `@Measurement`, `@OutputTimeUnit(NANOSECONDS)`, `@Fork(1)`).
These per-class annotations override the global defaults in the `jmh { }` block of `build.gradle`.
- `@State(Scope.Benchmark)` inner classes hold the inputs; a `@Setup` method does any one-time init.
- Every class ends with a `main()` calling `BenchmarkUtils.runBenchmark(ThisClass.class)`.
`BenchmarkUtils` writes JSON to `reports/<ClassSimpleName>.json`, enables GC between iterations,
and forces 1 fork. Running via `main()` bypasses the `jmh { }` block entirely.
- Lombok is available (`io.freefair.lombok` plugin) — see `lombok/BigDecimalTuple.java` (`@Builder`).

## Gotchas

- `reports/` is the output dir (created by `BenchmarkUtils`); it is untracked working output.
- Logging benchmarks set the log level to `ERROR` in `@Setup` so `debug(...)` calls measure the
guarded/no-op path, which is the whole point of the supplier vs concat/format comparison.
Loading