performance-benchmark
Skill679 starsChanged 49 days agoApache-2.0View on GitHub
Generate and run ad hoc BenchmarkDotNet benchmarks to validate the performance impact of a code change in opentelemetry-dotnet-contrib. Use this when asked to benchmark, profile, or validate performance claims in a PR.
4 other projects use a version of this, 2 of them edited.
Adopted by
Most recently taken up by
- domaindrivendev/Swashbuckle.AspNetCoreedited, first seen Oct 8, 2026
- App-vNext/Pollytheir own version, first seen Oct 8, 2026
- open-telemetry/opentelemetry-dotnetedited, first seen Oct 8, 2026
- Omerfaruk-aydn/Agent_OStheir own version, first seen Jul 20, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 2
- domaindrivendev/Swashbuckle.AspNetCore★ 5.5k10% same
- open-telemetry/opentelemetry-dotnet★ 3.8k27% same
Wrote their own version · 2
- App-vNext/Polly★ 14ksame name
- Omerfaruk-aydn/Agent_OS★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository3 other files it gives its agents.
- AGENTS.mdAGENTS.md
- code-reviewSkill
- semantic-conventions-upgradeSkill
The file
We split this skill into the rules it gives an agent
10 rules. 0 are common, 2 are distinctive, and 8 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 8
When you need to validate the performance impact of a code change, write a BenchmarkDotNet benchmark in the affected component's …
- Very specific: line 27
If a benchmark project doesn't exist yet, base the new project on an existing one such as …
- Very specific: line 35
Add a <ProjectReference> to the component under test - like an ordinary test project, a benchmark project references the shipping …
- Very specific: line 42
Follow the same folder convention as tests when there are multiple benchmark classes (e.g.
- Very specific: line 49
Move initialization to
[GlobalSetup]/[GlobalCleanup]: keep setup/teardown out of the measured method. - Very specific: line 57
Use
[MemoryDiagnoser]: when the change affects allocations - most instrumentation benchmarks in this repository enable it (see REVIEW.md … - Distinctive: line 61
If the benchmark environment's processors include both performance and efficiency cores (e.g., on Apple M1/M2 or Intel hybrid …
- Distinctive: line 126
Commit the benchmark itself and the change under test before running it - stashing either one makes it absent from every ref the script …
- Very specific: line 163
Run Get-Help ./benchmark.ps1 -Full for the complete parameter reference - it's the authoritative source for script behavior and should not …
- Very specific: line 186
If results show a regression in a specific scenario even though the overall change is a net improvement, call it out explicitly rather than …
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Ad Hoc Performance Benchmarking
When you need to validate the performance impact of a code change, write a
BenchmarkDotNet benchmark in the affected component's *.Benchmarks project and
use the repository's benchmark.ps1 script to compare a target ref against a
baseline ref. Unlike a manual build-and-swap workflow, benchmark.ps1 handles
checking out each ref, building, and running the benchmark for you.
Per REVIEW.md "Performance": any PR that claims a performance improvement
should include BenchmarkDotNet results to substantiate the claim, and
measurement should happen before and after the change rather than relying on
assumptions.
Step 1: Locate or Create the Benchmark Project
Benchmark projects live at test/OpenTelemetry.{Type}.{Name}.Benchmarks/
alongside the corresponding test/OpenTelemetry.{Type}.{Name}.Tests/ project
(see AGENTS.md "Repository Layout"). Check whether one already exists for the
component you're changing before creating a new one - e.g.
test/OpenTelemetry.Instrumentation.Http.Benchmarks/.
If a benchmark project doesn't exist yet, base the new project on an existing
one such as test/OpenTelemetry.Instrumentation.Http.Benchmarks/:
OutputTypeisExeandTargetFrameworksis$(SupportedNetTargets).Program.csis a one-liner:BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args).- Reference
BenchmarkDotNetviaDirectory.Packages.props(do not pin a version in the project file - seeAGENTS.md"Package Management"). - Add a
<ProjectReference>to the component under test - like an ordinary test project, a benchmark project references the shipping component's.csprojrather than linking its source. The exception issrc/Sharedcode, which has no shipping project to reference: link the Shared file directly instead, following the<Compile Include="$(RepoRoot)\src\Shared\...\" Link="Includes\...\" />pattern used bytest/OpenTelemetry.Contrib.Shared.Benchmarks. - Follow the same folder convention as tests when there are multiple benchmark
classes (e.g.
Instrumentation/,Exporter/).
Step 2: Write the Benchmark
Best Practices
- Move initialization to
[GlobalSetup]/[GlobalCleanup]: keep setup/teardown out of the measured method. - Avoid manual loops: BenchmarkDotNet invokes the method many times automatically.
- No side effects: benchmarks must be pure and produce consistent results across runs.
- Focus on the hot path being changed: instrumentation and exporter code runs per-request, so benchmark the common case, not error paths.
- Use
[MemoryDiagnoser]when the change affects allocations - most instrumentation benchmarks in this repository enable it (seeREVIEW.md"Performance" - avoiding unnecessary allocations on the hot path is a stated goal). - If the benchmark environment's processors include both performance and
efficiency cores (e.g., on Apple M1/M2 or Intel hybrid architectures),
consider pinning the benchmark to the performance cores to reduce noise.
This can be achieved using the
--affinityoption in BenchmarkDotNet to specify an affinity mask to set for the benchmark process. - Use
[Params]to compare variants relevant to the change (e.g. instrumentation enabled vs. disabled, or old vs. new semantic-convention opt-in mode) in a single run rather than writing near-duplicate benchmark methods. - Benchmark class requirements:
public, notsealed, notstatic, and aclass(not astruct).
Example
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
using BenchmarkDotNet.Attributes;
namespace OpenTelemetry.Instrumentation.Example.Benchmarks;
[MemoryDiagnoser]
public class ExampleBenchmarks
{
private TracerProvider? tracerProvider;
[Params(false, true)]
public bool EnableInstrumentation { get; set; }
[GlobalSetup]
public void Setup()
{
if (this.EnableInstrumentation)
{
this.tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddExampleInstrumentation()
.Build();
}
}
[GlobalCleanup]
public void Cleanup() => this.tracerProvider?.Dispose();
[Benchmark]
public void DoWork()
{
// Exercise the code path being changed.
}
}Also follow the general BenchmarkDotNet microbenchmark design guidelines that the .NET runtime itself follows for their own benchmarks: no manual loops (BenchmarkDotNet iterates for you), return values to avoid dead-code elimination, keep setup out of the measured method, and use consistent input data across runs.
Step 3: Run the Benchmark with benchmark.ps1
The repository root has a benchmark.ps1 script that checks out a target ref
and an optional baseline ref (default main), builds, and runs the benchmark
for each, writing artifacts to BenchmarkDotNet.Artifacts/<ref-name>/. It
requires a clean working tree because it switches refs while running.
Commit the benchmark itself and the change under test before running it -
stashing either one makes it absent from every ref the script checks out, so
the run can silently benchmark the old code or fail to find the benchmark at
all. Only stash unrelated, unfinished work that isn't part of this benchmark.
The selected benchmark must exist with the same name in both refs. For a new
benchmark, place the benchmark in its own commit (separate from the change
under test) and use that commit for -Baseline; comparing directly with
main cannot produce a baseline result for a benchmark that is not yet
present in that branch.
# Compare the current branch against main for a specific filter
./benchmark.ps1 "Instrumentation.Http" @("*HttpClientInstrumentationBenchmarks*")
# Compare an explicit target branch against main, with memory diagnostics
./benchmark.ps1 "Instrumentation.AspNetCore" @("*SamplerBenchmarks*") -Target my-feature -EnableMemoryDiagnoser
# Only run the current branch (no baseline comparison)
./benchmark.ps1 "Contrib.Shared" @("*SQL*") -SkipBaseline
# Run against multiple TFMs, including net472 (Windows only)
./benchmark.ps1 "Exporter.Geneva" @("*") -Runtimes @("net10.0", "net472") -Job ShortKey parameters:
ProjectName- accepts the short name (Instrumentation.Http) or fully-qualified name; the script resolves it againsttest/OpenTelemetry*.Benchmarks/.Benchmarks- one or more--filterexpressions.Target/Baseline- refs to compare;Baselinedefaults tomain.Job- a BenchmarkDotNet job name (e.g.Short) for faster iteration.Runtimes- target frameworks;.NET Frameworkruntimes only run on Windows.-EnableMemoryDiagnoser/-EnableEventPipeProfiler- opt-in diagnostics.-SkipBaseline- benchmark only the target ref.
Run Get-Help ./benchmark.ps1 -Full for the complete parameter reference - it's
the authoritative source for script behavior and should not be duplicated here.
Alternatively, for interactive exploration of a single project without a
baseline comparison, run it directly as documented in the project's own
README.md, e.g.:
cd test/OpenTelemetry.Instrumentation.Http.Benchmarks
dotnet run -c Release -f net10.0 -- -mSee the BenchmarkDotNet console args guide for the raw CLI form used by both invocations.
Step 4: Report Results
- Include the BenchmarkDotNet results table (Mean/Error/StdDev, and Allocated
when
[MemoryDiagnoser]is used) in the PR description or review comment - perREVIEW.md"Performance", this is required evidence for any performance claim, not optional supporting material. A paraphrased summary for a reviewer to glance at may be optionally included. - If results show a regression in a specific scenario even though the overall
change is a net improvement, call it out explicitly rather than omitting it -
per the
pr-assessment.md"Evidence & Data" guidance in thecode-reviewskill.
[!NOTE] AI-generated content disclosure: When posting benchmark results to GitHub under a user's credentials - i.e. the account is not a dedicated "copilot" or "bot" account/app - include a concise, visible disclosure note indicating the content was AI/Copilot-generated. Skip this only if the user explicitly asks you to omit it.
Build/test commands and general conventions are in
AGENTS.md; performance-specific review rules
(measuring before/after, avoiding unnecessary allocations on the hot path,
and preferring FrozenSet<T> where applicable) are in
REVIEW.md "Performance" and "Miscellaneous". This
skill only covers how to produce the benchmark evidence those rules ask for.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

