Add a benchmark whose only purpose is to exercise CodSpeed's flamegraph symbolization pipeline on macOS (codspeed-macro runners). It should:
- run on macOS (locally and on
codspeed-macroCI) - be skipped on Linux + Windows so it doesn't pollute the regular walltime results or fail on platforms where the flamegraph path isn't applicable
- have a stack shape that produces an obviously-recognisable flamegraph (deep
recursion =
fib)
examples/example-gradle/src/jmh/java/bench/FibBenchmark.javaalready defines a recursivefib(30)benchmark. Reusing the same shape (but a dedicated class) keeps the flamegraph trivially recognisable.- The Gradle JMH plugin (
me.champeau.jmhv0.7.2) exposesincludes/excludesregex filters. We're already usingincludesto curate the CI subset inexamples/example-gradle/build.gradle.kts:32. - CI is matrixed in
.github/workflows/ci.yml. The flamegraph-relevant job iswalltime-benchmarksoncodspeed-macro— macOS-only by definition. The cross-platform jobs (build-and-run-gradle,build-and-run-maven) run the JMH JAR on Linux + Windows + macOS, so a naive new benchmark would also execute there. - JMH itself has no
@SkipOnPlatform-style annotation. The two reasonable gating mechanisms are:- Filter at the runner level (Gradle
excludes) - Throw /
Assume.assumeTrue(...)inside@Setup(JMH treats a thrown exception in setup as a benchmark error, not a skip — so this is not suitable).
- Filter at the runner level (Gradle
Add FibFlamegraphBenchmark next to the existing FibBenchmark, and in
examples/example-gradle/build.gradle.kts exclude it everywhere except macOS:
import org.gradle.internal.os.OperatingSystem
jmh {
// …existing config…
if (!OperatingSystem.current().isMacOsX) {
excludes.add(".*FibFlamegraphBenchmark.*")
}
}The CODSPEED_ENV branch already filters down to a curated set on CI; add
FibFlamegraphBenchmark to that alternation so it runs on codspeed-macro
but stays out of the way on the regular Linux/Windows matrix.
Pros:
- One source of truth (the build script). No per-benchmark scaffolding.
OperatingSystem.current()is the same API Gradle uses internally and doesn't require a new dependency.- Easy to mirror in the Maven example (
<profile><activation><os>block).
Cons:
- Excludes are regex-based; the benchmark class name must stay matchable.
Mitigation: keep
Flamegraphin the class name and exclude on that token.
E.g. bench.macos.FibFlamegraphBenchmark, then exclude bench\\.macos\\..*
when !isMacOsX.
Pros: groups future macOS-only benchmarks naturally; no per-class regex. Cons: more moving parts up front for a single benchmark.
Make the benchmark body a no-op on non-macOS. JMH still runs it, so the run shows up in results as a near-zero benchmark.
Pros: no build-script changes. Cons: pollutes results, defeats the "skip on Linux" goal. Rejected.
Go with Option 1. Smallest diff, no new package, and the gating lives in
the same place as the existing CODSPEED_ENV filter so future readers
discover it immediately.
- New file
examples/example-gradle/src/jmh/java/bench/FibFlamegraphBenchmark.java— copy ofFibBenchmarkwith a distinct class name and a@Paramvalue that pushes recursion deep enough to be visually interesting in the flamegraph (e.g.35). - Edit
examples/example-gradle/build.gradle.kts:- import
org.gradle.internal.os.OperatingSystem - add
excludes.add(".*FibFlamegraphBenchmark.*")underif (!OperatingSystem.current().isMacOsX) - extend the
CODSPEED_ENVinclude alternation to includeFibFlamegraphBenchmark
- import
- No CI changes needed: the existing
walltime-benchmarksjob oncodspeed-macrowill pick it up automatically.
- Should the Maven example mirror this? The Maven example doesn't currently
run under
codspeed-macro, so probably no — but worth confirming. - Do we want to lock the
@Fork/ iteration counts down for the flamegraph benchmark specifically (a single long iteration produces a cleaner flamegraph than many short ones)?