{"id":16210322,"url":"https://github.com/johannesdeml/microbenchmarksdotnet","last_synced_at":"2026-02-27T15:08:03.553Z","repository":{"id":76786737,"uuid":"357900862","full_name":"JohannesDeml/MicroBenchmarksDotNet","owner":"JohannesDeml","description":"C# Benchmarks for a better understanding of performance cost","archived":false,"fork":false,"pushed_at":"2025-05-20T13:19:33.000Z","size":130,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-20T14:31:43.805Z","etag":null,"topics":["benchmark","csharp","dotnet","dotnet-core","hacktoberfest","performance","statistics"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JohannesDeml.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-14T12:39:55.000Z","updated_at":"2025-05-20T13:19:47.000Z","dependencies_parsed_at":"2023-12-12T19:26:31.738Z","dependency_job_id":"42edea85-83cc-49b1-8701-a3e4752b10e5","html_url":"https://github.com/JohannesDeml/MicroBenchmarksDotNet","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/JohannesDeml/MicroBenchmarksDotNet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohannesDeml%2FMicroBenchmarksDotNet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohannesDeml%2FMicroBenchmarksDotNet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohannesDeml%2FMicroBenchmarksDotNet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohannesDeml%2FMicroBenchmarksDotNet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JohannesDeml","download_url":"https://codeload.github.com/JohannesDeml/MicroBenchmarksDotNet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohannesDeml%2FMicroBenchmarksDotNet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29901298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T14:46:13.553Z","status":"ssl_error","status_checked_at":"2026-02-27T14:46:10.522Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["benchmark","csharp","dotnet","dotnet-core","hacktoberfest","performance","statistics"],"created_at":"2024-10-10T10:37:05.276Z","updated_at":"2026-02-27T15:08:03.510Z","avatar_url":"https://github.com/JohannesDeml.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Micro Benchmarks for C#\n\n*Various benchmarks for showing hidden performance costs*  \n[![Releases](https://img.shields.io/github/release/JohannesDeml/MicroBenchmarksDotNet/all.svg)](../../releases)[![.NET 6.0](https://img.shields.io/badge/.NET-6.0-blueviolet.svg)](https://dotnet.microsoft.com/download/dotnet/6.0)\n\n## Setup\nTo reproduce the results, run `win-benchmark.bat` or `linux-benchmark.sh` as admin/root.  \nThe benchmarks are run with 2020 gaming PC after bootup of the system - [Hardware Details](https://pcpartpicker.com/b/8MMcCJ)  \n\n```ini\nBenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042\nAMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores\n  [Host]     : .NET Framework 4.8 (4.8.4341.0), X64 RyuJIT\n  Job-YCYQHD : .NET Framework 4.8 (4.8.4341.0), X64 RyuJIT\n  Job-ZUSNEY : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT\n  Job-BBIORM : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT\n  Job-OVYJNY : Mono 5.11.0 (Visual Studio), X86 \n\nPlatform=X64  Concurrent=True  Force=True  \nServer=True  IterationTime=250.0000 ms  MaxIterationCount=20  \nMinIterationCount=15  UnrollFactor=16  WarmupCount=1  \nVersion=1.0.0  OS=Microsoft Windows 10.0.19042   DateTime=04/13/2021 12:37:54  \n```\n\nBy default only **.NET 6** is tested. However, you can target different runtimes with the environment variable `TARGET_RUNTIMES` (example: `export TARGET_RUNTIMES=Core60,Core50,Net48,Mono`). See also [linux-benchmark-all-runtimes.sh](./linux-benchmark-all-runtimes.sh) as an example for targeting all platforms.\n\n| Runtime      | Variable |\n| ------------ | -------- |\n| .NET 7       | Core70   |\n| NativeAOT 7  | Aot70    |\n| .NET 6       | Core60   |\n| NativeAOT 6, | Aot60    |\n| .NET 5       | Core50   |\n| .NET 4.8     | Net48    |\n| (Unity) Mono | Mono     |\n\n## Findings\n\n### Conditional methods ([Test](./MicroBenchmarks/Comparisons/ConditionalFormattedLoggingBenchmark.cs))\n\n* stripping method call logic happens also for the parameters inside the method call, so you don't have to worry about string concatenation if you do it in the call itself.\n\n  * ```csharp\n    // No overhead when stripped\n    // the string interpolation is stripped away, if Log is stripped away\n    Log($\"firstParam: {firstParam}, secondParam: {secondParam}, thirdParam:{thirdParam}\");\n    ```\n\n  * ```csharp\n    // Adds overheas when stripped\n    // String interpolation happens independant of stripping of Log\n    string preparedMessage = $\"firstParam: {firstParam}, secondParam: {secondParam}, thirdParam:{thirdParam}\";\n    Log(preparedMessage);\n    ```\n\n\n\n### Lambda calls ([Test](./MicroBenchmarks/Comparisons/LambdaBenchmark.cs))\n\n* Even though lambdas are a nice way to add code directly within the method, it does result in less performance than having a direct method call and adds additional memory pressure. Using functions which don't require access to local variables does not result in additional memory pressure and has less general overhead.\n\n  * ```csharp\n    // Slow\n    // accesses local variables and therefore allocates additional memory\n    Action lambda = () =\u003e { result = FirstValue + SecondValue; };\n    lambda.Invoke();\n    return result;\n    ```\n\n  * ```csharp\n    // Faster\n    // no need to access local variables\n    var lambda = new Func\u003cint, int, int\u003e((a, b) =\u003e a + b);\n    return lambda.Invoke(FirstValue, SecondValue);\n    ```\n\n  * ```csharp\n    // Fastest\n    // direct calls are still the way to go for critical code paths\n    int AddWithReturn(int a, int b)\n    {\n    \treturn a + b;\n    }\n    return AddWithReturn(FirstValue, SecondValue);\n    ```\n\n\n\n### Caller Information ([Test](./MicroBenchmarks/Comparisons/CallerInformationAttributesBenchmark.cs))\n\n* Using attributes such as `[CallerMemberName]`, `[CallerFilePath]` and `[CallerLineNumber]` are a great addition to retrieve information about the calling methods without relying on expensive stacktrace methods. The overhead of the attributes is not measurable with Benchmarkdotnet (and therefore virtually nothing).\n\n\n\n### String Search ([Test](./MicroBenchmarks/Comparisons/StringSearchBenchmark.cs))\n\n* For short strings: `IndexOfChar` and `LastIndexOfChar` are significantly faster than both `IndexOfString` and `LastIndexOfString`.\n* For long strings: While `IndexOfChar` is only slightly faster than `IndexOfString`, the increase between `LastIndexOfChar` and `LastIndexOfString`  is significant for .NET 6. `LastIndexOfChar` is over 7 times faster than `LastIndexOfString`, but still slower than the other two methods.\n\n\n\n### Hash Generation ([Test](./MicroBenchmarks/Comparisons/HashGenerationBenchmark.cs))\n\n![Hash Generation Comparison Chart](./Docs/hashgeneration100bytes-1.0.0.png)\n\n* If you can, always use `TryXHash()` instead of `XHash()`\n* Surprisingly, Sha256 performs better, than Sha1 which performs better than Md5 for .NET Core and .NET framework on modern hardware for .NET, for Mono it is the other way around. Always test on your target hardware, which is faster and don't assume that Md5 will be faster than Sha256 not matter what.\n\n\n\n### UDP Sockets ([Test](./MicroBenchmarks/Comparisons/UdpBenchmark.cs))\n\n![UDP Comparison Chart](./Docs/udpsocket-sendreceive-1.0.0.png)\n\n* For some (for me yet unknown) reason, UDP performs a lot better on Windows safe mode, than it does with normal windows mode. This is the only benchmark, that shows that kind of behavior. For all other benchmarks the results are statistically the same for normal and safe mode.\n* A further discussion can be found on [superuser](https://superuser.com/questions/1640588/windows-10-udp-socket-benchmark-a-lot-faster-in-safe-mode). If you have any input, I would love to know!\n\n\n\n### Pause Accuracy ([Test](./MicroBenchmarks/Comparisons/PauseAccuracyBenchmark.cs))\n\n![Pause Accuracy Chart](./Docs/pauseaccuracy2ms-1.0.0.png)\n\n* `Thread.SpinWait(10)` and `Thread.Sleep(0)` allow for maximum precision, also on windows, but they block processing time that might be needed by other processes.\n* `Thread.Sleep(TimeoutDuration)`  and all others have the problem that the granularity follows that of the system timer. For windows this is ~15ms.  \n* There is trick to change the granularity by using winmm.dll's `timeBeginPeriod(uint period)` and `timeEndPeriod(uint period)`. This can be seen with ThreadSleepEnhanced - It works for .NET 5 and .NET Core 3.1, but not the others.\n* Linux is a lot better in its granularity, but `TaskDelay` and `TimerAwait` have precision problems as well on .NET Core.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohannesdeml%2Fmicrobenchmarksdotnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohannesdeml%2Fmicrobenchmarksdotnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohannesdeml%2Fmicrobenchmarksdotnet/lists"}