Skip to content

ci: consumable, contestant-focused benchmark reporting - #108

Open
dfa1 wants to merge 5 commits into
mainfrom
ci/benchmark-artifacts
Open

ci: consumable, contestant-focused benchmark reporting#108
dfa1 wants to merge 5 commits into
mainfrom
ci/benchmark-artifacts

Conversation

@dfa1

@dfa1 dfa1 commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replaces benchmark-lto.yml (compared the same code built two ways — an already-closed investigation, see CHANGELOG's rejected-LTO entry) with benchmark.yml: a workflow_dispatch job that reports zstd-java (byte[] and MemorySegment) vs zstd-jni vs aircompressor on identical input, across all 4 supported platforms.
  • Adds a reused-vs-fresh-per-call native context comparison (two new JMH benchmark methods each in CompressBenchmark/DecompressBenchmark), quantifying the cost of not following this library's reuse-the-context recommendation.
  • Results are now consumable without a browser sign-in: every table prints to the plain job log (gh run view --log) and uploads as a per-classifier artifact (raw JMH JSON + rendered markdown) via gh run download.
  • Fixes a real bug the CI verification run surfaced: compare-benchmarks.py's ± mangled to on Windows (default console code page isn't UTF-8) — same fix (sys.stdout.reconfigure(encoding="utf-8")) applied to the new format-contestants.py.
  • Adds zstdJavaStream to LargeFileBenchmark — it previously compared the mmap+MemorySegment path only against zstd-jni's stream, never against this library's own ZstdOutputStream, so the mmap path's gain over our own conventional API was invisible.

Verified

  • Local JMH run + real 4-platform CI run (linux-x86_64/aarch64, osx-aarch64, windows-x86_64), confirming: correct method filtering (no MultiThreadCompressBenchmark leakage), log + artifact output both work, Windows ± renders correctly, and results are directionally sane (zstd-java's MemorySegment path leads compress on all 4 platforms; context reuse consistently wins, most sharply on Windows compress at -33%).
  • zstdJavaStream's exact write-loop pattern round-tripped byte-for-byte through Zstd.decompress in a standalone check (ZstdOutputStream itself is already covered by ZstdStreamTest).
  • ./mvnw validate (checkstyle) and actionlint both clean.

Test plan

  • Trigger benchmark.yml on main once merged (can't workflow_dispatch a renamed file until it exists on the default branch) to confirm the simplified single-checkout shape runs clean end-to-end.

dfa1 added 5 commits July 26, 2026 20:36
Step Summary alone requires a signed-in browser session to view and
isn't retrievable via the API. Tee the comparison table into the job
log too, and upload the raw JMH JSON + rendered table as a per-classifier
artifact for offline diffing.
Windows' console code page isn't UTF-8 by default, so the '±' in the
comparison table garbled to '?' on windows-x86_64 runs.
…cost

The LTO-diff workflow only ever compared the same code built two ways.
Add a contestants report (zstd-java byte[]/MemorySegment vs zstd-jni vs
aircompressor) from the baseline build, plus reused-vs-fresh-per-call
native context overhead via two new JMH benchmark methods per class.

Tighten the JMH filter to anchor on the package-separator dot so
CompressBenchmark no longer also matches MultiThreadCompressBenchmark,
which has no zstdJni/aircompressor peers and only produced empty cells.
LTO was already investigated and rejected (CHANGELOG.md: regression on
x86_64, and zig's Mach-O linker doesn't support it on macOS at all,
issues #70/#77) - this was leftover machinery from that closed
investigation, not something anyone runs against a real branch
(experiment/lto-linux never existed).

Renamed benchmark-lto.yml -> benchmark.yml since it's no longer
LTO-specific, dropped compare-benchmarks.py (its only caller), and
simplified back to a single checkout/build/run per platform.
LargeFileBenchmark only ever compared the mmap + MemorySegment path
against zstd-jni's stream API, never against this library's own
ZstdOutputStream - so the mmap path's gain over our own conventional
stream API was invisible. Add zstdJavaStream (same buffered-read/
heap-write shape as zstdJniStream) to close that gap.

Verified: compiles, runs clean at size=4194304, and the exact
write()-loop-through-ZstdOutputStream pattern round-trips byte-for-byte
through Zstd.decompress (checked standalone, not asserted in the
benchmark itself - JMH benchmarks here aren't correctness tests,
ZstdOutputStream's correctness is already covered by ZstdStreamTest).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant