Skip to content

Add BenchmarkDotNet benchmark project - #12720

Closed
niksedk wants to merge 2 commits into
mainfrom
feature/benchmarkdotnet
Closed

Add BenchmarkDotNet benchmark project#12720
niksedk wants to merge 2 commits into
mainfrom
feature/benchmarkdotnet

Conversation

@niksedk

@niksedk niksedk commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Adds tests/benchmarks — a BenchmarkDotNet project for libse hot paths — plus a Run benchmarks workflow, with a first suite motivated by #12716 (replacing hand-rolled byte-shifting helpers with BinaryPrimitives).

BinaryPrimitivesBenchmarks compares the removed helpers against the BinaryPrimitives calls that replace them. The legacy implementations are kept as verbatim private copies inside the benchmark, since #12716 deletes the originals from libse — so this project builds and runs against main as well, independent of whether #12716 merges.

GlobalSetup asserts that both implementations agree on every offset of the test buffer before any timing runs, so the benchmark also guards #12716's byte-exactness claim rather than just timing it.

Workflow

.github/workflows/benchmarks.yml runs the suite on linux-x64, windows-x64 and macos-arm64. It triggers on workflow_dispatch (with a filter input) and on PRs touching tests/benchmarks/** — deliberately not on every push, since a full run costs a few minutes per runner and the numbers only matter when someone is reading them. Results go to the job summary and are uploaded as artifacts.

Results

Ratio vs the legacy shift-chain, lower is better, 1.00 = legacy baseline. Each benchmark sweeps a 64 KB buffer with OperationsPerInvoke normalizing back to ns/read.

Category linux-x64
Xeon 6973P
windows-x64
EPYC 7763
macos-arm64
M1 (virtual)
Int32LE 0.48 0.48 0.66
UInt16BE 0.89 0.91 1.07
UInt32BE 0.73 0.47 0.76
UInt64BE 0.47 0.33 0.51

BinaryPrimitives wins in nearly every category, and the win is larger on x64 than arm64 — up to 3x on the 64-bit read. That fits movbe/bswap collapsing a chain of 8 byte loads, shifts and ors into roughly one instruction, so the widest read gains the most.

The 16-bit case is a wash everywhere (0.89–1.07); a 2-byte swap was never going to be a bottleneck. The 1.07 on the macos-arm64 runner is noise at sub-nanosecond scale on a shared virtual runner, not a real regression.

These are sub-nanosecond, machine-specific measurements — the ratios are the signal, not the absolute values. Both implementations allocate nothing.

Test plan

  • dotnet build tests/benchmarks/LibSEBenchmarks.csproj -c Release — clean, 0 warnings
  • Full suite run locally on main and on the Replace manual byte-shifting helpers with BinaryPrimitives #12716 branch — identical results
  • CI run green on all three runners; GlobalSetup equality assertion passes on x64 and arm64
  • BenchmarkDotNet.Artifacts/ was already in .gitignore
  • Project added to the solution under the existing tests folder

🤖 Generated with Claude Code

Introduces tests/benchmarks with a first suite comparing the hand-rolled
byte-shifting helpers this branch removed against the BinaryPrimitives
calls that replaced them.

The legacy implementations are kept as verbatim private copies in the
benchmark, since the originals are gone from libse. GlobalSetup asserts
both implementations agree on every offset of the test buffer before any
timing runs, so the benchmark also guards the byte-exactness claim.

Each benchmark sweeps a 64 KB buffer and uses OperationsPerInvoke to
normalize to ns/read, keeping the measurement above BenchmarkDotNet's
single-call resolution floor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Runs the benchmark suite on real x64 hardware (linux-x64 and windows-x64)
plus macos-arm64 for comparison, since endianness intrinsics differ
sharply between arm64 (rev) and x64 (movbe/bswap) and local arm64 numbers
do not transfer.

Triggers on workflow_dispatch and on PRs touching tests/benchmarks, not on
every push - a full run costs a few minutes per runner and the numbers are
only useful when someone is looking at them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@niksedk niksedk closed this Jul 22, 2026
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