-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Add performance-benchmark skill for ad hoc benchmarking with EgorBot #123319
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
5ab11c8
Initial plan
Copilot cc0c486
Add performance-benchmark skill for ad hoc benchmarking with EgorBot
Copilot b01040b
Fix missing System.Linq using statements in benchmark examples
Copilot 4059503
Remove ICollection<TSource> check from Any() method
Copilot 3a84d8d
Update Best Practices based on Microbenchmark Design Guidelines
Copilot 2874ee7
Remove Baseline attribute from example and Azure targets preferred bu…
Copilot 66c2019
Revert ICollection<TSource> check removal in Any() method
Copilot 1775846
Update target flags guidance based on EgorBo's recommendations
Copilot 74bc8d1
Remove ICollection<TSource> check from Any() method for benchma…
Copilot 065b524
Revert ICollection<TSource> check removal in Any() method
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,260 @@ | ||
| --- | ||
| name: performance-benchmark | ||
| description: Generate and run ad hoc performance benchmarks to validate code changes. Use this when asked to benchmark, profile, or validate the performance impact of a code change in dotnet/runtime. | ||
| --- | ||
|
|
||
| # Ad Hoc Performance Benchmarking | ||
|
|
||
| When you need to validate the performance impact of a code change, follow this process to write a BenchmarkDotNet benchmark and trigger EgorBot to run it. | ||
|
|
||
| ## Step 1: Write the Benchmark | ||
|
|
||
| Create a BenchmarkDotNet benchmark that tests the specific operation being changed. Follow these guidelines: | ||
|
|
||
| ### Benchmark Structure | ||
|
|
||
| ```csharp | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Running; | ||
|
|
||
| BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args); | ||
|
|
||
| public class Bench | ||
| { | ||
| // Add setup/cleanup if needed | ||
| [GlobalSetup] | ||
| public void Setup() | ||
| { | ||
| // Initialize test data | ||
| } | ||
|
|
||
| [Benchmark(Baseline = true)] | ||
| public void BaselineMethod() | ||
| { | ||
| // Test the operation | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public void OptimizedMethod() | ||
| { | ||
| // Test the optimized version if applicable | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Best Practices | ||
|
|
||
| - **Avoid trivial benchmarks**: Include some complexity (e.g., loops, realistic data sizes) to get meaningful measurements | ||
stephentoub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - **Use `[Benchmark(Baseline = true)]`** to mark the baseline method when comparing implementations | ||
| - **Use `[GlobalSetup]`** to initialize test data outside the measured code | ||
| - **Avoid `[DisassemblyDiagnoser]`**: It causes crashes on Linux. Use `--envvars DOTNET_JitDisasm:MethodName` instead | ||
| - **Return values** from benchmark methods to prevent dead code elimination | ||
| - **Use realistic data sizes** that represent actual usage patterns | ||
stephentoub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Example: String Operation Benchmark | ||
|
|
||
| ```csharp | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Running; | ||
|
|
||
| BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args); | ||
|
|
||
| [MemoryDiagnoser] | ||
| public class Bench | ||
| { | ||
| private string _testString = default!; | ||
|
|
||
| [Params(10, 100, 1000)] | ||
| public int Length { get; set; } | ||
|
|
||
| [GlobalSetup] | ||
| public void Setup() | ||
| { | ||
| _testString = new string('a', Length); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public int StringOperation() | ||
| { | ||
| return _testString.IndexOf('z'); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Example: Collection Operation Benchmark | ||
|
|
||
| ```csharp | ||
| using System.Linq; | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Running; | ||
|
|
||
| BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args); | ||
|
|
||
| [MemoryDiagnoser] | ||
| public class Bench | ||
| { | ||
| private int[] _array = default!; | ||
| private List<int> _list = default!; | ||
|
|
||
| [Params(100, 1000, 10000)] | ||
| public int Count { get; set; } | ||
|
|
||
| [GlobalSetup] | ||
| public void Setup() | ||
| { | ||
| _array = Enumerable.Range(0, Count).ToArray(); | ||
| _list = _array.ToList(); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public int SumArray() | ||
| { | ||
| int sum = 0; | ||
| foreach (var item in _array) | ||
| sum += item; | ||
| return sum; | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public int SumList() | ||
| { | ||
| int sum = 0; | ||
| foreach (var item in _list) | ||
| sum += item; | ||
| return sum; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Step 2: Post the EgorBot Comment | ||
|
|
||
| Post a comment on the PR to trigger EgorBot with your benchmark. The general format is: | ||
|
|
||
| ``` | ||
| @EgorBot [target flags] [options] [BenchmarkDotNet args] | ||
|
|
||
| ```cs | ||
| // Your benchmark code here | ||
| ``` | ||
| ``` | ||
|
|
||
| ### Target Flags (Required - Choose at Least One) | ||
|
|
||
| | Flag | Architecture | Description | | ||
| |------|--------------|-------------| | ||
| | `-arm` | ARM64 | Azure Cobalt100 (Neoverse-N2) | | ||
| | `-intel` | x64 | Azure Cascade Lake | | ||
| | `-amd` or `-x64` | x64 | Azure Genoa (AMD EPYC 9V74) | | ||
|
|
||
| You can combine multiple targets: `-arm -intel -amd` | ||
stephentoub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Common Options | ||
|
|
||
| | Option | Description | | ||
| |--------|-------------| | ||
| | `-profiler` | Collect flamegraph/hot assembly using perf record | | ||
stephentoub marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | `--envvars KEY:VALUE` | Set environment variables (e.g., `DOTNET_JitDisasm:MethodName`) | | ||
| | `-commit <hash>` | Run against a specific commit | | ||
| | `-commit <hash1> vs <hash2>` | Compare two commits | | ||
| | `-commit <hash> vs previous` | Compare commit with its parent | | ||
stephentoub marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ### Example: Basic PR Benchmark | ||
|
|
||
| To benchmark the current PR changes against the base branch: | ||
|
|
||
| ``` | ||
| @EgorBot -intel -arm | ||
|
|
||
| ```cs | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Running; | ||
|
|
||
| BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args); | ||
|
|
||
| [MemoryDiagnoser] | ||
| public class Bench | ||
| { | ||
| [Benchmark] | ||
| public int MyOperation() | ||
| { | ||
| // Your benchmark code | ||
| return 42; | ||
| } | ||
| } | ||
| ``` | ||
| ``` | ||
|
|
||
| ### Example: Benchmark with Profiling and Disassembly | ||
|
|
||
| ``` | ||
| @EgorBot -intel -profiler --envvars DOTNET_JitDisasm:MyOperation | ||
|
|
||
| ```cs | ||
| using System.Linq; | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Running; | ||
|
|
||
| BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args); | ||
|
|
||
| public class Bench | ||
| { | ||
| private int[] _data = Enumerable.Range(0, 1000).ToArray(); | ||
|
|
||
| [Benchmark] | ||
| public int MyOperation() | ||
| { | ||
| int sum = 0; | ||
| foreach (var x in _data) | ||
| sum += x; | ||
| return sum; | ||
| } | ||
| } | ||
| ``` | ||
| ``` | ||
|
|
||
| ### Example: Compare Two Commits | ||
|
|
||
| ``` | ||
| @EgorBot -amd -commit abc1234 vs def5678 | ||
|
|
||
| ```cs | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Running; | ||
|
|
||
| BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args); | ||
|
|
||
| public class Bench | ||
| { | ||
| [Benchmark] | ||
| public void TestMethod() | ||
| { | ||
| // Benchmark code | ||
| } | ||
| } | ||
| ``` | ||
| ``` | ||
|
|
||
| ### Example: Run Existing dotnet/performance Benchmarks | ||
|
|
||
| To run benchmarks from the dotnet/performance repository (no code snippet needed): | ||
|
|
||
| ``` | ||
| @EgorBot -arm -intel --filter `*TryGetValueFalse<String, String>*` | ||
| ``` | ||
|
|
||
| **Note**: Surround filter expressions with backticks to avoid issues with special characters. | ||
|
|
||
| ## Important Notes | ||
|
|
||
| - **Bot response time**: EgorBot uses polling and may take up to 30 seconds to respond | ||
| - **Supported repositories**: EgorBot monitors `dotnet/runtime` and `EgorBot/runtime-utils` | ||
| - **PR mode (default)**: When posting in a PR, EgorBot automatically compares the PR changes against the base branch | ||
| - **Results variability**: Results may vary between runs due to VM differences. Do not compare results across different architectures or cloud providers | ||
| - **Azure targets preferred**: Use Azure targets (`-arm`, `-intel`, `-amd`) unless you have a specific reason to use AWS/Hetzner | ||
stephentoub marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - **Check the manual**: EgorBot replies include a link to the [manual](https://github.com/EgorBot/runtime-utils) for advanced options | ||
|
|
||
| ## Additional Resources | ||
|
|
||
| - [BenchmarkDotNet CLI Arguments](https://github.com/dotnet/BenchmarkDotNet/blob/master/docs/articles/guides/console-args.md) | ||
| - [EgorBot Manual](https://github.com/EgorBot/runtime-utils) | ||
| - [BenchmarkDotNet Filter Simulator](http://egorbot.westus2.cloudapp.azure.com:5042/microbenchmarks) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.