├── .github └── workflows │ ├── Arrays Benchmark.yml │ ├── BranchingTechniques.yml │ ├── ExceptionHandlingTechniques.yml │ ├── IndexedCollectionAbstractions.yml │ ├── MethodAbstractions.yml │ ├── MultipleReturnsTechniques.yml │ ├── ReflectionTechniques.yml │ └── StringFormattingTechniques.yml ├── .gitignore ├── Articles ├── params.md ├── readme.md └── simd.md ├── Benchmarks ├── Arrays │ ├── ArrayVsRawPointer.cs │ ├── Arrays.csproj │ ├── Delegates.cs │ ├── Intrinsics.cs │ ├── ParallelFor.cs │ ├── Program.cs │ └── README.md ├── Boxing │ └── Boxing │ │ ├── Boxing.csproj │ │ ├── EqualsBoxing.cs │ │ └── Program.cs ├── BranchingTechniques │ ├── BranchingTechniques.cs │ ├── BranchingTechniques.csproj │ ├── BranchingTechniques.tt │ ├── Program.cs │ └── README.md ├── ExceptionHandlingTechniques │ ├── ExceptionHandlingBenchmarks.cs │ ├── ExceptionHandlingTechniques.csproj │ ├── Program.cs │ └── README.md ├── IndexedCollectionAbstractions │ ├── IndexedCollectionAbstractions.csproj │ ├── IndexedCollectionBenchmarks.cs │ ├── ParamsIndexerVsOverloads.cs │ ├── Program.cs │ └── README.md ├── IterationRefParametersVsReturns │ ├── IterationRefParametersVsReturns.csproj │ ├── IterationRefParametersVsReturnsBenchmarks.cs │ ├── Program.cs │ └── README.md ├── MethodAbstractions │ ├── AdditionBenchmarks.cs │ ├── EnumerableBenchmarks.cs │ ├── MethodAbstractions.csproj │ ├── Program.cs │ └── README.md ├── MultipleReturnsTechniques │ ├── MultipleReturnsIntBenchmarks.cs │ ├── MultipleReturnsIntBenchmarks.tt │ ├── MultipleReturnsObjectBenchmarks.cs │ ├── MultipleReturnsObjectBenchmarks.tt │ ├── MultipleReturnsTechniques.csproj │ ├── Program.cs │ └── README.md ├── README.md ├── ReflectionTechniques │ ├── Program.cs │ ├── README.md │ ├── ReflectionBenchmarks.cs │ └── ReflectionTechniques.csproj ├── StringFormattingTechniques │ ├── Program.cs │ ├── README.md │ ├── StringBenchmarks.cs │ ├── StringBenchmarks.tt │ └── StringFormattingTechniques.csproj ├── TypeEqualityChecking │ ├── Program.cs │ ├── README.md │ ├── TypeEqualityChecking.csproj │ ├── TypeEqualityCheckingBenchmarks.cs │ └── TypeEqualityCheckingBenchmarks.tt └── dotnet-benchmarks.sln ├── LICENSE └── README.md /.github/workflows/Arrays Benchmark.yml: -------------------------------------------------------------------------------- 1 | name: Arrays Benchmark 2 | 3 | on: 4 | #push: 5 | # paths: 6 | # - 'Arrays/**' 7 | #pull_request: 8 | # paths: 9 | # - 'Arrays/**' 10 | workflow_dispatch: 11 | inputs: 12 | dotnet-version: 13 | description: '.NET Version' 14 | required: true 15 | default: '5.0.100-preview.7.20366.6' 16 | os: 17 | description: 'OS' 18 | required: true 19 | default: 'windows-latest, ubuntu-latest, macos-latest' 20 | 21 | jobs: 22 | Arrays: 23 | runs-on: ${{ github.event.inputs.os }} 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: ${{ github.event.inputs.dotnet-version }} 30 | - name: Running Benchmarking 31 | run: dotnet run -p Arrays -c Release -------------------------------------------------------------------------------- /.github/workflows/BranchingTechniques.yml: -------------------------------------------------------------------------------- 1 | name: Branching Techniques Benchmark 2 | 3 | on: 4 | #push: 5 | # paths: 6 | # - 'BranchingTechniques/**' 7 | #pull_request: 8 | # paths: 9 | # - 'BranchingTechniques/**' 10 | workflow_dispatch: 11 | inputs: 12 | dotnet-version: 13 | description: '.NET Version' 14 | required: true 15 | default: '5.0.100-preview.7.20366.6' 16 | os: 17 | description: 'OS' 18 | required: true 19 | default: 'windows-latest, ubuntu-latest, macos-latest' 20 | 21 | jobs: 22 | Arrays: 23 | runs-on: ${{ github.event.inputs.os }} 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: ${{ github.event.inputs.dotnet-version }} 30 | - name: Running Benchmarking 31 | run: dotnet run -p BranchingTechniques -c Release 32 | -------------------------------------------------------------------------------- /.github/workflows/ExceptionHandlingTechniques.yml: -------------------------------------------------------------------------------- 1 | name: Exception Handling Techniques Benchmark 2 | 3 | on: 4 | #push: 5 | # paths: 6 | # - 'ExceptionHandlingTechniques/**' 7 | #pull_request: 8 | # paths: 9 | # - 'ExceptionHandlingTechniques/**' 10 | workflow_dispatch: 11 | inputs: 12 | dotnet-version: 13 | description: '.NET Version' 14 | required: true 15 | default: '5.0.100-preview.7.20366.6' 16 | os: 17 | description: 'OS' 18 | required: true 19 | default: 'windows-latest, ubuntu-latest, macos-latest' 20 | 21 | jobs: 22 | Arrays: 23 | runs-on: ${{ github.event.inputs.os }} 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: ${{ github.event.inputs.dotnet-version }} 30 | - name: Running Benchmarking 31 | run: dotnet run -p ExceptionHandlingTechniques -c Release 32 | -------------------------------------------------------------------------------- /.github/workflows/IndexedCollectionAbstractions.yml: -------------------------------------------------------------------------------- 1 | name: Indexed Collection Abstractions Benchmark 2 | 3 | on: 4 | #push: 5 | # paths: 6 | # - 'IndexedCollectionAbstractions/**' 7 | #pull_request: 8 | # paths: 9 | # - 'IndexedCollectionAbstractions/**' 10 | workflow_dispatch: 11 | inputs: 12 | dotnet-version: 13 | description: '.NET Version' 14 | required: true 15 | default: '5.0.100-preview.7.20366.6' 16 | os: 17 | description: 'OS' 18 | required: true 19 | default: 'windows-latest, ubuntu-latest, macos-latest' 20 | 21 | jobs: 22 | Arrays: 23 | runs-on: ${{ github.event.inputs.os }} 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: ${{ github.event.inputs.dotnet-version }} 30 | - name: Running Benchmarking 31 | run: dotnet run -p IndexedCollectionAbstractions -c Release 32 | -------------------------------------------------------------------------------- /.github/workflows/MethodAbstractions.yml: -------------------------------------------------------------------------------- 1 | name: Method Abstractions Benchmark 2 | 3 | on: 4 | #push: 5 | # paths: 6 | # - 'MethodAbstractions/**' 7 | #pull_request: 8 | # paths: 9 | # - 'MethodAbstractions/**' 10 | workflow_dispatch: 11 | inputs: 12 | dotnet-version: 13 | description: '.NET Version' 14 | required: true 15 | default: '5.0.100-preview.7.20366.6' 16 | os: 17 | description: 'OS' 18 | required: true 19 | default: 'windows-latest, ubuntu-latest, macos-latest' 20 | 21 | jobs: 22 | Arrays: 23 | runs-on: ${{ github.event.inputs.os }} 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: ${{ github.event.inputs.dotnet-version }} 30 | - name: Running Benchmarking 31 | run: dotnet run -p MethodAbstractions -c Release -------------------------------------------------------------------------------- /.github/workflows/MultipleReturnsTechniques.yml: -------------------------------------------------------------------------------- 1 | name: Multiple Returns Techniques Benchmark 2 | 3 | on: 4 | #push: 5 | # paths: 6 | # - 'MultipleReturnsTechniques/**' 7 | #pull_request: 8 | # paths: 9 | # - 'MultipleReturnsTechniques/**' 10 | workflow_dispatch: 11 | inputs: 12 | dotnet-version: 13 | description: '.NET Version' 14 | required: true 15 | default: '5.0.100-preview.7.20366.6' 16 | os: 17 | description: 'OS' 18 | required: true 19 | default: 'windows-latest, ubuntu-latest, macos-latest' 20 | 21 | jobs: 22 | Arrays: 23 | runs-on: ${{ github.event.inputs.os }} 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: ${{ github.event.inputs.dotnet-version }} 30 | - name: Running Benchmarking 31 | run: dotnet run -p MultipleReturnsTechniques -c Release -------------------------------------------------------------------------------- /.github/workflows/ReflectionTechniques.yml: -------------------------------------------------------------------------------- 1 | name: Reflection Techniques Benchmark 2 | 3 | on: 4 | #push: 5 | # paths: 6 | # - 'ReflectionTechniques/**' 7 | #pull_request: 8 | # paths: 9 | # - 'ReflectionTechniques/**' 10 | workflow_dispatch: 11 | inputs: 12 | dotnet-version: 13 | description: '.NET Version' 14 | required: true 15 | default: '5.0.100-preview.7.20366.6' 16 | os: 17 | description: 'OS' 18 | required: true 19 | default: 'windows-latest, ubuntu-latest, macos-latest' 20 | 21 | jobs: 22 | Arrays: 23 | runs-on: ${{ github.event.inputs.os }} 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: ${{ github.event.inputs.dotnet-version }} 30 | - name: Running Benchmarking 31 | run: dotnet run -p ReflectionTechniques -c Release 32 | -------------------------------------------------------------------------------- /.github/workflows/StringFormattingTechniques.yml: -------------------------------------------------------------------------------- 1 | name: String Formatting Techniques Benchmark 2 | 3 | on: 4 | #push: 5 | # paths: 6 | # - 'StringFormattingTechniques/**' 7 | #pull_request: 8 | # paths: 9 | # - 'StringFormattingTechniques/**' 10 | workflow_dispatch: 11 | inputs: 12 | dotnet-version: 13 | description: '.NET Version' 14 | required: true 15 | default: '5.0.100-preview.7.20366.6' 16 | os: 17 | description: 'OS' 18 | required: true 19 | default: 'windows-latest, ubuntu-latest, macos-latest' 20 | 21 | jobs: 22 | Arrays: 23 | runs-on: ${{ github.event.inputs.os }} 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: ${{ github.event.inputs.dotnet-version }} 30 | - name: Running Benchmarking 31 | run: dotnet run -p StringFormattingTechniques -c Release -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /Articles/params.md: -------------------------------------------------------------------------------- 1 | ## "Params" kills the performance 2 | 3 | Benchmark 4 | 5 | ### Do not use params when number of arguments vary very little 6 | 7 | Unlike C++ (where you usually make a template method), 8 | in C# this merely creates an array. Say, you want to find the sum of numbers that you pass 9 | to an index. Now, say that BenchOverloaded1D has an overload with one argument, while 10 | BenchLazy1D only has an indexer (method, whatever) with `params int[] ids`: 11 | 12 | | Method | Mean | 13 | |----------------------- |-----------:| 14 | | BenchLazy1D | 656.5 ns | 15 | | BenchOverloaded1D | 172.2 ns | <- the overload is hit here 16 | 17 | We see, that the method with an overload for one argument is by far more efficient, than 18 | those without any overloads. So if you don't plan to have a lot of arguments passed to the method, 19 | but varying from 0 to, say, 3, you are likely to switch to the overloaded mechanism. 20 | 21 | ### Use them when a lot of arguments likely to be passed 22 | 23 | The tip about overloading won't work if you constantly pass quite a lot of arguments. Let us look 24 | at the case for more than 1 argument passed: 25 | 26 | | Method | Mean | 27 | |----------------------- |-----------:| 28 | | BenchLazy2D | 752.2 ns | 29 | | BenchOverloaded2D | 1,250.3 ns | 30 | 31 | The overloaded version is now far behind (its arguments are (int, params int[])). Yes, you can 32 | overload for 2 arguments as well, but in general, it might not work if you have no ideas how many 33 | arguments potentially could be. 34 | 35 | ### Drawbacks of overloads 36 | 37 | Is that you have to add (int[]) overload too, otherwise you won't be able to pass an array of those 38 | arguments. 39 | 40 | ### Conclusion 41 | 42 | That is how I see when 0 to 3 arguments are most likely to be passed: 43 | 44 | ```cs 45 | Method() => ... 46 | Method(T a) => ... 47 | Method(T a, T b) => ... 48 | Method(T a, T b, T c) => ... 49 | Method(T a, T b, T c, params T[] others) => ... 50 | Method(T[] values) => ... 51 | ``` 52 | 53 | which, in terms of logic, is equivalent to 54 | 55 | ```cs 56 | Method(params T[] values) => ... 57 | ``` 58 | -------------------------------------------------------------------------------- /Articles/readme.md: -------------------------------------------------------------------------------- 1 | ## Articles 2 | 3 | Here community adds their five cents to the results of benchmarks. Just for convenience sake, 4 | we somehow interpret results of benchmarks and add these advices. 5 | 6 | - [SIMD instructions on arrays, when it's worth](./simd.md) 7 | - [Be careful when using Params](./params.md) -------------------------------------------------------------------------------- /Articles/simd.md: -------------------------------------------------------------------------------- 1 | ## SIMD Instructions. When to use and when not to use 2 | 3 | Benchmark 4 | 5 | ### Use on small data 6 | 7 | Modern CPUs are capable of storing decent amount of data in their caches, and for 8 | many cases it will even store it in L1. Let us see how efficient it for an array of integers 9 | on a machine that supports AVX-256: 10 | 11 | | Method | Length | Mean | 12 | |----------- |--------- |-----------------:| 13 | | NoVector | 160 | 117.27 ns | 14 | | VectorT | 160 | 19.99 ns | 15 | | Vector256T | 160 | 19.50 ns | 16 | | | | | 17 | | NoVector | 1600 | 1,070.47 ns | 18 | | VectorT | 1600 | 168.75 ns | 19 | | Vector256T | 1600 | 153.90 ns | 20 | 21 | That's a huge boost. 22 | 23 | ### Don't use on big data 24 | 25 | If you know that you are going to process a huge chunk of data, you are likely to 26 | face RAM access performance issues, as CPUs cannot store a lot of data in their caches. 27 | Let's have a look: 28 | 29 | | Method | Length | Mean | 30 | |----------- |--------- |-----------------:| 31 | | NoVector | 1600000 | 1,395,028.32 ns | 32 | | VectorT | 1600000 | 1,039,098.82 ns | 33 | | Vector256T | 1600000 | 1,097,105.87 ns | 34 | | | | | 35 | | NoVector | 16000000 | 14,293,466.20 ns | 36 | | VectorT | 16000000 | 11,581,997.24 ns | 37 | | Vector256T | 16000000 | 11,712,245.86 ns | 38 | 39 | There's a slight performance improvement, but it may not worth the code you would write 40 | to use SIMD. -------------------------------------------------------------------------------- /Benchmarks/Arrays/ArrayVsRawPointer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.InteropServices; 4 | using BenchmarkDotNet.Attributes; 5 | 6 | public class ArrayVsRawPointer 7 | { 8 | // TODO: replace a const with [Params] 9 | // but keep in mind LENGTH is used in Setup 10 | public const int LENGTH = 1_000; 11 | 12 | public int slowChaoticId = 0; 13 | public int fastChaoticId = 0; 14 | public int slowRowId = 0; 15 | public int fastRowId = 0; 16 | 17 | [GlobalSetup] 18 | public unsafe void Setup() 19 | { 20 | slowChaoticId = 0; 21 | fastChaoticId = 0; 22 | slowRowId = 0; 23 | fastRowId = 0; 24 | arrSlow = new int[LENGTH]; 25 | for (int i = 0; i < LENGTH; i++) 26 | arrSlow[i] = i; 27 | var r = new Random(); 28 | arrSlow = arrSlow.OrderBy(c => r.Next(0, LENGTH)).ToArray(); 29 | 30 | arrFast = (int*)Marshal.AllocHGlobal(LENGTH * sizeof(int)).ToPointer(); 31 | for (int i = 0; i < LENGTH; i++) 32 | arrFast[i] = arrSlow[i]; 33 | } 34 | 35 | [GlobalCleanup] 36 | public unsafe void Cleanup() 37 | { 38 | Marshal.FreeHGlobal((IntPtr)arrFast); 39 | } 40 | 41 | public int[] arrSlow; 42 | public unsafe int* arrFast; 43 | 44 | 45 | [Benchmark] 46 | public void TestSlowChaotic() 47 | { 48 | slowChaoticId = arrSlow[slowChaoticId]; 49 | } 50 | 51 | [Benchmark] 52 | public unsafe void TestFastChaotic() 53 | { 54 | fastChaoticId = arrFast[fastChaoticId]; 55 | } 56 | 57 | [Benchmark] 58 | public void TestSlowRow() 59 | { 60 | var _ = arrSlow[slowRowId]; 61 | slowRowId++; 62 | if (slowRowId >= LENGTH) 63 | slowRowId = 0; 64 | } 65 | 66 | [Benchmark] 67 | public unsafe void TestFastRow() 68 | { 69 | var _ = arrFast[fastRowId]; 70 | fastRowId++; 71 | if (fastRowId >= LENGTH) 72 | fastRowId = 0; 73 | } 74 | } 75 | 76 | /* 77 | Outdated 78 | 79 | | Method | 10 | 1_000 | 30_000 | 1_000_000 | 16_000_000 | 80 | |---------------- |----------:|----------:|----------:|-----------:|-----------:| 81 | | TestSlowChaotic | 0.9583 ns | 1.0177 ns | 2.8141 ns | 21.2463 ns | 86.9868 ns | 82 | | TestFastChaotic | 0.9741 ns | 0.9583 ns | 2.7750 ns | 24.7405 ns | 86.9611 ns | 83 | | TestSlowRow | 0.8036 ns | 0.7278 ns | 0.7230 ns | 0.7543 ns | 0.6666 ns | 84 | | TestFastRow | 0.3722 ns | 0.3746 ns | 0.3648 ns | 0.6517 ns | 0.5979 ns | 85 | 86 | */ -------------------------------------------------------------------------------- /Benchmarks/Arrays/Arrays.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 11 | 4 12 | 13 | 14 | 15 | 16 | true 17 | 18 | 19 | 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Benchmarks/Arrays/Delegates.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Results of benchmark for i7-7700HQ are at the end of the file 3 | */ 4 | 5 | using System; 6 | using System.Runtime.CompilerServices; 7 | using BenchmarkDotNet.Attributes; 8 | 9 | namespace Arrays 10 | { 11 | public class BenchArrays 12 | { 13 | [Params(100, 1000, 10000)] 14 | public int Length { get; set; } 15 | public const int CAPACITY = 10000; 16 | 17 | public readonly int[] a = new int[CAPACITY]; 18 | public readonly int[] b = new int[CAPACITY]; 19 | public readonly int[] c = new int[CAPACITY]; 20 | 21 | [GlobalSetup] 22 | public void Setup() 23 | { 24 | for (int i = 0; i < Length; i++) 25 | { 26 | unchecked 27 | { 28 | a[i] = i * i; 29 | b[i] = i * i * i; 30 | } 31 | } 32 | } 33 | 34 | [Benchmark] 35 | [MethodImpl(MethodImplOptions.NoOptimization)] 36 | public void PureLoopNoOptimization() 37 | { 38 | var locA = a; 39 | var locB = b; 40 | var locC = c; 41 | for (int i = 0; i < Length; i++) 42 | { 43 | unchecked 44 | { 45 | locC[i] = locA[i] + locB[i]; 46 | } 47 | } 48 | } 49 | 50 | [Benchmark] 51 | public void PureLoop() 52 | { 53 | var locA = a; 54 | var locB = b; 55 | var locC = c; 56 | for (int i = 0; i < Length; i++) 57 | { 58 | unchecked 59 | { 60 | locC[i] = locA[i] + locB[i]; 61 | } 62 | } 63 | } 64 | 65 | public readonly Func Add = (a, b) => unchecked(a + b); 66 | 67 | [Benchmark] 68 | [MethodImpl(MethodImplOptions.NoOptimization)] 69 | public void CallDelegateInLoopNoOptimization() 70 | { 71 | var locA = a; 72 | var locB = b; 73 | var locC = c; 74 | for (int i = 0; i < Length; i++) 75 | locC[i] = Add(locA[i], locB[i]); 76 | } 77 | 78 | [Benchmark] 79 | public void CallDelegateInLoop() 80 | { 81 | var locA = a; 82 | var locB = b; 83 | var locC = c; 84 | for (int i = 0; i < Length; i++) 85 | locC[i] = Add(locA[i], locB[i]); 86 | } 87 | 88 | public readonly Action FullLoop = (a, b, c, locLength) => 89 | { 90 | for (int i = 0; i < locLength; i++) 91 | { 92 | unchecked 93 | { 94 | c[i] = a[i] + b[i]; 95 | } 96 | } 97 | }; 98 | 99 | [Benchmark] 100 | [MethodImpl(MethodImplOptions.NoOptimization)] 101 | public void CallDelegateNoOptimization() 102 | { 103 | FullLoop(a, b, c, Length); 104 | } 105 | 106 | [Benchmark] 107 | public void CallDelegate() 108 | { 109 | FullLoop(a, b, c, Length); 110 | } 111 | } 112 | } 113 | 114 | 115 | /* 116 | 117 | | Method | Length | Mean | Error | StdDev | 118 | |--------------------------------- |------- |-------------:|-----------:|-----------:| 119 | | PureLoopNoOptimization | 100 | 308.77 ns | 5.890 ns | 5.785 ns | 120 | | PureLoop | 100 | 89.21 ns | 0.684 ns | 0.606 ns | 121 | | CallDelegateInLoopNoOptimization | 100 | 539.78 ns | 10.492 ns | 10.305 ns | 122 | | CallDelegateInLoop | 100 | 220.21 ns | 1.622 ns | 1.517 ns | 123 | | CallDelegateNoOptimization | 100 | 72.90 ns | 0.627 ns | 0.523 ns | 124 | | CallDelegate | 100 | 70.48 ns | 0.731 ns | 0.684 ns | 125 | | PureLoopNoOptimization | 1000 | 2,906.22 ns | 34.718 ns | 32.475 ns | 126 | | PureLoop | 1000 | 847.84 ns | 16.685 ns | 22.839 ns | 127 | | CallDelegateInLoopNoOptimization | 1000 | 4,670.09 ns | 43.089 ns | 38.197 ns | 128 | | CallDelegateInLoop | 1000 | 2,155.94 ns | 35.198 ns | 34.569 ns | 129 | | CallDelegateNoOptimization | 1000 | 629.08 ns | 11.863 ns | 9.906 ns | 130 | | CallDelegate | 1000 | 635.41 ns | 9.149 ns | 8.111 ns | 131 | | PureLoopNoOptimization | 10000 | 30,460.60 ns | 585.865 ns | 840.231 ns | 132 | | PureLoop | 10000 | 8,481.05 ns | 164.510 ns | 219.616 ns | 133 | | CallDelegateInLoopNoOptimization | 10000 | 46,725.67 ns | 436.313 ns | 386.780 ns | 134 | | CallDelegateInLoop | 10000 | 21,587.40 ns | 398.567 ns | 372.820 ns | 135 | | CallDelegateNoOptimization | 10000 | 6,199.72 ns | 108.509 ns | 96.190 ns | 136 | | CallDelegate | 10000 | 6,356.80 ns | 121.489 ns | 119.318 ns | 137 | 138 | */ -------------------------------------------------------------------------------- /Benchmarks/Arrays/Intrinsics.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System.Numerics; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.Intrinsics.X86; 5 | 6 | public class VectorVsVector256 7 | { 8 | public const int CAPACITY = 16_000_000; 9 | [Params(160, 1_600, 16_000, 160_000, 1_600_000, 16_000_000)] 10 | public int Length { get; set; } 11 | public int[] a; 12 | public int[] b; 13 | public int[] c; 14 | 15 | [GlobalSetup] 16 | public void Setup() 17 | { 18 | a = new int[CAPACITY]; 19 | b = new int[CAPACITY]; 20 | c = new int[CAPACITY]; 21 | for (int i = 0; i < CAPACITY; i++) 22 | { 23 | b[i] = i * 3; 24 | a[i] = i * 5; 25 | } 26 | } 27 | 28 | [Benchmark(Baseline = true)] 29 | public unsafe void NoVector() 30 | { 31 | unchecked 32 | { 33 | fixed (int* ap = a, bp = b, cp = c) 34 | { 35 | for (int i = 0; i < Length; i++) 36 | cp[i] = ap[i] * bp[i]; 37 | } 38 | } 39 | } 40 | 41 | [Benchmark] 42 | public unsafe void VectorT() 43 | { 44 | unchecked 45 | { 46 | fixed (int* ap = a, bp = b, cp = c) 47 | { 48 | for (int i = 0; i < Length; i += 8) 49 | { 50 | var block1 = *(Vector*)(ap + i); 51 | var block2 = *(Vector*)(bp + i); 52 | *(Vector*)(cp + i) = block1 * block2; 53 | } 54 | } 55 | } 56 | } 57 | 58 | [Benchmark] 59 | public unsafe void Vector256T() 60 | { 61 | unchecked 62 | { 63 | fixed (int* ap = a, bp = b, cp = c) 64 | { 65 | for (int i = 0; i < Length; i += 8) 66 | { 67 | var block1 = Avx2.LoadVector256(ap + i); 68 | var block2 = Avx2.LoadVector256(bp + i); 69 | var bl = Avx2.MultiplyLow(block1, block2); 70 | Avx2.Store(cp + i, bl); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | /* 78 | 79 | i7-7700HQ 80 | 81 | | Method | Length | Mean | Error | StdDev | Median | Ratio | RatioSD | 82 | |----------- |--------- |-----------------:|---------------:|---------------:|-----------------:|------:|--------:| 83 | | NoVector | 160 | 117.27 ns | 1.054 ns | 0.880 ns | 116.94 ns | 1.00 | 0.00 | 84 | | VectorT | 160 | 19.99 ns | 0.417 ns | 0.480 ns | 19.90 ns | 0.17 | 0.00 | 85 | | Vector256T | 160 | 19.50 ns | 0.361 ns | 0.338 ns | 19.43 ns | 0.17 | 0.00 | 86 | | | | | | | | | | 87 | | NoVector | 1600 | 1,070.47 ns | 9.314 ns | 7.272 ns | 1,072.03 ns | 1.00 | 0.00 | 88 | | VectorT | 1600 | 168.75 ns | 3.239 ns | 3.030 ns | 169.31 ns | 0.16 | 0.00 | 89 | | Vector256T | 1600 | 153.90 ns | 3.090 ns | 4.125 ns | 153.01 ns | 0.15 | 0.00 | 90 | | | | | | | | | | 91 | | NoVector | 16000 | 11,140.08 ns | 217.774 ns | 305.288 ns | 11,104.45 ns | 1.00 | 0.00 | 92 | | VectorT | 16000 | 2,601.62 ns | 33.129 ns | 29.368 ns | 2,601.89 ns | 0.23 | 0.01 | 93 | | Vector256T | 16000 | 2,606.40 ns | 30.515 ns | 27.051 ns | 2,604.79 ns | 0.23 | 0.01 | 94 | | | | | | | | | | 95 | | NoVector | 160000 | 112,583.93 ns | 2,024.219 ns | 1,794.417 ns | 112,123.37 ns | 1.00 | 0.00 | 96 | | VectorT | 160000 | 43,904.00 ns | 634.792 ns | 593.785 ns | 43,888.51 ns | 0.39 | 0.01 | 97 | | Vector256T | 160000 | 43,470.56 ns | 733.789 ns | 720.679 ns | 43,172.89 ns | 0.39 | 0.01 | 98 | | | | | | | | | | 99 | | NoVector | 1600000 | 1,395,028.32 ns | 19,602.973 ns | 18,336.633 ns | 1,399,623.44 ns | 1.00 | 0.00 | 100 | | VectorT | 1600000 | 1,039,098.82 ns | 30,624.182 ns | 90,296.093 ns | 1,000,621.19 ns | 0.85 | 0.05 | 101 | | Vector256T | 1600000 | 1,097,105.87 ns | 21,939.680 ns | 48,158.129 ns | 1,091,926.32 ns | 0.78 | 0.06 | 102 | | | | | | | | | | 103 | | NoVector | 16000000 | 14,293,466.20 ns | 278,722.532 ns | 309,799.356 ns | 14,160,007.81 ns | 1.00 | 0.00 | 104 | | VectorT | 16000000 | 11,581,997.24 ns | 132,511.740 ns | 110,653.305 ns | 11,581,384.38 ns | 0.81 | 0.02 | 105 | | Vector256T | 16000000 | 11,712,245.86 ns | 224,194.448 ns | 230,231.192 ns | 11,628,498.44 ns | 0.82 | 0.03 | 106 | 107 | */ -------------------------------------------------------------------------------- /Benchmarks/Arrays/ParallelFor.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Results of benchmark for i7-7700HQ are at the end of the file 3 | */ 4 | 5 | using System; 6 | using System.Numerics; 7 | using System.Runtime.CompilerServices; 8 | using System.Runtime.InteropServices; 9 | using System.Threading.Tasks; 10 | using BenchmarkDotNet.Attributes; 11 | using BenchmarkDotNet.Order; 12 | 13 | namespace Arrays 14 | { 15 | [SkewnessColumn, KurtosisColumn] 16 | [Orderer(SummaryOrderPolicy.FastestToSlowest)] 17 | public unsafe class ParallelFor 18 | { 19 | private int* arr1; 20 | private int* arr2; 21 | private int* arr3; 22 | private const int CAPACITY = 4_000_000; 23 | 24 | [Params(128, 128 * 8, 128 * 8 * 8, 128 * 8 * 8 * 8)] 25 | public int Size { get; set; } 26 | 27 | [GlobalSetup] 28 | public void Setup() 29 | { 30 | arr1 = (int*)Marshal.AllocHGlobal(CAPACITY * sizeof(int)); 31 | arr2 = (int*)Marshal.AllocHGlobal(CAPACITY * sizeof(int)); 32 | arr3 = (int*)Marshal.AllocHGlobal(CAPACITY * sizeof(int)); 33 | } 34 | 35 | [Benchmark(Baseline = true)] 36 | public void ParallelForNaive() 37 | { 38 | Parallel.For(0, Size, i => *(arr1 + i) = *(arr2 + i) + *(arr3 + i)); 39 | } 40 | 41 | private const int Threads = 16; 42 | 43 | [Benchmark] 44 | public void ParallelForBatch() 45 | { 46 | Parallel.For(0, Threads, 47 | id => { 48 | var lArr1 = arr1; 49 | var lArr2 = arr2; 50 | var lArr3 = arr3; 51 | var startId = Size / Threads * id; 52 | var finishId = Size / Threads * (id + 1); 53 | for (int i = startId; i < finishId; i++) 54 | *(lArr1 + i) = *(lArr2 + i) + *(lArr3 + i); 55 | }); 56 | } 57 | 58 | [Benchmark] 59 | public void ParallelForBatchSimd() 60 | { 61 | Parallel.For(0, Threads, 62 | id => { 63 | var lArr1 = arr1; 64 | var lArr2 = arr2; 65 | var lArr3 = arr3; 66 | var startId = Size / Threads * id; 67 | var finishId = Size / Threads * (id + 1); 68 | var c = Vector.Count; 69 | for (int i = startId; i < finishId; i += c) 70 | { 71 | var vec1 = *(Vector*)(lArr1 + i); 72 | var vec2 = *(Vector*)(lArr2 + i); 73 | *(Vector*)(lArr3 + i) = vec1 + vec2; 74 | } 75 | }); 76 | } 77 | } 78 | } 79 | 80 | /* 81 | 82 | | Method | Size | Mean | Error | StdDev | Skewness | Kurtosis | Ratio | RatioSD | 83 | |--------------------- |------ |----------:|----------:|----------:|---------:|---------:|------:|--------:| 84 | | ParallelForBatchSimd | 128 | 3.030 us | 0.0473 us | 0.0443 us | 0.3389 | 1.817 | 0.79 | 0.01 | 85 | | ParallelForBatch | 128 | 3.226 us | 0.0607 us | 0.0623 us | -0.5232 | 1.542 | 0.84 | 0.02 | 86 | | ParallelForNaive | 128 | 3.817 us | 0.0344 us | 0.0322 us | -0.4436 | 1.810 | 1.00 | 0.00 | 87 | | | | | | | | | | | 88 | | ParallelForBatchSimd | 1024 | 3.286 us | 0.0591 us | 0.0552 us | 0.8498 | 2.142 | 0.38 | 0.01 | 89 | | ParallelForBatch | 1024 | 3.968 us | 0.0546 us | 0.0511 us | 0.2136 | 2.525 | 0.46 | 0.01 | 90 | | ParallelForNaive | 1024 | 8.548 us | 0.0459 us | 0.0429 us | -0.5724 | 2.300 | 1.00 | 0.00 | 91 | | | | | | | | | | | 92 | | ParallelForBatchSimd | 8192 | 4.776 us | 0.0653 us | 0.0611 us | 0.0559 | 2.010 | 0.27 | 0.01 | 93 | | ParallelForBatch | 8192 | 6.402 us | 0.0230 us | 0.0216 us | 0.1045 | 2.139 | 0.37 | 0.01 | 94 | | ParallelForNaive | 8192 | 17.450 us | 0.2988 us | 0.3778 us | 0.1228 | 2.103 | 1.00 | 0.00 | 95 | | | | | | | | | | | 96 | | ParallelForBatchSimd | 65536 | 10.768 us | 0.0642 us | 0.0601 us | -0.5767 | 2.684 | 0.14 | 0.00 | 97 | | ParallelForBatch | 65536 | 20.390 us | 0.4062 us | 0.4171 us | -0.3309 | 1.512 | 0.26 | 0.01 | 98 | | ParallelForNaive | 65536 | 80.463 us | 1.5860 us | 2.7358 us | -0.0386 | 2.190 | 1.00 | 0.00 | 99 | 100 | */ -------------------------------------------------------------------------------- /Benchmarks/Arrays/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace Arrays 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | BenchmarkRunner.Run(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Benchmarks/Arrays/README.md: -------------------------------------------------------------------------------- 1 | # Arrays 2 | 3 | _TODO_ 4 | 5 | ``` ini 6 | 7 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 8 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 9 | .NET Core SDK=5.0.100-preview.6.20318.15 10 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 11 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 12 | 13 | 14 | ``` 15 | | Method | Length | Mean | Error | StdDev | 16 | |--------------------------------- |------- |-------------:|-----------:|-----------:| 17 | | **PureLoopNoOptimization** | **100** | **260.93 ns** | **2.487 ns** | **2.327 ns** | 18 | | PureLoop | 100 | 87.00 ns | 1.750 ns | 2.875 ns | 19 | | CallDelegateInLoopNoOptimization | 100 | 402.99 ns | 6.323 ns | 5.605 ns | 20 | | CallDelegateInLoop | 100 | 203.18 ns | 2.381 ns | 2.227 ns | 21 | | CallDelegateNoOptimization | 100 | 58.08 ns | 1.061 ns | 0.940 ns | 22 | | CallDelegate | 100 | 55.21 ns | 0.455 ns | 0.380 ns | 23 | | **PureLoopNoOptimization** | **1000** | **2,583.44 ns** | **7.935 ns** | **7.034 ns** | 24 | | PureLoop | 1000 | 779.35 ns | 7.917 ns | 6.611 ns | 25 | | CallDelegateInLoopNoOptimization | 1000 | 3,968.34 ns | 78.333 ns | 76.934 ns | 26 | | CallDelegateInLoop | 1000 | 2,020.55 ns | 10.171 ns | 8.493 ns | 27 | | CallDelegateNoOptimization | 1000 | 463.66 ns | 5.475 ns | 4.853 ns | 28 | | CallDelegate | 1000 | 457.76 ns | 1.083 ns | 1.013 ns | 29 | | **PureLoopNoOptimization** | **10000** | **25,320.75 ns** | **188.888 ns** | **176.686 ns** | 30 | | PureLoop | 10000 | 7,777.32 ns | 24.456 ns | 22.876 ns | 31 | | CallDelegateInLoopNoOptimization | 10000 | 38,489.62 ns | 109.517 ns | 102.442 ns | 32 | | CallDelegateInLoop | 10000 | 22,932.48 ns | 390.801 ns | 365.555 ns | 33 | | CallDelegateNoOptimization | 10000 | 4,520.21 ns | 17.053 ns | 15.117 ns | 34 | | CallDelegate | 10000 | 4,472.78 ns | 17.625 ns | 14.717 ns | 35 | -------------------------------------------------------------------------------- /Benchmarks/Boxing/Boxing/Boxing.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Benchmarks/Boxing/Boxing/EqualsBoxing.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Boxing 8 | { 9 | public class EqualsBoxing 10 | { 11 | private int[] permutedArray; 12 | private const int PERMUTED_ARRAY_LENGTH = 20; 13 | 14 | [GlobalSetup] 15 | public void Setup() 16 | { 17 | var r = new Random(); 18 | permutedArray = Enumerable.Range(0, PERMUTED_ARRAY_LENGTH).OrderBy(c => r.Next(0, 10000)).ToArray(); 19 | } 20 | 21 | private int id1 = 0; 22 | private int id2 = PERMUTED_ARRAY_LENGTH / 2; // they should differ 23 | 24 | [Benchmark] 25 | public void NoEqualsCalled() 26 | { 27 | bool decision = id1 == id2; 28 | id1 = (id1 + (decision ? 1 : 0)) % permutedArray.Length; 29 | id1 = permutedArray[id1]; 30 | id2 = permutedArray[id2]; 31 | } 32 | 33 | public bool CallEqualsWithBoxing(T a, T b) 34 | => a.Equals(b); 35 | 36 | [Benchmark] 37 | public void EqualsCalled() 38 | { 39 | bool decision = CallEqualsWithBoxing(id1, id2); 40 | id1 = (id1 + (decision ? 1 : 0)) % permutedArray.Length; 41 | id1 = permutedArray[id1]; 42 | id2 = permutedArray[id2]; 43 | } 44 | 45 | public bool CallEqualsWithBoxingForStructs(T a, T b) where T : struct 46 | => a.Equals(b); 47 | 48 | [Benchmark] 49 | public void EqualsCalledOnStructs() 50 | { 51 | bool decision = CallEqualsWithBoxingForStructs(id1, id2); 52 | id1 = (id1 + (decision ? 1 : 0)) % permutedArray.Length; 53 | id1 = permutedArray[id1]; 54 | id2 = permutedArray[id2]; 55 | } 56 | } 57 | } 58 | 59 | 60 | /* 61 | * 62 | * 63 | | Method | Mean | Error | StdDev | 64 | |---------------------- |---------:|---------:|---------:| 65 | | NoEqualsCalled | 10.45 ns | 0.165 ns | 0.154 ns | 66 | | EqualsCalled | 12.95 ns | 0.289 ns | 0.270 ns | 67 | | EqualsCalledOnStructs | 13.71 ns | 0.315 ns | 0.398 ns | 68 | 69 | Although it might be distorted a little bit as quite a significant chunk of time is taken by addressing fields of the array 70 | and addressing the array itself, while the equality comparison of two ints is cheap 71 | 72 | 73 | */ -------------------------------------------------------------------------------- /Benchmarks/Boxing/Boxing/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | using System; 3 | 4 | namespace Boxing 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | BenchmarkRunner.Run(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Benchmarks/BranchingTechniques/BranchingTechniques.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | BranchingTechniques.tt 15 | True 16 | True 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | BranchingTechniques.cs 27 | TextTemplatingFileGenerator 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | True 38 | True 39 | BranchingTechniques.tt 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Benchmarks/BranchingTechniques/BranchingTechniques.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Collections.Generic" #> 6 | <#@ output extension=".cs" #> 7 | //------------------------------------------------------------------------------ 8 | // 9 | // This code was generated from the "BranchingTechniques.tt" T4 Text Template. 10 | // 11 | //------------------------------------------------------------------------------ 12 | <# int[] sizes = { 1, 2, 3, 4, 5, 10, 100, 1000 }; #> 13 | 14 | using BenchmarkDotNet.Attributes; 15 | 16 | namespace BranchingTechniques 17 | { 18 | public class BranchingTechniquesBenchmarks 19 | { 20 | int temp = default; 21 | <# foreach (int i in sizes) { #> 22 | 23 | [Benchmark] 24 | public void switchStatement<#= i #>() 25 | { 26 | for (int i = 0; i < <#= i #>; i++) 27 | { 28 | switch (i) 29 | { 30 | <# for (int j = 0; j < i; j++) { #> 31 | case <#= j #>: temp = i; break; 32 | <# } #> 33 | } 34 | } 35 | } 36 | 37 | [Benchmark] 38 | public void switchExpression<#= i #>() 39 | { 40 | for (int i = 0; i < <#= i #>; i++) 41 | { 42 | temp = i switch 43 | { 44 | <# for (int j = 0; j < i; j++) { #> 45 | <#= j #> => i, 46 | <# } #> 47 | }; 48 | } 49 | } 50 | 51 | [Benchmark] 52 | public void if<#= i #>() 53 | { 54 | for (int i = 0; i < <#= i #>; i++) 55 | { 56 | <# for (int j = 0; j < i; j++) { #> 57 | <#= j != 0 ? "else " : "" #>if (i == <#= j #>) temp = i; 58 | <# } #> 59 | } 60 | } 61 | 62 | [Benchmark] 63 | public void conditional<#= i #>() 64 | { 65 | <# if (i <= 100) { #> 66 | for (int i = 0; i < <#= i #>; i++) 67 | { 68 | temp = 69 | <# for (int j = 0; j < i; j++) { #> 70 | i == <#= j #> ? i : 71 | <# } #> 72 | default; 73 | } 74 | <# } #> 75 | <# else { #> 76 | throw new System.Exception("too large; throws exception on build"); 77 | <# } #> 78 | } 79 | <# } #> 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Benchmarks/BranchingTechniques/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace BranchingTechniques 4 | { 5 | class Program 6 | { 7 | static void Main() 8 | { 9 | BenchmarkRunner.Run(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Benchmarks/BranchingTechniques/README.md: -------------------------------------------------------------------------------- 1 | # Branching Techniques 2 | 3 | This benchmark compares various types of branching that is possible inside of C#: switch statements, 4 | switch expressions, if statements, and conditional expressions. 5 | 6 | _Note: large conditional expressions cause a `StackOverflowException` in Roslyn and cannot be built._ 7 | 8 | ``` ini 9 | 10 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 11 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 12 | .NET Core SDK=5.0.100-preview.8.20417.9 13 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.40711, CoreFX 5.0.20.40711), X64 RyuJIT 14 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.40711, CoreFX 5.0.20.40711), X64 RyuJIT 15 | 16 | 17 | ``` 18 | | Method | Mean | Error | StdDev | Median | 19 | |--------------------- |----------------:|--------------:|--------------:|----------------:| 20 | | switchStatement1 | 0.0014 ns | 0.0029 ns | 0.0027 ns | 0.0000 ns | 21 | | switchExpression1 | 0.2407 ns | 0.0097 ns | 0.0090 ns | 0.2412 ns | 22 | | if1 | 0.2357 ns | 0.0076 ns | 0.0071 ns | 0.2352 ns | 23 | | conditional1 | 0.5756 ns | 0.0365 ns | 0.0448 ns | 0.5699 ns | 24 | | switchStatement2 | 1.4590 ns | 0.0133 ns | 0.0124 ns | 1.4559 ns | 25 | | switchExpression2 | 1.5196 ns | 0.0555 ns | 0.0814 ns | 1.5300 ns | 26 | | if2 | 1.3594 ns | 0.0532 ns | 0.0523 ns | 1.3353 ns | 27 | | conditional2 | 1.1767 ns | 0.0333 ns | 0.0311 ns | 1.1730 ns | 28 | | switchStatement3 | 4.1114 ns | 0.0771 ns | 0.0602 ns | 4.1107 ns | 29 | | switchExpression3 | 4.3437 ns | 0.1110 ns | 0.1520 ns | 4.3152 ns | 30 | | if3 | 3.8700 ns | 0.0404 ns | 0.0358 ns | 3.8752 ns | 31 | | conditional3 | 3.5161 ns | 0.0676 ns | 0.0632 ns | 3.5530 ns | 32 | | switchStatement4 | 5.6173 ns | 0.0763 ns | 0.1470 ns | 5.6022 ns | 33 | | switchExpression4 | 6.1340 ns | 0.1434 ns | 0.1271 ns | 6.0750 ns | 34 | | if4 | 6.2361 ns | 0.1501 ns | 0.2338 ns | 6.2479 ns | 35 | | conditional4 | 5.5457 ns | 0.0675 ns | 0.0599 ns | 5.5537 ns | 36 | | switchStatement5 | 7.0590 ns | 0.1593 ns | 0.1897 ns | 7.0144 ns | 37 | | switchExpression5 | 6.9517 ns | 0.1623 ns | 0.2756 ns | 6.8837 ns | 38 | | if5 | 8.8664 ns | 0.0877 ns | 0.0820 ns | 8.8887 ns | 39 | | conditional5 | 7.8437 ns | 0.1637 ns | 0.1608 ns | 7.8131 ns | 40 | | switchStatement10 | 15.2778 ns | 0.3325 ns | 0.3696 ns | 15.2038 ns | 41 | | switchExpression10 | 17.0714 ns | 0.3612 ns | 0.5730 ns | 16.9284 ns | 42 | | if10 | 28.8714 ns | 0.4889 ns | 0.5231 ns | 28.7261 ns | 43 | | conditional10 | 16.4725 ns | 0.1321 ns | 0.1235 ns | 16.4475 ns | 44 | | switchStatement100 | 143.5896 ns | 1.1010 ns | 0.9760 ns | 143.2851 ns | 45 | | switchExpression100 | 157.8319 ns | 2.9648 ns | 2.3147 ns | 157.1144 ns | 46 | | if100 | 1,788.5813 ns | 35.2708 ns | 39.2034 ns | 1,787.3701 ns | 47 | | conditional100 | 701.0713 ns | 3.3936 ns | 3.1744 ns | 699.9504 ns | 48 | | switchStatement1000 | 6,243.7404 ns | 126.9243 ns | 366.2056 ns | 6,067.1780 ns | 49 | | switchExpression1000 | 4,642.0040 ns | 90.2316 ns | 96.5468 ns | 4,622.6555 ns | 50 | | if1000 | 270,967.1061 ns | 2,022.8634 ns | 1,579.3187 ns | 271,108.5205 ns | 51 | | conditional1000 | NA | NA | NA | NA | 52 | 53 | Benchmarks with issues: 54 | BranchingTechniquesBenchmarks.conditional1000: DefaultJob 55 | -------------------------------------------------------------------------------- /Benchmarks/ExceptionHandlingTechniques/ExceptionHandlingBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace ExceptionHandlingTechniques 6 | { 7 | public class ExceptionHandlingBenchmarks 8 | { 9 | int temp; 10 | 11 | [Benchmark] 12 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 13 | public void TryMethodPattern() 14 | { 15 | if (!TryDo(2, out temp)) 16 | { 17 | temp = -1; 18 | } 19 | } 20 | public static bool TryDo(int value, out int result) 21 | { 22 | if (value % 2 == 0) 23 | { 24 | result = default; 25 | return false; 26 | } 27 | else 28 | { 29 | result = value; 30 | return true; 31 | } 32 | } 33 | 34 | [Benchmark] 35 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 36 | public void TryCatch() 37 | { 38 | try 39 | { 40 | temp = Do(2); 41 | } 42 | catch 43 | { 44 | temp = -1; 45 | } 46 | } 47 | public static int Do(int value) 48 | { 49 | if (value % 2 == 0) 50 | { 51 | throw new Exception(); 52 | } 53 | else 54 | { 55 | return value; 56 | } 57 | } 58 | 59 | [Benchmark] 60 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 61 | public void Int32_TryParse() 62 | { 63 | if (!int.TryParse(" ", out temp)) 64 | { 65 | temp = -1; 66 | } 67 | } 68 | 69 | [Benchmark] 70 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 71 | public void Int32_Parse_TryCatch() 72 | { 73 | try 74 | { 75 | temp = int.Parse(" "); 76 | } 77 | catch 78 | { 79 | temp = -1; 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Benchmarks/ExceptionHandlingTechniques/ExceptionHandlingTechniques.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Benchmarks/ExceptionHandlingTechniques/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace ExceptionHandlingTechniques 4 | { 5 | class Program 6 | { 7 | static void Main() 8 | { 9 | BenchmarkRunner.Run(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Benchmarks/ExceptionHandlingTechniques/README.md: -------------------------------------------------------------------------------- 1 | # Exception Handling Techniques 2 | 3 | This benchmark compares the performance of `try`+`catch` blocks vs versus try method patterns such as `int.TryParse`. 4 | 5 | ``` ini 6 | 7 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 8 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 9 | .NET Core SDK=5.0.100-preview.8.20417.9 10 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.40711, CoreFX 5.0.20.40711), X64 RyuJIT 11 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.40711, CoreFX 5.0.20.40711), X64 RyuJIT 12 | 13 | 14 | ``` 15 | | Method | Mean | Error | StdDev | 16 | |--------------------- |--------------:|-----------:|-----------:| 17 | | TryMethodPattern | 1.528 ns | 0.0459 ns | 0.0406 ns | 18 | | TryCatch | 16,020.844 ns | 78.5419 ns | 65.5861 ns | 19 | | Int32_TryParse | 11.376 ns | 0.1443 ns | 0.1349 ns | 20 | | Int32_Parse_TryCatch | 19,084.244 ns | 81.3079 ns | 67.8958 ns | 21 | -------------------------------------------------------------------------------- /Benchmarks/IndexedCollectionAbstractions/IndexedCollectionAbstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Benchmarks/IndexedCollectionAbstractions/IndexedCollectionBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace IndexedCollectionAbstractions 6 | { 7 | public class IndexedCollectionBenchmarks 8 | { 9 | [Params(10, 100, 1000)] 10 | public int Size; 11 | 12 | int[] array; 13 | List list; 14 | IList ilist_array; 15 | IList ilist_list; 16 | 17 | [GlobalSetup] 18 | public void Setup() 19 | { 20 | array = Enumerable.Range(0, Size).ToArray(); 21 | list = new List(array); 22 | ilist_array = array; 23 | ilist_list = list; 24 | } 25 | 26 | [Benchmark] 27 | public void Array() 28 | { 29 | for (int i = 0; i < Size; i++) 30 | { 31 | _ = array[i]; 32 | } 33 | } 34 | 35 | [Benchmark] 36 | public void List() 37 | { 38 | for (int i = 0; i < Size; i++) 39 | { 40 | _ = list[i]; 41 | } 42 | } 43 | 44 | [Benchmark] 45 | public void IListArray() 46 | { 47 | for (int i = 0; i < Size; i++) 48 | { 49 | _ = ilist_array[i]; 50 | } 51 | } 52 | 53 | [Benchmark] 54 | public void IListList() 55 | { 56 | for (int i = 0; i < Size; i++) 57 | { 58 | _ = ilist_list[i]; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Benchmarks/IndexedCollectionAbstractions/ParamsIndexerVsOverloads.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace IndexedCollectionAbstractions 9 | { 10 | public class LazyNDArray 11 | { 12 | public int this[params int[] ids] 13 | { 14 | get 15 | { 16 | var sum = 0; 17 | var length = ids.Length; // caching to guarantee no reuse of potentially expensive Length 18 | for (int i = 0; i < length; i++) 19 | sum += ids[i]; 20 | return sum; 21 | } 22 | } 23 | } 24 | 25 | public class OverloadedNDArray 26 | { 27 | public int this[int a] => a; 28 | public int this[int a, params int[] other] 29 | { 30 | get 31 | { 32 | var sum = a; 33 | var length = other.Length; // caching to guarantee no reuse of potentially expensive Length 34 | for (int i = 0; i < length; i++) 35 | sum += other[i]; 36 | return sum; 37 | } 38 | } 39 | } 40 | 41 | public class ParamsIndexerVsOverloads 42 | { 43 | private LazyNDArray lazy; 44 | private OverloadedNDArray overloaded; 45 | private const int ITERATIONS = 100; 46 | 47 | // To avoid any attempts to cache the result 48 | private int currId; 49 | public int GetCurrent() 50 | => currId++; 51 | 52 | [GlobalSetup] 53 | public void Setup() 54 | { 55 | lazy = new(); 56 | overloaded = new(); 57 | } 58 | 59 | [Benchmark] 60 | public void HowMuchGetCurrentTakes() 61 | { 62 | for (int i = 0; i < ITERATIONS; i++) 63 | GetCurrent(); 64 | } 65 | 66 | [Benchmark] 67 | public void BenchLazy1D() 68 | { 69 | for (int i = 0; i < ITERATIONS; i++) // to avoid nano-benchmark problems 70 | { 71 | _ = lazy[GetCurrent()]; 72 | } 73 | } 74 | 75 | [Benchmark] 76 | public void BenchLazy2D() 77 | { 78 | for (int i = 0; i < ITERATIONS; i++) // to avoid nano-benchmark problems 79 | { 80 | _ = lazy[GetCurrent(), 4]; 81 | } 82 | } 83 | 84 | [Benchmark] 85 | public void BenchOverloaded1D() 86 | { 87 | for (int i = 0; i < ITERATIONS; i++) // to avoid nano-benchmark problems 88 | { 89 | _ = overloaded[GetCurrent()]; 90 | } 91 | } 92 | 93 | [Benchmark] 94 | public void BenchOverloaded2D() 95 | { 96 | for (int i = 0; i < ITERATIONS; i++) // to avoid nano-benchmark problems 97 | { 98 | _ = overloaded[GetCurrent(), 4]; 99 | } 100 | } 101 | } 102 | } 103 | 104 | /* 105 | 106 | i7 7700 HQ 107 | 108 | (ITERATIONS iterations for each method to avoid micro-benchmark issues) 109 | 110 | | Method | Mean | Error | StdDev | 111 | |----------------------- |-----------:|---------:|---------:| 112 | | HowMuchGetCurrentTakes | 167.1 ns | 2.16 ns | 1.80 ns | 113 | | BenchLazy1D | 656.5 ns | 13.14 ns | 20.83 ns | 114 | | BenchLazy2D | 752.2 ns | 15.04 ns | 26.33 ns | 115 | | BenchOverloaded1D | 172.2 ns | 2.24 ns | 2.10 ns | 116 | | BenchOverloaded2D | 1,250.3 ns | 23.72 ns | 21.03 ns | 117 | 118 | */ -------------------------------------------------------------------------------- /Benchmarks/IndexedCollectionAbstractions/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace IndexedCollectionAbstractions 4 | { 5 | public class Program 6 | { 7 | public static void Main() 8 | { 9 | //BenchmarkRunner.Run(); 10 | BenchmarkRunner.Run(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Benchmarks/IndexedCollectionAbstractions/README.md: -------------------------------------------------------------------------------- 1 | # Indexed Collection Abstractions 2 | 3 | The `IList` type in C# allows you to abstract indexed types like `List` and `T[]` so that you can code against one type, 4 | `IList`, and support multiple types. However there is a cost to this abstraction. This benchmark is to help you determine if you 5 | should rely on `IList` or if you should include explicit versions for `T[]` as well. 6 | 7 | ``` ini 8 | 9 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 10 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 11 | .NET Core SDK=5.0.100-preview.6.20318.15 12 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 13 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 14 | 15 | 16 | ``` 17 | | Method | Size | Mean | Error | StdDev | 18 | |----------- |----- |-------------:|-----------:|-----------:| 19 | | **Array** | **10** | **3.003 ns** | **0.0298 ns** | **0.0264 ns** | 20 | | List | 10 | 5.077 ns | 0.1159 ns | 0.0968 ns | 21 | | IListArray | 10 | 25.452 ns | 0.4401 ns | 0.4117 ns | 22 | | IListList | 10 | 20.818 ns | 0.4045 ns | 0.3784 ns | 23 | | **Array** | **100** | **36.439 ns** | **0.3145 ns** | **0.2788 ns** | 24 | | List | 100 | 49.675 ns | 0.4670 ns | 0.4140 ns | 25 | | IListArray | 100 | 260.702 ns | 4.3079 ns | 4.0296 ns | 26 | | IListList | 100 | 207.413 ns | 2.0283 ns | 1.8973 ns | 27 | | **Array** | **1000** | **261.166 ns** | **2.5422 ns** | **2.2536 ns** | 28 | | List | 1000 | 415.206 ns | 3.1724 ns | 2.6491 ns | 29 | | IListArray | 1000 | 2,534.601 ns | 26.5846 ns | 23.5665 ns | 30 | | IListList | 1000 | 2,003.540 ns | 14.7835 ns | 13.8285 ns | 31 | -------------------------------------------------------------------------------- /Benchmarks/IterationRefParametersVsReturns/IterationRefParametersVsReturns.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Benchmarks/IterationRefParametersVsReturns/IterationRefParametersVsReturnsBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace IterationRefParametersVsReturns 7 | { 8 | public unsafe class IterationRefParametersVsReturnsBenchmarks 9 | { 10 | [Params(10, 100, 1000, 10000, 100000)] 11 | public int N; 12 | 13 | int[] array; 14 | 15 | [IterationSetup] 16 | public void Setup() 17 | { 18 | array = new int[N]; 19 | for (int i = 0; i < N; i++) 20 | { 21 | array[i] = i; 22 | } 23 | } 24 | 25 | [Benchmark] 26 | public void Control() 27 | { 28 | for (int i = 0; i < N; i++) 29 | { 30 | array[i] += 1; 31 | } 32 | } 33 | 34 | [Benchmark] 35 | public void DelegateReturn() => Update(array, x => x + 1); 36 | 37 | public void Update(T[] array, Func updater) 38 | { 39 | for (int i = 0; i < N; i++) 40 | { 41 | array[i] = updater(array[i]); 42 | } 43 | } 44 | 45 | [Benchmark] 46 | public void DelegateRefParam() => Update(array, (ref int x) => x += 1); 47 | 48 | public delegate void Updater(ref T value); 49 | 50 | public void Update(T[] array, Updater updater) 51 | { 52 | for (int i = 0; i < N; i++) 53 | { 54 | updater(ref array[i]); 55 | } 56 | } 57 | 58 | [Benchmark] 59 | public void StructGenericReturn() => UpdateReturn(array); 60 | 61 | /// 62 | public interface IFunc 63 | { 64 | /// 65 | TResult Invoke(T1 arg1); 66 | } 67 | 68 | struct Int32Increment : IFunc { public int Invoke(int a) => a + 1; } 69 | 70 | public void UpdateReturn(T[] array, Updater updater = default) 71 | where Updater : struct, IFunc 72 | { 73 | for (int i = 0; i < N; i++) 74 | { 75 | array[i] = updater.Invoke(array[i]); 76 | } 77 | } 78 | 79 | [Benchmark] 80 | public void StructGenericRefParam() => UpdateRefParam(array); 81 | 82 | public interface IUpdater 83 | { 84 | void Invoke(ref T1 arg1); 85 | } 86 | 87 | struct Int32RefIncrement : IUpdater { public void Invoke(ref int a) => a += 1; } 88 | 89 | public void UpdateRefParam(T[] array, Updater updater = default) 90 | where Updater : struct, IUpdater 91 | { 92 | for (int i = 0; i < N; i++) 93 | { 94 | updater.Invoke(ref array[i]); 95 | } 96 | } 97 | 98 | [Benchmark] 99 | public void FunctionPointer() => UpdateWithPointer(array, &IncrementInt32); 100 | static int IncrementInt32(int x) => x + 1; 101 | public void UpdateWithPointer(T[] array, delegate* updater) 102 | { 103 | for (int i = 0; i < N; i++) 104 | { 105 | array[i] = updater(array[i]); 106 | } 107 | } 108 | 109 | [Benchmark] 110 | public void FunctionPointerRefParam() => UpdateWithPointerRef(array, &IncrementInt32); 111 | static void IncrementInt32(ref int x) => x += 1; 112 | public void UpdateWithPointerRef(T[] array, delegate* updater) 113 | { 114 | for (int i = 0; i < N; i++) 115 | { 116 | updater(ref array[i]); 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Benchmarks/IterationRefParametersVsReturns/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | using System; 3 | 4 | namespace IterationRefParametersVsReturns 5 | { 6 | class Program 7 | { 8 | static void Main() 9 | { 10 | BenchmarkRunner.Run(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Benchmarks/IterationRefParametersVsReturns/README.md: -------------------------------------------------------------------------------- 1 | # Iteration Ref Parameters Vs Returns 2 | 3 | `IEnumerable` does not allow for modification during iteration. But if you want to support 4 | that functionality on custom generic data structures, is it faster to modify and return the 5 | value (example: using `Func`), or is it faster to use `ref` parameters? That is the goal 6 | of this benchmark. 7 | 8 | Conclusion from results below: 9 | Using `ref` parameters are simply less efficient for passing `int`s. 10 | 11 | ``` ini 12 | 13 | BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19041.985 (2004/May2020Update/20H1) 14 | Intel Core i5-8400 CPU 2.80GHz (Coffee Lake), 1 CPU, 6 logical and 6 physical cores 15 | .NET SDK=5.0.301 16 | [Host] : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT 17 | Job-YFVYXE : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT 18 | 19 | InvocationCount=1 UnrollFactor=1 20 | 21 | ``` 22 | | Method | N | Mean | Error | StdDev | Median | 23 | |------------------------ |------- |--------------:|-------------:|-------------:|-------------:| 24 | | **Control** | **10** | **75.26 ns** | **17.959 ns** | **52.104 ns** | **100.0 ns** | 25 | | DelegateReturn | 10 | 188.10 ns | 12.127 ns | 32.579 ns | 200.0 ns | 26 | | DelegateRefParam | 10 | 242.05 ns | 21.827 ns | 60.118 ns | 200.0 ns | 27 | | StructGenericReturn | 10 | 84.62 ns | 14.068 ns | 36.314 ns | 100.0 ns | 28 | | StructGenericRefParam | 10 | 100.00 ns | 0.000 ns | 0.000 ns | 100.0 ns | 29 | | FunctionPointer | 10 | 100.00 ns | 0.000 ns | 0.000 ns | 100.0 ns | 30 | | FunctionPointerRefParam | 10 | 137.37 ns | 17.281 ns | 50.681 ns | 100.0 ns | 31 | | **Control** | **100** | **142.86 ns** | **20.296 ns** | **59.204 ns** | **100.0 ns** | 32 | | DelegateReturn | 100 | 373.33 ns | 15.953 ns | 44.469 ns | 400.0 ns | 33 | | DelegateRefParam | 100 | 396.77 ns | 11.761 ns | 17.961 ns | 400.0 ns | 34 | | StructGenericReturn | 100 | 139.58 ns | 17.765 ns | 51.256 ns | 100.0 ns | 35 | | StructGenericRefParam | 100 | 173.00 ns | 18.579 ns | 54.781 ns | 200.0 ns | 36 | | FunctionPointer | 100 | 296.00 ns | 9.799 ns | 19.795 ns | 300.0 ns | 37 | | FunctionPointerRefParam | 100 | 292.21 ns | 10.525 ns | 26.981 ns | 300.0 ns | 38 | | **Control** | **1000** | **714.61 ns** | **25.835 ns** | **71.590 ns** | **700.0 ns** | 39 | | DelegateReturn | 1000 | 2,111.76 ns | 45.134 ns | 72.883 ns | 2,100.0 ns | 40 | | DelegateRefParam | 1000 | 2,151.72 ns | 39.194 ns | 57.450 ns | 2,100.0 ns | 41 | | StructGenericReturn | 1000 | 630.00 ns | 18.197 ns | 50.725 ns | 600.0 ns | 42 | | StructGenericRefParam | 1000 | 670.73 ns | 18.252 ns | 48.401 ns | 700.0 ns | 43 | | FunctionPointer | 1000 | 1,968.42 ns | 42.966 ns | 47.757 ns | 2,000.0 ns | 44 | | FunctionPointerRefParam | 1000 | 1,992.31 ns | 36.682 ns | 94.689 ns | 1,950.0 ns | 45 | | **Control** | **10000** | **6,061.54 ns** | **60.640 ns** | **50.637 ns** | **6,100.0 ns** | 46 | | DelegateReturn | 10000 | 19,041.67 ns | 85.632 ns | 66.856 ns | 19,000.0 ns | 47 | | DelegateRefParam | 10000 | 19,642.86 ns | 186.226 ns | 165.084 ns | 19,600.0 ns | 48 | | StructGenericReturn | 10000 | 5,473.91 ns | 109.817 ns | 138.883 ns | 5,400.0 ns | 49 | | StructGenericRefParam | 10000 | 6,507.69 ns | 103.266 ns | 86.232 ns | 6,500.0 ns | 50 | | FunctionPointer | 10000 | 18,769.23 ns | 57.528 ns | 48.038 ns | 18,800.0 ns | 51 | | FunctionPointerRefParam | 10000 | 18,830.77 ns | 157.547 ns | 131.559 ns | 18,800.0 ns | 52 | | **Control** | **100000** | **60,658.82 ns** | **850.069 ns** | **1,372.703 ns** | **60,150.0 ns** | 53 | | DelegateReturn | 100000 | 191,573.33 ns | 3,422.537 ns | 3,201.443 ns | 191,000.0 ns | 54 | | DelegateRefParam | 100000 | 195,771.43 ns | 2,473.219 ns | 2,192.445 ns | 194,500.0 ns | 55 | | StructGenericReturn | 100000 | 53,325.00 ns | 1,007.244 ns | 786.390 ns | 53,000.0 ns | 56 | | StructGenericRefParam | 100000 | 63,509.52 ns | 1,174.654 ns | 2,147.921 ns | 63,500.0 ns | 57 | | FunctionPointer | 100000 | 186,592.31 ns | 1,296.685 ns | 1,082.791 ns | 186,100.0 ns | 58 | | FunctionPointerRefParam | 100000 | 187,740.00 ns | 1,850.697 ns | 1,731.143 ns | 186,900.0 ns | 59 | -------------------------------------------------------------------------------- /Benchmarks/MethodAbstractions/AdditionBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | using System.Linq.Expressions; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace MethodAbstractions 7 | { 8 | public class AdditionBenchmarks 9 | { 10 | int temp; 11 | int _1; 12 | int _2; 13 | int _3; 14 | int _4; 15 | int _5; 16 | 17 | [GlobalSetup] 18 | public void Setup() 19 | { 20 | _1 = 1; 21 | _2 = 2; 22 | _3 = 3; 23 | _4 = 4; 24 | _5 = 5; 25 | } 26 | 27 | [Benchmark] 28 | public void IntegerAdd() 29 | { 30 | temp = _1 + _1; 31 | temp = _2 + _2; 32 | temp = _3 + _3; 33 | temp = _4 + _4; 34 | temp = _5 + _5; 35 | } 36 | 37 | [Benchmark] 38 | public void IntegerAddStructWithAggressiveInlining() 39 | { 40 | temp = DoWithAggressiveInlining(_1, _1); 41 | temp = DoWithAggressiveInlining(_2, _2); 42 | temp = DoWithAggressiveInlining(_3, _3); 43 | temp = DoWithAggressiveInlining(_4, _4); 44 | temp = DoWithAggressiveInlining(_5, _5); 45 | } 46 | 47 | [Benchmark] 48 | public void IntegerAddLinqWithAggressiveInlining() 49 | { 50 | temp = AdditionWithAggressiveInlining(_1, _1); 51 | temp = AdditionWithAggressiveInlining(_2, _2); 52 | temp = AdditionWithAggressiveInlining(_3, _3); 53 | temp = AdditionWithAggressiveInlining(_4, _4); 54 | temp = AdditionWithAggressiveInlining(_5, _5); 55 | } 56 | 57 | [Benchmark] 58 | public void IntegerAddStruct() 59 | { 60 | temp = Do(_1, _1); 61 | temp = Do(_2, _2); 62 | temp = Do(_3, _3); 63 | temp = Do(_4, _4); 64 | temp = Do(_5, _5); 65 | } 66 | 67 | [Benchmark] 68 | public void IntegerAddLinq() 69 | { 70 | temp = Addition(_1, _1); 71 | temp = Addition(_2, _2); 72 | temp = Addition(_3, _3); 73 | temp = Addition(_4, _4); 74 | temp = Addition(_5, _5); 75 | } 76 | 77 | public struct g__AdditionWithAggressiveInlining_1 : IFunction 78 | { 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | public int Do(int a, int b) => a + b; 81 | } 82 | 83 | public struct g__Addition_1 : IFunction 84 | { 85 | public int Do(int a, int b) => a + b; 86 | } 87 | 88 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 89 | public static T DoWithAggressiveInlining(T a, T b) 90 | where Addition : struct, IFunction 91 | { 92 | Addition addition = default; 93 | return addition.Do(a, b); 94 | } 95 | 96 | public static T Do(T a, T b) 97 | where Addition : struct, IFunction 98 | { 99 | Addition addition = default; 100 | return addition.Do(a, b); 101 | } 102 | 103 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 104 | public static T AdditionWithAggressiveInlining(T a, T b) 105 | { 106 | return AdditionImplementation.Function(a, b); 107 | } 108 | 109 | public static T Addition(T a, T b) 110 | { 111 | return AdditionImplementation.Function(a, b); 112 | } 113 | 114 | internal static class AdditionImplementation 115 | { 116 | internal static Func Function = (T a, T b) => 117 | { 118 | ParameterExpression A = Expression.Parameter(typeof(T)); 119 | ParameterExpression B = Expression.Parameter(typeof(T)); 120 | Expression BODY = Expression.Add(A, B); 121 | Function = Expression.Lambda>(BODY, A, B).Compile(); 122 | return Function(a, b); 123 | }; 124 | } 125 | } 126 | 127 | public interface IFunction { C Do(A a, B b); } 128 | } 129 | -------------------------------------------------------------------------------- /Benchmarks/MethodAbstractions/EnumerableBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using BenchmarkDotNet.Attributes; 6 | 7 | namespace MethodAbstractions 8 | { 9 | public class EnumerableBenchmarks 10 | { 11 | private static int[] data; 12 | private static int sum; 13 | 14 | [GlobalSetup] 15 | public void Setup() 16 | { 17 | const int N = 10; 18 | int[] dataArray = new int[N]; 19 | for (int i = 0; i < N; i++) 20 | { 21 | dataArray[i] = i; 22 | } 23 | data = dataArray; 24 | } 25 | 26 | [Benchmark] 27 | public void Control() 28 | { 29 | sum = 0; 30 | foreach (int i in data) 31 | { 32 | if (i % 2 == 0) 33 | { 34 | sum += i; 35 | } 36 | } 37 | } 38 | 39 | [Benchmark] 40 | public void LinqWhereLambda() 41 | { 42 | sum = 0; 43 | foreach (int i in data.Where(i => i % 2 == 0)) 44 | { 45 | sum += i; 46 | } 47 | } 48 | 49 | [Benchmark] 50 | public void LinqWhere() 51 | { 52 | sum = 0; 53 | foreach (int i in data.Where(IsEven)) 54 | { 55 | sum += i; 56 | } 57 | } 58 | 59 | [Benchmark] 60 | public void CustomWhereLambda() 61 | { 62 | sum = 0; 63 | foreach (int i in data.CustomWhere(i => i % 2 == 0)) 64 | { 65 | sum += i; 66 | } 67 | } 68 | 69 | [Benchmark] 70 | public void CustomWhere() 71 | { 72 | sum = 0; 73 | foreach (int i in data.CustomWhere(IsEven)) 74 | { 75 | sum += i; 76 | } 77 | } 78 | 79 | [Benchmark] 80 | public void GenericStructWhere() 81 | { 82 | sum = 0; 83 | foreach (int i in data.Where()) 84 | { 85 | sum += i; 86 | } 87 | } 88 | 89 | [Benchmark] 90 | public void GenericStructWhereAction() 91 | { 92 | sum = 0; 93 | data.Where(); 94 | } 95 | 96 | [Benchmark] 97 | public void CustomWhereWithAggressiveInlining() 98 | { 99 | sum = 0; 100 | foreach (int i in data.CustomWhere(IsEvenWithAggressiveInlining)) 101 | { 102 | sum += i; 103 | } 104 | } 105 | 106 | [Benchmark] 107 | public void GenericStructWhereWithAggressiveInlining() 108 | { 109 | sum = 0; 110 | foreach (int i in data.Where()) 111 | { 112 | sum += i; 113 | } 114 | } 115 | 116 | [Benchmark] 117 | public void GenericStructWhereActionWithAggressiveInlining() 118 | { 119 | sum = 0; 120 | data.Where(); 121 | } 122 | 123 | public struct g__ActionWithAggressiveInlining_1 : IAction 124 | { 125 | [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] 126 | public void Do(int a) => sum += a; 127 | } 128 | 129 | public struct g__IsEvenWithAggressiveInlining_1 : IFunction 130 | { 131 | [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] 132 | public bool Do(int a) => IsEvenWithAggressiveInlining(a); 133 | } 134 | 135 | public struct g__Action_1 : IAction 136 | { 137 | public void Do(int a) => sum += a; 138 | } 139 | 140 | public struct g__IsEven_1 : IFunction 141 | { 142 | public bool Do(int a) => IsEven(a); 143 | } 144 | 145 | [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] 146 | public static bool IsEvenWithAggressiveInlining(int i) => i % 2 == 0; 147 | 148 | public static bool IsEven(int i) => i % 2 == 0; 149 | 150 | } 151 | 152 | #region Interface Types That Would Be Added To .NET 153 | 154 | public interface IAction { void Do(A a); } 155 | public interface IFunction { B Do(A a); } 156 | 157 | #endregion 158 | 159 | public static class Extensions 160 | { 161 | public static IEnumerable CustomWhere(this T[] ienumerable, Predicate where) 162 | { 163 | foreach (T value in ienumerable) 164 | { 165 | if (where(value)) 166 | { 167 | yield return value; 168 | } 169 | } 170 | } 171 | 172 | public static IEnumerable Where(this T[] ienumerable) 173 | /// There should be syntax sugar on the constraints to 174 | /// make them look more like function constraints. 175 | where Predicate : struct, IFunction // -> where Predicate : [bool method(T)] 176 | { 177 | #region Code that should be unnecessary 178 | 179 | Predicate predicate = default; 180 | 181 | #endregion 182 | 183 | /// The generic parameter should be invocable directly the ".Do" calls should be unnecessary. 184 | 185 | foreach (T value in ienumerable) 186 | { 187 | if (predicate.Do(value)) 188 | { 189 | yield return value; 190 | } 191 | } 192 | } 193 | 194 | public static void Where(this T[] ienumerable) 195 | /// There should be syntax sugar on the constraints to 196 | /// make them look more like function constraints. 197 | where Predicate : struct, IFunction // -> where Predicate : [bool method(T)] 198 | where Action : struct, IAction // -> where Action : [void method(T)] 199 | { 200 | #region Code that should be unnecessary 201 | 202 | Predicate predicate = default; 203 | Action action = default; 204 | 205 | #endregion 206 | 207 | /// The generic parameter should be invocable directly the ".Do" calls should be unnecessary. 208 | 209 | foreach (T value in ienumerable) 210 | { 211 | if (predicate.Do(value)) 212 | { 213 | action.Do(value); 214 | } 215 | } 216 | } 217 | } 218 | } -------------------------------------------------------------------------------- /Benchmarks/MethodAbstractions/MethodAbstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Benchmarks/MethodAbstractions/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace MethodAbstractions 4 | { 5 | public class Program 6 | { 7 | public static void Main() 8 | { 9 | BenchmarkRunner.Run(); 10 | BenchmarkRunner.Run(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Benchmarks/MethodAbstractions/README.md: -------------------------------------------------------------------------------- 1 | # Method Abstractions 2 | 3 | There are numerous ways to abstract methods in C# to make them more reusable. This benchmark 4 | puts several patterns of abstraction to the test to see help you determine what abstraction 5 | pattern you should use in your code. 6 | 7 | ### AdditionBenchmarks 8 | 9 | ``` ini 10 | 11 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 12 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 13 | .NET Core SDK=5.0.100-preview.6.20318.15 14 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 15 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 16 | 17 | 18 | ``` 19 | | Method | Mean | Error | StdDev | 20 | |--------------------------------------- |-----------:|----------:|----------:| 21 | | IntegerAdd | 0.7552 ns | 0.0195 ns | 0.0182 ns | 22 | | IntegerAddStructWithAggressiveInlining | 0.9201 ns | 0.0086 ns | 0.0072 ns | 23 | | IntegerAddLinqWithAggressiveInlining | 10.1687 ns | 0.1508 ns | 0.1337 ns | 24 | | IntegerAddStruct | 6.2190 ns | 0.1140 ns | 0.1066 ns | 25 | | IntegerAddLinq | 9.9643 ns | 0.1672 ns | 0.1564 ns | 26 | 27 | ### EnumerableBenchmarks 28 | 29 | ``` ini 30 | 31 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 32 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 33 | .NET Core SDK=5.0.100-preview.6.20318.15 34 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 35 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 36 | 37 | 38 | ``` 39 | | Method | Mean | Error | StdDev | 40 | |----------------------------------------------- |----------:|----------:|----------:| 41 | | Control | 7.544 ns | 0.1754 ns | 0.2088 ns | 42 | | LinqWhereLambda | 73.708 ns | 1.2615 ns | 1.2390 ns | 43 | | LinqWhere | 85.193 ns | 0.5620 ns | 0.4693 ns | 44 | | CustomWhereLambda | 82.561 ns | 1.2625 ns | 1.0542 ns | 45 | | CustomWhere | 92.224 ns | 1.8695 ns | 2.1529 ns | 46 | | GenericStructWhere | 64.016 ns | 0.9528 ns | 0.7438 ns | 47 | | GenericStructWhereAction | 11.295 ns | 0.1643 ns | 0.1537 ns | 48 | | CustomWhereWithAggressiveInlining | 96.397 ns | 1.5992 ns | 1.4959 ns | 49 | | GenericStructWhereWithAggressiveInlining | 61.405 ns | 0.9640 ns | 1.0315 ns | 50 | | GenericStructWhereActionWithAggressiveInlining | 11.479 ns | 0.2485 ns | 0.2862 ns | 51 | -------------------------------------------------------------------------------- /Benchmarks/MultipleReturnsTechniques/MultipleReturnsIntBenchmarks.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from the "MultipleReturnsIntBenchmarks.tt" T4 Text Template. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using BenchmarkDotNet.Attributes; 8 | using System; 9 | using System.Runtime.CompilerServices; 10 | 11 | namespace MultipleReturnsTechniques 12 | { 13 | public class MultipleReturnsIntBenchmarks 14 | { 15 | [Benchmark] 16 | public void return_int_1() => _ = return_int_1_method(); 17 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 18 | public int return_int_1_method() 19 | { 20 | return new int(); 21 | } 22 | 23 | [Benchmark] 24 | public void out_int_1() => out_int_1_method(out _); 25 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 26 | public void out_int_1_method(out int _1) 27 | { 28 | _1 = new int(); 29 | } 30 | 31 | [Benchmark] 32 | public void tuple_int_1() => _ = tuple_int_1_method(); 33 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 34 | public Tuple tuple_int_1_method() 35 | { 36 | return new Tuple( 37 | new int()); 38 | } 39 | 40 | [Benchmark] 41 | public void valuetuple_int_1() => _ = valuetuple_int_1_method(); 42 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 43 | public ValueTuple valuetuple_int_1_method() 44 | { 45 | return new ValueTuple( 46 | new int()); 47 | } 48 | 49 | [Benchmark] 50 | public void out_int_2() => out_int_2_method(out _, out _); 51 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 52 | public void out_int_2_method(out int _1, out int _2) 53 | { 54 | _1 = new int(); 55 | _2 = new int(); 56 | } 57 | 58 | [Benchmark] 59 | public void tuple_int_2() => _ = tuple_int_2_method(); 60 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 61 | public Tuple tuple_int_2_method() 62 | { 63 | return new Tuple( 64 | new int(), 65 | new int()); 66 | } 67 | 68 | [Benchmark] 69 | public void valuetuple_int_2() => _ = valuetuple_int_2_method(); 70 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 71 | public ValueTuple valuetuple_int_2_method() 72 | { 73 | return new ValueTuple( 74 | new int(), 75 | new int()); 76 | } 77 | 78 | [Benchmark] 79 | public void out_int_3() => out_int_3_method(out _, out _, out _); 80 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 81 | public void out_int_3_method(out int _1, out int _2, out int _3) 82 | { 83 | _1 = new int(); 84 | _2 = new int(); 85 | _3 = new int(); 86 | } 87 | 88 | [Benchmark] 89 | public void tuple_int_3() => _ = tuple_int_3_method(); 90 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 91 | public Tuple tuple_int_3_method() 92 | { 93 | return new Tuple( 94 | new int(), 95 | new int(), 96 | new int()); 97 | } 98 | 99 | [Benchmark] 100 | public void valuetuple_int_3() => _ = valuetuple_int_3_method(); 101 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 102 | public ValueTuple valuetuple_int_3_method() 103 | { 104 | return new ValueTuple( 105 | new int(), 106 | new int(), 107 | new int()); 108 | } 109 | 110 | [Benchmark] 111 | public void out_int_4() => out_int_4_method(out _, out _, out _, out _); 112 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 113 | public void out_int_4_method(out int _1, out int _2, out int _3, out int _4) 114 | { 115 | _1 = new int(); 116 | _2 = new int(); 117 | _3 = new int(); 118 | _4 = new int(); 119 | } 120 | 121 | [Benchmark] 122 | public void tuple_int_4() => _ = tuple_int_4_method(); 123 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 124 | public Tuple tuple_int_4_method() 125 | { 126 | return new Tuple( 127 | new int(), 128 | new int(), 129 | new int(), 130 | new int()); 131 | } 132 | 133 | [Benchmark] 134 | public void valuetuple_int_4() => _ = valuetuple_int_4_method(); 135 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 136 | public ValueTuple valuetuple_int_4_method() 137 | { 138 | return new ValueTuple( 139 | new int(), 140 | new int(), 141 | new int(), 142 | new int()); 143 | } 144 | 145 | [Benchmark] 146 | public void out_int_5() => out_int_5_method(out _, out _, out _, out _, out _); 147 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 148 | public void out_int_5_method(out int _1, out int _2, out int _3, out int _4, out int _5) 149 | { 150 | _1 = new int(); 151 | _2 = new int(); 152 | _3 = new int(); 153 | _4 = new int(); 154 | _5 = new int(); 155 | } 156 | 157 | [Benchmark] 158 | public void tuple_int_5() => _ = tuple_int_5_method(); 159 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 160 | public Tuple tuple_int_5_method() 161 | { 162 | return new Tuple( 163 | new int(), 164 | new int(), 165 | new int(), 166 | new int(), 167 | new int()); 168 | } 169 | 170 | [Benchmark] 171 | public void valuetuple_int_5() => _ = valuetuple_int_5_method(); 172 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 173 | public ValueTuple valuetuple_int_5_method() 174 | { 175 | return new ValueTuple( 176 | new int(), 177 | new int(), 178 | new int(), 179 | new int(), 180 | new int()); 181 | } 182 | 183 | [Benchmark] 184 | public void out_int_6() => out_int_6_method(out _, out _, out _, out _, out _, out _); 185 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 186 | public void out_int_6_method(out int _1, out int _2, out int _3, out int _4, out int _5, out int _6) 187 | { 188 | _1 = new int(); 189 | _2 = new int(); 190 | _3 = new int(); 191 | _4 = new int(); 192 | _5 = new int(); 193 | _6 = new int(); 194 | } 195 | 196 | [Benchmark] 197 | public void tuple_int_6() => _ = tuple_int_6_method(); 198 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 199 | public Tuple tuple_int_6_method() 200 | { 201 | return new Tuple( 202 | new int(), 203 | new int(), 204 | new int(), 205 | new int(), 206 | new int(), 207 | new int()); 208 | } 209 | 210 | [Benchmark] 211 | public void valuetuple_int_6() => _ = valuetuple_int_6_method(); 212 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 213 | public ValueTuple valuetuple_int_6_method() 214 | { 215 | return new ValueTuple( 216 | new int(), 217 | new int(), 218 | new int(), 219 | new int(), 220 | new int(), 221 | new int()); 222 | } 223 | 224 | [Benchmark] 225 | public void out_int_7() => out_int_7_method(out _, out _, out _, out _, out _, out _, out _); 226 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 227 | public void out_int_7_method(out int _1, out int _2, out int _3, out int _4, out int _5, out int _6, out int _7) 228 | { 229 | _1 = new int(); 230 | _2 = new int(); 231 | _3 = new int(); 232 | _4 = new int(); 233 | _5 = new int(); 234 | _6 = new int(); 235 | _7 = new int(); 236 | } 237 | 238 | [Benchmark] 239 | public void tuple_int_7() => _ = tuple_int_7_method(); 240 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 241 | public Tuple tuple_int_7_method() 242 | { 243 | return new Tuple( 244 | new int(), 245 | new int(), 246 | new int(), 247 | new int(), 248 | new int(), 249 | new int(), 250 | new int()); 251 | } 252 | 253 | [Benchmark] 254 | public void valuetuple_int_7() => _ = valuetuple_int_7_method(); 255 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 256 | public ValueTuple valuetuple_int_7_method() 257 | { 258 | return new ValueTuple( 259 | new int(), 260 | new int(), 261 | new int(), 262 | new int(), 263 | new int(), 264 | new int(), 265 | new int()); 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /Benchmarks/MultipleReturnsTechniques/MultipleReturnsIntBenchmarks.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Collections.Generic" #> 6 | <#@ output extension=".cs" #> 7 | //------------------------------------------------------------------------------ 8 | // 9 | // This code was generated from the "MultipleReturnsIntBenchmarks.tt" T4 Text Template. 10 | // 11 | //------------------------------------------------------------------------------ 12 | <# int maxsize = 7; #> 13 | 14 | using BenchmarkDotNet.Attributes; 15 | using System; 16 | using System.Runtime.CompilerServices; 17 | 18 | namespace MultipleReturnsTechniques 19 | { 20 | public class MultipleReturnsIntBenchmarks 21 | { 22 | [Benchmark] 23 | public void return_int_1() => _ = return_int_1_method(); 24 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 25 | public int return_int_1_method() 26 | { 27 | return new int(); 28 | } 29 | <# for (int i = 1; i <= maxsize; i++) { #> 30 | 31 | [Benchmark] 32 | public void out_int_<#= i #>() => out_int_<#= i #>_method(<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"out _")) #>); 33 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 34 | public void out_int_<#= i #>_method(<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"out int _{x}")) #>) 35 | { 36 | <# for (int j = 1; j <= i; j++) { #> 37 | _<#= j #> = new int(); 38 | <# } #> 39 | } 40 | 41 | [Benchmark] 42 | public void tuple_int_<#= i #>() => _ = tuple_int_<#= i #>_method(); 43 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 44 | public Tuple<<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"int")) #>> tuple_int_<#= i #>_method() 45 | { 46 | return new Tuple<<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"int")) #>>( 47 | new int()<# for (int j = 1; j < i; j++) { #>, 48 | new int()<# } #>); 49 | } 50 | 51 | [Benchmark] 52 | public void valuetuple_int_<#= i #>() => _ = valuetuple_int_<#= i #>_method(); 53 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 54 | public ValueTuple<<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"int")) #>> valuetuple_int_<#= i #>_method() 55 | { 56 | return new ValueTuple<<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"int")) #>>( 57 | new int()<# for (int j = 1; j < i; j++) { #>, 58 | new int()<# } #>); 59 | } 60 | <# } #> 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Benchmarks/MultipleReturnsTechniques/MultipleReturnsObjectBenchmarks.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from the "MultipleReturnsObjectBenchmarks.tt" T4 Text Template. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using BenchmarkDotNet.Attributes; 8 | using System; 9 | using System.Runtime.CompilerServices; 10 | 11 | namespace MultipleReturnsTechniques 12 | { 13 | public class MultipleReturnsObjectBenchmarks 14 | { 15 | [Benchmark] 16 | public void return_object_1() => _ = return_object_1_method(); 17 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 18 | public object return_object_1_method() 19 | { 20 | return new object(); 21 | } 22 | 23 | [Benchmark] 24 | public void out_object_1() => out_object_1_method(out _); 25 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 26 | public void out_object_1_method(out object _1) 27 | { 28 | _1 = new object(); 29 | } 30 | 31 | [Benchmark] 32 | public void tuple_object_1() => _ = tuple_object_1_method(); 33 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 34 | public Tuple tuple_object_1_method() 35 | { 36 | return new Tuple( 37 | new object()); 38 | } 39 | 40 | [Benchmark] 41 | public void valuetuple_object_1() => _ = valuetuple_object_1_method(); 42 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 43 | public ValueTuple valuetuple_object_1_method() 44 | { 45 | return new ValueTuple( 46 | new object()); 47 | } 48 | 49 | [Benchmark] 50 | public void out_object_2() => out_object_2_method(out _, out _); 51 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 52 | public void out_object_2_method(out object _1, out object _2) 53 | { 54 | _1 = new object(); 55 | _2 = new object(); 56 | } 57 | 58 | [Benchmark] 59 | public void tuple_object_2() => _ = tuple_object_2_method(); 60 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 61 | public Tuple tuple_object_2_method() 62 | { 63 | return new Tuple( 64 | new object(), 65 | new object()); 66 | } 67 | 68 | [Benchmark] 69 | public void valuetuple_object_2() => _ = valuetuple_object_2_method(); 70 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 71 | public ValueTuple valuetuple_object_2_method() 72 | { 73 | return new ValueTuple( 74 | new object(), 75 | new object()); 76 | } 77 | 78 | [Benchmark] 79 | public void out_object_3() => out_object_3_method(out _, out _, out _); 80 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 81 | public void out_object_3_method(out object _1, out object _2, out object _3) 82 | { 83 | _1 = new object(); 84 | _2 = new object(); 85 | _3 = new object(); 86 | } 87 | 88 | [Benchmark] 89 | public void tuple_object_3() => _ = tuple_object_3_method(); 90 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 91 | public Tuple tuple_object_3_method() 92 | { 93 | return new Tuple( 94 | new object(), 95 | new object(), 96 | new object()); 97 | } 98 | 99 | [Benchmark] 100 | public void valuetuple_object_3() => _ = valuetuple_object_3_method(); 101 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 102 | public ValueTuple valuetuple_object_3_method() 103 | { 104 | return new ValueTuple( 105 | new object(), 106 | new object(), 107 | new object()); 108 | } 109 | 110 | [Benchmark] 111 | public void out_object_4() => out_object_4_method(out _, out _, out _, out _); 112 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 113 | public void out_object_4_method(out object _1, out object _2, out object _3, out object _4) 114 | { 115 | _1 = new object(); 116 | _2 = new object(); 117 | _3 = new object(); 118 | _4 = new object(); 119 | } 120 | 121 | [Benchmark] 122 | public void tuple_object_4() => _ = tuple_object_4_method(); 123 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 124 | public Tuple tuple_object_4_method() 125 | { 126 | return new Tuple( 127 | new object(), 128 | new object(), 129 | new object(), 130 | new object()); 131 | } 132 | 133 | [Benchmark] 134 | public void valuetuple_object_4() => _ = valuetuple_object_4_method(); 135 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 136 | public ValueTuple valuetuple_object_4_method() 137 | { 138 | return new ValueTuple( 139 | new object(), 140 | new object(), 141 | new object(), 142 | new object()); 143 | } 144 | 145 | [Benchmark] 146 | public void out_object_5() => out_object_5_method(out _, out _, out _, out _, out _); 147 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 148 | public void out_object_5_method(out object _1, out object _2, out object _3, out object _4, out object _5) 149 | { 150 | _1 = new object(); 151 | _2 = new object(); 152 | _3 = new object(); 153 | _4 = new object(); 154 | _5 = new object(); 155 | } 156 | 157 | [Benchmark] 158 | public void tuple_object_5() => _ = tuple_object_5_method(); 159 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 160 | public Tuple tuple_object_5_method() 161 | { 162 | return new Tuple( 163 | new object(), 164 | new object(), 165 | new object(), 166 | new object(), 167 | new object()); 168 | } 169 | 170 | [Benchmark] 171 | public void valuetuple_object_5() => _ = valuetuple_object_5_method(); 172 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 173 | public ValueTuple valuetuple_object_5_method() 174 | { 175 | return new ValueTuple( 176 | new object(), 177 | new object(), 178 | new object(), 179 | new object(), 180 | new object()); 181 | } 182 | 183 | [Benchmark] 184 | public void out_object_6() => out_object_6_method(out _, out _, out _, out _, out _, out _); 185 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 186 | public void out_object_6_method(out object _1, out object _2, out object _3, out object _4, out object _5, out object _6) 187 | { 188 | _1 = new object(); 189 | _2 = new object(); 190 | _3 = new object(); 191 | _4 = new object(); 192 | _5 = new object(); 193 | _6 = new object(); 194 | } 195 | 196 | [Benchmark] 197 | public void tuple_object_6() => _ = tuple_object_6_method(); 198 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 199 | public Tuple tuple_object_6_method() 200 | { 201 | return new Tuple( 202 | new object(), 203 | new object(), 204 | new object(), 205 | new object(), 206 | new object(), 207 | new object()); 208 | } 209 | 210 | [Benchmark] 211 | public void valuetuple_object_6() => _ = valuetuple_object_6_method(); 212 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 213 | public ValueTuple valuetuple_object_6_method() 214 | { 215 | return new ValueTuple( 216 | new object(), 217 | new object(), 218 | new object(), 219 | new object(), 220 | new object(), 221 | new object()); 222 | } 223 | 224 | [Benchmark] 225 | public void out_object_7() => out_object_7_method(out _, out _, out _, out _, out _, out _, out _); 226 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 227 | public void out_object_7_method(out object _1, out object _2, out object _3, out object _4, out object _5, out object _6, out object _7) 228 | { 229 | _1 = new object(); 230 | _2 = new object(); 231 | _3 = new object(); 232 | _4 = new object(); 233 | _5 = new object(); 234 | _6 = new object(); 235 | _7 = new object(); 236 | } 237 | 238 | [Benchmark] 239 | public void tuple_object_7() => _ = tuple_object_7_method(); 240 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 241 | public Tuple tuple_object_7_method() 242 | { 243 | return new Tuple( 244 | new object(), 245 | new object(), 246 | new object(), 247 | new object(), 248 | new object(), 249 | new object(), 250 | new object()); 251 | } 252 | 253 | [Benchmark] 254 | public void valuetuple_object_7() => _ = valuetuple_object_7_method(); 255 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 256 | public ValueTuple valuetuple_object_7_method() 257 | { 258 | return new ValueTuple( 259 | new object(), 260 | new object(), 261 | new object(), 262 | new object(), 263 | new object(), 264 | new object(), 265 | new object()); 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /Benchmarks/MultipleReturnsTechniques/MultipleReturnsObjectBenchmarks.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Collections.Generic" #> 6 | <#@ output extension=".cs" #> 7 | //------------------------------------------------------------------------------ 8 | // 9 | // This code was generated from the "MultipleReturnsObjectBenchmarks.tt" T4 Text Template. 10 | // 11 | //------------------------------------------------------------------------------ 12 | <# int maxsize = 7; #> 13 | 14 | using BenchmarkDotNet.Attributes; 15 | using System; 16 | using System.Runtime.CompilerServices; 17 | 18 | namespace MultipleReturnsTechniques 19 | { 20 | public class MultipleReturnsObjectBenchmarks 21 | { 22 | [Benchmark] 23 | public void return_object_1() => _ = return_object_1_method(); 24 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 25 | public object return_object_1_method() 26 | { 27 | return new object(); 28 | } 29 | <# for (int i = 1; i <= maxsize; i++) { #> 30 | 31 | [Benchmark] 32 | public void out_object_<#= i #>() => out_object_<#= i #>_method(<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"out _")) #>); 33 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 34 | public void out_object_<#= i #>_method(<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"out object _{x}")) #>) 35 | { 36 | <# for (int j = 1; j <= i; j++) { #> 37 | _<#= j #> = new object(); 38 | <# } #> 39 | } 40 | 41 | [Benchmark] 42 | public void tuple_object_<#= i #>() => _ = tuple_object_<#= i #>_method(); 43 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 44 | public Tuple<<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"object")) #>> tuple_object_<#= i #>_method() 45 | { 46 | return new Tuple<<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"object")) #>>( 47 | new object()<# for (int j = 1; j < i; j++) { #>, 48 | new object()<# } #>); 49 | } 50 | 51 | [Benchmark] 52 | public void valuetuple_object_<#= i #>() => _ = valuetuple_object_<#= i #>_method(); 53 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 54 | public ValueTuple<<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"object")) #>> valuetuple_object_<#= i #>_method() 55 | { 56 | return new ValueTuple<<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"object")) #>>( 57 | new object()<# for (int j = 1; j < i; j++) { #>, 58 | new object()<# } #>); 59 | } 60 | <# } #> 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Benchmarks/MultipleReturnsTechniques/MultipleReturnsTechniques.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | True 11 | True 12 | MultipleReturnsIntBenchmarks.tt 13 | 14 | 15 | True 16 | True 17 | MultipleReturnsObjectBenchmarks.tt 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | TextTemplatingFileGenerator 28 | MultipleReturnsIntBenchmarks.cs 29 | 30 | 31 | TextTemplatingFileGenerator 32 | MultipleReturnsObjectBenchmarks.cs 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | True 43 | True 44 | MultipleReturnsIntBenchmarks.tt 45 | 46 | 47 | True 48 | True 49 | MultipleReturnsObjectBenchmarks.tt 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Benchmarks/MultipleReturnsTechniques/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace MultipleReturnsTechniques 4 | { 5 | class Program 6 | { 7 | static void Main() 8 | { 9 | BenchmarkRunner.Run(); 10 | BenchmarkRunner.Run(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Benchmarks/MultipleReturnsTechniques/README.md: -------------------------------------------------------------------------------- 1 | # Multiple Returns Techniques 2 | 3 | This benchmark it to help determine the most appropriate pattern for returning multiple values 4 | from a method: 5 | - `out` parameters 6 | - returning classes (`Tuple`) 7 | - returning structs (`ValueTuple`) 8 | 9 | ``` ini 10 | 11 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 12 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 13 | .NET Core SDK=5.0.100-preview.6.20318.15 14 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 15 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 16 | 17 | 18 | ``` 19 | | Method | Mean | Error | StdDev | Median | 20 | |-------------------- |----------:|----------:|----------:|----------:| 21 | | return_object_1 | 5.512 ns | 0.1352 ns | 0.1660 ns | 5.453 ns | 22 | | out_object_1 | 7.174 ns | 0.1311 ns | 0.1226 ns | 7.188 ns | 23 | | tuple_object_1 | 12.453 ns | 0.2740 ns | 0.2814 ns | 12.435 ns | 24 | | valuetuple_object_1 | 8.831 ns | 0.2009 ns | 0.1973 ns | 8.742 ns | 25 | | out_object_2 | 14.012 ns | 0.2429 ns | 0.2599 ns | 13.958 ns | 26 | | tuple_object_2 | 18.416 ns | 0.2248 ns | 0.2103 ns | 18.316 ns | 27 | | valuetuple_object_2 | 17.783 ns | 0.1786 ns | 0.1583 ns | 17.756 ns | 28 | | out_object_3 | 19.789 ns | 0.3101 ns | 0.2901 ns | 19.742 ns | 29 | | tuple_object_3 | 25.341 ns | 0.2785 ns | 0.2469 ns | 25.248 ns | 30 | | valuetuple_object_3 | 24.357 ns | 0.1456 ns | 0.1216 ns | 24.380 ns | 31 | | out_object_4 | 27.393 ns | 0.5725 ns | 0.9406 ns | 27.111 ns | 32 | | tuple_object_4 | 31.564 ns | 0.6439 ns | 0.6613 ns | 31.379 ns | 33 | | valuetuple_object_4 | 31.492 ns | 0.4104 ns | 0.3638 ns | 31.456 ns | 34 | | out_object_5 | 32.031 ns | 0.5698 ns | 0.4758 ns | 31.896 ns | 35 | | tuple_object_5 | 38.926 ns | 0.7763 ns | 0.8940 ns | 38.631 ns | 36 | | valuetuple_object_5 | 38.895 ns | 0.2511 ns | 0.2097 ns | 38.854 ns | 37 | | out_object_6 | 38.134 ns | 0.3765 ns | 0.3522 ns | 38.013 ns | 38 | | tuple_object_6 | 56.387 ns | 0.5732 ns | 0.5081 ns | 56.321 ns | 39 | | valuetuple_object_6 | 47.245 ns | 0.9334 ns | 0.9987 ns | 47.181 ns | 40 | | out_object_7 | 46.267 ns | 0.9470 ns | 0.9301 ns | 46.146 ns | 41 | | tuple_object_7 | 55.636 ns | 1.1037 ns | 2.8292 ns | 54.674 ns | 42 | | valuetuple_object_7 | 57.058 ns | 1.1644 ns | 1.8802 ns | 56.180 ns | 43 | 44 | ``` ini 45 | 46 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 47 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 48 | .NET Core SDK=5.0.100-preview.6.20318.15 49 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 50 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.30506, CoreFX 5.0.20.30506), X64 RyuJIT 51 | 52 | 53 | ``` 54 | | Method | Mean | Error | StdDev | Median | 55 | |----------------- |---------:|----------:|----------:|---------:| 56 | | return_int_1 | 1.564 ns | 0.0462 ns | 0.0386 ns | 1.571 ns | 57 | | out_int_1 | 1.614 ns | 0.0604 ns | 0.1634 ns | 1.545 ns | 58 | | tuple_int_1 | 5.902 ns | 0.1449 ns | 0.1488 ns | 5.878 ns | 59 | | valuetuple_int_1 | 2.519 ns | 0.0766 ns | 0.1321 ns | 2.508 ns | 60 | | out_int_2 | 1.524 ns | 0.0491 ns | 0.0459 ns | 1.506 ns | 61 | | tuple_int_2 | 5.852 ns | 0.0583 ns | 0.0545 ns | 5.835 ns | 62 | | valuetuple_int_2 | 4.630 ns | 0.0283 ns | 0.0251 ns | 4.625 ns | 63 | | out_int_3 | 2.253 ns | 0.0243 ns | 0.0215 ns | 2.250 ns | 64 | | tuple_int_3 | 6.927 ns | 0.0515 ns | 0.0457 ns | 6.917 ns | 65 | | valuetuple_int_3 | 5.942 ns | 0.0195 ns | 0.0173 ns | 5.937 ns | 66 | | out_int_4 | 2.906 ns | 0.0273 ns | 0.0256 ns | 2.894 ns | 67 | | tuple_int_4 | 6.974 ns | 0.0705 ns | 0.0659 ns | 6.982 ns | 68 | | valuetuple_int_4 | 6.585 ns | 0.0215 ns | 0.0191 ns | 6.583 ns | 69 | | out_int_5 | 3.307 ns | 0.0309 ns | 0.0241 ns | 3.306 ns | 70 | | tuple_int_5 | 7.741 ns | 0.0642 ns | 0.0536 ns | 7.764 ns | 71 | | valuetuple_int_5 | 7.248 ns | 0.1475 ns | 0.1380 ns | 7.351 ns | 72 | | out_int_6 | 4.201 ns | 0.1085 ns | 0.1065 ns | 4.186 ns | 73 | | tuple_int_6 | 8.544 ns | 0.1892 ns | 0.2103 ns | 8.500 ns | 74 | | valuetuple_int_6 | 8.251 ns | 0.1036 ns | 0.0969 ns | 8.221 ns | 75 | | out_int_7 | 7.654 ns | 0.1287 ns | 0.1141 ns | 7.663 ns | 76 | | tuple_int_7 | 9.647 ns | 0.2139 ns | 0.2999 ns | 9.598 ns | 77 | | valuetuple_int_7 | 8.702 ns | 0.1395 ns | 0.1305 ns | 8.641 ns | 78 | -------------------------------------------------------------------------------- /Benchmarks/README.md: -------------------------------------------------------------------------------- 1 | ### Benchmarks 2 | 3 | This is the root of all answers. No matter how well you're into the subject, 4 | the reality knows more than you do. But benchmarks themselves might result in 5 | different conclusions, so it's all up to the reader how to perceive them. 6 | 7 | - [Arrays](./Arrays) 8 | - [Branching Techniques](./BranchingTechniques) 9 | - [Exception Handling Techniques](./ExceptionHandlingTechniques) 10 | - [Indexed Collection Abstractions](./IndexedCollectionAbstractions) 11 | - [Method Abstractions](./MethodAbstractions) 12 | - [Multiple Returns Techniques](./MultipleReturnsTechniques) 13 | - [Reflection Techniques](./ReflectionTechniques) 14 | - [String Formatting Techniques](./StringFormattingTechniques) 15 | - [Boxing](./Boxing) 16 | - [Type Equality Checking](./TypeEqualityChecking) 17 | - [Iteration Ref Parameters Vs Returns](./IterationRefParametersVsReturns) -------------------------------------------------------------------------------- /Benchmarks/ReflectionTechniques/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace ReflectionTechniques 4 | { 5 | class Program 6 | { 7 | static void Main() 8 | { 9 | BenchmarkRunner.Run(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Benchmarks/ReflectionTechniques/README.md: -------------------------------------------------------------------------------- 1 | # Reflection Techniques 2 | 3 | This benchmark compares some patterns for using reflection. 4 | 5 | ``` ini 6 | 7 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 8 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 9 | .NET Core SDK=5.0.100-preview.8.20417.9 10 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.40711, CoreFX 5.0.20.40711), X64 RyuJIT 11 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.40711, CoreFX 5.0.20.40711), X64 RyuJIT 12 | 13 | 14 | ``` 15 | | Method | Mean | Error | StdDev | 16 | |--------------------------------------- |-----------:|----------:|----------:| 17 | | FieldDirect | 0.4253 ns | 0.0328 ns | 0.0307 ns | 18 | | FieldReflection | 89.7686 ns | 0.5237 ns | 0.4373 ns | 19 | | FieldReflectionCached | 2.5252 ns | 0.0303 ns | 0.0283 ns | 20 | | FieldReflectionCachedLookUpKnownType | 19.8763 ns | 0.1196 ns | 0.1060 ns | 21 | | FieldReflectionCachedLookUpUnknownType | 52.8681 ns | 0.8062 ns | 0.7147 ns | 22 | -------------------------------------------------------------------------------- /Benchmarks/ReflectionTechniques/ReflectionBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Linq.Expressions; 5 | using Microsoft.Diagnostics.Runtime.Interop; 6 | using System.Collections.Generic; 7 | using System.Reflection; 8 | 9 | namespace ReflectionTechniques 10 | { 11 | public class ReflectionBenchmarks 12 | { 13 | A a = new A(); 14 | int temp; 15 | 16 | Func cachedReflection; 17 | 18 | [GlobalSetup] 19 | public void GlobalSetup() 20 | { 21 | cachedReflection = FieldGetterDelegate(nameof(A.a)); 22 | } 23 | 24 | [Benchmark] 25 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 26 | public void FieldDirect() 27 | { 28 | temp = a.a; 29 | } 30 | 31 | [Benchmark] 32 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 33 | public void FieldReflection() 34 | { 35 | temp = (int)typeof(A).GetField(nameof(A.a)).GetValue(a); 36 | } 37 | 38 | [Benchmark] 39 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 40 | public void FieldReflectionCached() 41 | { 42 | temp = cachedReflection(a); 43 | } 44 | 45 | [Benchmark] 46 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 47 | public void FieldReflectionCachedLookUpKnownType() 48 | { 49 | temp = GetFieldValue(a, nameof(A.a)); 50 | } 51 | 52 | [Benchmark] 53 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 54 | public void FieldReflectionCachedLookUpUnknownType() 55 | { 56 | temp = (int)GetFieldValue(a, nameof(A.a)); 57 | } 58 | 59 | public static Field GetFieldValue(Type instance, string name) => 60 | GetFieldValueImplementation.Do(instance, name); 61 | 62 | internal static class GetFieldValueImplementation 63 | { 64 | public static Dictionary> Delegates = 65 | new Dictionary>(); 66 | 67 | internal static Field Do(Type instance, string field) 68 | { 69 | if (Delegates.TryGetValue(field, out var cachedGet)) 70 | { 71 | return cachedGet(instance); 72 | } 73 | var newGet = FieldGetterDelegate(field); 74 | Delegates.Add(field, newGet); 75 | return newGet(instance); 76 | } 77 | } 78 | 79 | public static object GetFieldValue(object instance, string name) => 80 | GetFieldValueObjectImplementation.Do(instance, name); 81 | 82 | internal static class GetFieldValueObjectImplementation 83 | { 84 | public static Dictionary<(Type, string), Func> Delegates = 85 | new Dictionary<(Type, string), Func>(); 86 | 87 | internal static object Do(object instance, string field) 88 | { 89 | Type type = instance.GetType(); 90 | if (Delegates.TryGetValue((type, field), out var cachedGet)) 91 | { 92 | return cachedGet(instance); 93 | } 94 | FieldInfo fieldInfo = type.GetField(field, 95 | BindingFlags.Instance | 96 | BindingFlags.Public | 97 | BindingFlags.NonPublic); 98 | ParameterExpression A = Expression.Parameter(typeof(object)); 99 | Expression BODY = Expression.Convert(Expression.Field(Expression.Convert(A, type), fieldInfo), typeof(object)); 100 | var newGet = Expression.Lambda>(BODY, A).Compile(); 101 | Delegates.Add((type, field), newGet); 102 | return newGet(instance); 103 | } 104 | } 105 | 106 | public static Func FieldGetterDelegate(string name) 107 | { 108 | FieldInfo fieldInfo = typeof(T1).GetField(name, 109 | BindingFlags.Instance | 110 | BindingFlags.Public | 111 | BindingFlags.NonPublic); 112 | ParameterExpression A = Expression.Parameter(typeof(T1)); 113 | Expression BODY = Expression.Field(A, fieldInfo); 114 | return Expression.Lambda>(BODY, A).Compile(); 115 | } 116 | } 117 | 118 | public class A 119 | { 120 | public int a = -1; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Benchmarks/ReflectionTechniques/ReflectionTechniques.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Benchmarks/StringFormattingTechniques/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace StringFormattingTechniques 4 | { 5 | class Program 6 | { 7 | static void Main() 8 | { 9 | BenchmarkRunner.Run(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Benchmarks/StringFormattingTechniques/README.md: -------------------------------------------------------------------------------- 1 | # String Formatting Techniques 2 | 3 | This benchmark it to help determine the most appropriate pattern for creating formatted strings: 4 | - `string.Format` 5 | - `string` interpolation 6 | - `string` addition operator `+` 7 | - `StringBuilder` 8 | - `string` add equals operator `+=` 9 | - `string.Concat` 10 | 11 | ``` ini 12 | 13 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2) 14 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 15 | .NET Core SDK=5.0.100-preview.8.20417.9 16 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.40711, CoreFX 5.0.20.40711), X64 RyuJIT 17 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.40711, CoreFX 5.0.20.40711), X64 RyuJIT 18 | 19 | ``` 20 | Below is the mean time to required each method in nanoseconds (ns). 21 | | n | AddOperator | Format | Interpolation | StringBuilder | StringConcat | AddEqual | 22 | |------|-------------|------------|---------------|---------------|--------------|---------------| 23 | | 1 | 22.936 | 54.764 | 54.731 | 30.234 | 29.510 | 6.472 | 24 | | 2 | 57.093 | 78.482 | 80.777 | 44.757 | 85.012 | 41.773 | 25 | | 3 | 75.102 | 105.266 | 105.412 | 54.801 | 117.253 | 76.963 | 26 | | 4 | 98.671 | 168.388 | 166.350 | 69.490 | 150.042 | 113.962 | 27 | | 5 | 129.662 | 199.057 | 197.629 | 84.966 | 196.839 | 147.956 | 28 | | 10 | 237.780 | 359.197 | 362.663 | 202.503 | 358.128 | 343.394 | 29 | | 100 | 2,915.839 | 3,478.252 | 3,430.917 | 1,801.927 | 4,109.209 | 8,783.083 | 30 | | 1000 | 40,973.943 | 41,055.947 | 41,389.303 | 20,599.310 | 52,959.182 | 1,273,860.226 | 31 | 32 | 33 | | Method | Mean | Error | StdDev | 34 | |--------------------- |-----------------:|---------------:|---------------:| 35 | | AddOperator1 | 22.936 ns | 0.0810 ns | 0.0757 ns | 36 | | Format1 | 54.764 ns | 0.2573 ns | 0.2281 ns | 37 | | Interpolation1 | 54.731 ns | 0.2984 ns | 0.2791 ns | 38 | | StringBuilder1 | 30.234 ns | 0.0941 ns | 0.0835 ns | 39 | | StringConcat1 | 29.510 ns | 0.1511 ns | 0.1339 ns | 40 | | AddEqualOperator1 | 6.472 ns | 0.0292 ns | 0.0259 ns | 41 | | AddOperator2 | 57.093 ns | 0.2869 ns | 0.2684 ns | 42 | | Format2 | 78.482 ns | 0.5314 ns | 0.4710 ns | 43 | | Interpolation2 | 80.777 ns | 1.5722 ns | 1.6822 ns | 44 | | StringBuilder2 | 44.757 ns | 0.1973 ns | 0.1845 ns | 45 | | StringConcat2 | 85.012 ns | 0.5368 ns | 0.4482 ns | 46 | | AddEqualOperator2 | 41.773 ns | 0.2032 ns | 0.1901 ns | 47 | | AddOperator3 | 75.102 ns | 0.3256 ns | 0.3046 ns | 48 | | Format3 | 105.266 ns | 0.4089 ns | 0.3825 ns | 49 | | Interpolation3 | 105.412 ns | 0.5524 ns | 0.4313 ns | 50 | | StringBuilder3 | 54.801 ns | 0.1994 ns | 0.1665 ns | 51 | | StringConcat3 | 117.253 ns | 0.4922 ns | 0.4363 ns | 52 | | AddEqualOperator3 | 76.963 ns | 1.5714 ns | 2.3521 ns | 53 | | AddOperator4 | 98.671 ns | 1.5725 ns | 1.3940 ns | 54 | | Format4 | 168.388 ns | 2.7069 ns | 2.5320 ns | 55 | | Interpolation4 | 166.350 ns | 0.5034 ns | 0.3930 ns | 56 | | StringBuilder4 | 69.490 ns | 1.0857 ns | 1.0155 ns | 57 | | StringConcat4 | 150.042 ns | 1.2438 ns | 1.1026 ns | 58 | | AddEqualOperator4 | 113.962 ns | 1.2124 ns | 1.0748 ns | 59 | | AddOperator5 | 129.662 ns | 1.6293 ns | 1.5241 ns | 60 | | Format5 | 199.057 ns | 3.5246 ns | 3.2969 ns | 61 | | Interpolation5 | 197.629 ns | 1.8404 ns | 1.7215 ns | 62 | | StringBuilder5 | 84.966 ns | 1.0275 ns | 0.9611 ns | 63 | | StringConcat5 | 196.839 ns | 0.8423 ns | 0.6576 ns | 64 | | AddEqualOperator5 | 147.956 ns | 0.8973 ns | 0.8393 ns | 65 | | AddOperator10 | 237.780 ns | 1.1644 ns | 1.0322 ns | 66 | | Format10 | 359.197 ns | 1.0668 ns | 0.8329 ns | 67 | | Interpolation10 | 362.663 ns | 1.4516 ns | 1.3578 ns | 68 | | StringBuilder10 | 202.503 ns | 1.3022 ns | 1.0167 ns | 69 | | StringConcat10 | 358.128 ns | 3.5933 ns | 3.1854 ns | 70 | | AddEqualOperator10 | 343.394 ns | 1.5348 ns | 1.3606 ns | 71 | | AddOperator100 | 2,915.839 ns | 44.0534 ns | 41.2076 ns | 72 | | Format100 | 3,478.252 ns | 14.5496 ns | 12.1495 ns | 73 | | Interpolation100 | 3,430.917 ns | 46.3178 ns | 43.3257 ns | 74 | | StringBuilder100 | 1,801.297 ns | 5.5425 ns | 4.9133 ns | 75 | | StringConcat100 | 4,019.209 ns | 51.3871 ns | 48.0675 ns | 76 | | AddEqualOperator100 | 8,783.083 ns | 94.8101 ns | 79.1707 ns | 77 | | AddOperator1000 | 40,973.943 ns | 238.3025 ns | 222.9083 ns | 78 | | Format1000 | 41,055.947 ns | 472.2821 ns | 441.7729 ns | 79 | | Interpolation1000 | 41,389.303 ns | 633.4845 ns | 592.5618 ns | 80 | | StringBuilder1000 | 20,599.310 ns | 206.1106 ns | 172.1117 ns | 81 | | StringConcat1000 | 52,959.182 ns | 401.8360 ns | 335.5513 ns | 82 | | AddEqualOperator1000 | 1,273,860.226 ns | 14,541.0831 ns | 12,890.2914 ns | 83 | -------------------------------------------------------------------------------- /Benchmarks/StringFormattingTechniques/StringBenchmarks.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Collections.Generic" #> 6 | <#@ output extension=".cs" #> 7 | //------------------------------------------------------------------------------ 8 | // 9 | // This code was generated from the "MultipleReturnsIntBenchmarks.tt" T4 Text Template. 10 | // 11 | //------------------------------------------------------------------------------ 12 | <# int[] sizes = { 1, 2, 3, 4, 5, 10, 100, 1000 }; #> 13 | 14 | using BenchmarkDotNet.Attributes; 15 | using System.Runtime.CompilerServices; 16 | using System.Text; 17 | 18 | namespace StringFormattingTechniques 19 | { 20 | public class StringBenchmarks 21 | { 22 | <# foreach (int i in sizes) { #> 23 | 24 | [Benchmark] 25 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 26 | public void AddOperator<#= i #>() 27 | { 28 | <# for (int j = 0; j < i; j++) { #> 29 | int _<#= j + 1 #> = <#= j + 1 #>; 30 | <# } #> 31 | _ = " " + <#= string.Join(@" + "" "" + ", Enumerable.Range(1, i).Select(j => $@"_{j}")) #> + " "; 32 | } 33 | 34 | [Benchmark] 35 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 36 | public void Format<#= i #>() 37 | { 38 | <# for (int j = 0; j < i; j++) { #> 39 | int _<#= j + 1 #> = <#= j + 1 #>; 40 | <# } #> 41 | _ = string.Format(" <#= string.Join(" ", Enumerable.Range(0, i).Select(j => $"{{{j}}}")) #> ", <#= string.Join(", ", Enumerable.Range(1, i).Select(j => $"_{j}")) #>); 42 | } 43 | 44 | [Benchmark] 45 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 46 | public void Interpolation<#= i #>() 47 | { 48 | <# for (int j = 0; j < i; j++) { #> 49 | int _<#= j + 1 #> = <#= j + 1 #>; 50 | <# } #> 51 | _ = $" <#= string.Join(@" ", Enumerable.Range(1, i).Select(j => $@"{{_{j}}}")) #> "; 52 | } 53 | 54 | [Benchmark] 55 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 56 | public void StringBuilder<#= i #>() 57 | { 58 | <# for (int j = 0; j < i; j++) { #> 59 | int _<#= j + 1 #> = <#= j + 1 #>; 60 | <# } #> 61 | StringBuilder stringBuilder = new StringBuilder(); 62 | stringBuilder.Append(_1); 63 | <# for (int j = 1; j < i; j++) { #> 64 | stringBuilder.Append(" "); 65 | stringBuilder.Append(_<#= j + 1 #>); 66 | <# } #> 67 | _ = stringBuilder.ToString(); 68 | } 69 | 70 | [Benchmark] 71 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 72 | public void StringConcat<#= i #>() 73 | { 74 | <# for (int j = 0; j < i; j++) { #> 75 | int _<#= j + 1 #> = <#= j + 1 #>; 76 | <# } #> 77 | _ = string.Concat(" ", <#= string.Join(@", "" "", ", Enumerable.Range(1, i).Select(j => $@"_{j}")) #>, " "); 78 | } 79 | 80 | [Benchmark] 81 | [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] 82 | public void AddEqualOperator<#= i #>() 83 | { 84 | <# for (int j = 0; j < i; j++) { #> 85 | int _<#= j + 1 #> = <#= j + 1 #>; 86 | <# } #> 87 | string result = string.Empty; 88 | result += _1; 89 | <# for (int j = 1; j < i; j++) { #> 90 | result += " "; 91 | result += _<#= j + 1 #>; 92 | <# } #> 93 | _ = result.ToString(); 94 | } 95 | <# } #> 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Benchmarks/StringFormattingTechniques/StringFormattingTechniques.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | True 11 | True 12 | StringBenchmarks.tt 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | TextTemplatingFileGenerator 23 | StringBenchmarks.cs 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | True 34 | True 35 | StringBenchmarks.tt 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Benchmarks/TypeEqualityChecking/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace TypeEqualityChecking 4 | { 5 | public class Program 6 | { 7 | public static void Main() 8 | { 9 | BenchmarkRunner.Run(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Benchmarks/TypeEqualityChecking/README.md: -------------------------------------------------------------------------------- 1 | # Type Equality Checking 2 | 3 | This benchmark compares some patterns for comparing types for equality. 4 | 5 | Currently, the JIT is not optimizing the generic methods in these 6 | benchmarks without the `[MethodImpl(MethodImplOptions.AggressiveInlining)]` 7 | attribute, but with the attribute they are optimized. 8 | 9 | Although the cached version achieves similar performance as the inlined 10 | methods without the need of `[MethodImpl(MethodImplOptions.AggressiveInlining)]` 11 | it has memory overhead that the others do not. 12 | 13 | ``` ini 14 | 15 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1139 (1909/November2018Update/19H2) 16 | Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores 17 | .NET Core SDK=5.0.100-rc.2.20479.15 18 | [Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.47505, CoreFX 5.0.20.47505), X64 RyuJIT 19 | DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.47505, CoreFX 5.0.20.47505), X64 RyuJIT 20 | 21 | 22 | ``` 23 | | Method | Mean | Error | StdDev | 24 | |---------------------------- |----------:|----------:|----------:| 25 | | TypeOfEquals | 1.816 ns | 0.0550 ns | 0.0487 ns | 26 | | TypeOfEqualsGeneric | 16.369 ns | 0.1464 ns | 0.1222 ns | 27 | | TypeOfEqualsGenericInlined | 1.920 ns | 0.0667 ns | 0.1114 ns | 28 | | StructWrappedIs | 16.188 ns | 0.1151 ns | 0.0961 ns | 29 | | StructWrappedIsInlined | 1.954 ns | 0.0520 ns | 0.0461 ns | 30 | | GetTypeEqualsGeneric | 36.403 ns | 0.5152 ns | 0.4820 ns | 31 | | GetTypeEqualsGenericInlined | 16.548 ns | 0.2444 ns | 0.2286 ns | 32 | | CachedTypeEquality | 1.936 ns | 0.0208 ns | 0.0185 ns | 33 | -------------------------------------------------------------------------------- /Benchmarks/TypeEqualityChecking/TypeEqualityChecking.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | True 11 | True 12 | TypeEqualityCheckingBenchmarks.tt 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | TextTemplatingFileGenerator 23 | TypeEqualityCheckingBenchmarks.cs 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | True 34 | True 35 | TypeEqualityCheckingBenchmarks.tt 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Benchmarks/TypeEqualityChecking/TypeEqualityCheckingBenchmarks.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from the "TypeEqualityCheckingBenchmarks.tt" T4 Text Template. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using BenchmarkDotNet.Attributes; 8 | using System.Runtime.CompilerServices; 9 | 10 | namespace TypeEqualityChecking 11 | { 12 | public class TypeEqualityCheckingBenchmarks 13 | { 14 | bool temp; 15 | 16 | [Benchmark] 17 | public void TypeOfEquals() 18 | { 19 | // true checks 20 | temp = typeof(string) == typeof(string); 21 | temp = typeof(int) == typeof(int); 22 | temp = typeof(double) == typeof(double); 23 | temp = typeof(decimal) == typeof(decimal); 24 | temp = typeof(byte) == typeof(byte); 25 | temp = typeof(short) == typeof(short); 26 | 27 | // false checks 28 | temp = typeof(string) == typeof(int); 29 | temp = typeof(int) == typeof(string); 30 | temp = typeof(double) == typeof(int); 31 | temp = typeof(decimal) == typeof(int); 32 | temp = typeof(byte) == typeof(int); 33 | temp = typeof(short) == typeof(int); 34 | } 35 | 36 | static bool TypeOfEqualsGeneric() => typeof(A) == typeof(B); 37 | 38 | [Benchmark] 39 | public void TypeOfEqualsGeneric() 40 | { 41 | // true checks 42 | temp = TypeOfEqualsGeneric(); 43 | temp = TypeOfEqualsGeneric(); 44 | temp = TypeOfEqualsGeneric(); 45 | temp = TypeOfEqualsGeneric(); 46 | temp = TypeOfEqualsGeneric(); 47 | temp = TypeOfEqualsGeneric(); 48 | 49 | // false checks 50 | temp = TypeOfEqualsGeneric(); 51 | temp = TypeOfEqualsGeneric(); 52 | temp = TypeOfEqualsGeneric(); 53 | temp = TypeOfEqualsGeneric(); 54 | temp = TypeOfEqualsGeneric(); 55 | temp = TypeOfEqualsGeneric(); 56 | } 57 | 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | static bool TypeOfEqualsGenericInlined() => typeof(A) == typeof(B); 60 | 61 | [Benchmark] 62 | public void TypeOfEqualsGenericInlined() 63 | { 64 | // true checks 65 | temp = TypeOfEqualsGenericInlined(); 66 | temp = TypeOfEqualsGenericInlined(); 67 | temp = TypeOfEqualsGenericInlined(); 68 | temp = TypeOfEqualsGenericInlined(); 69 | temp = TypeOfEqualsGenericInlined(); 70 | temp = TypeOfEqualsGenericInlined(); 71 | 72 | // false checks 73 | temp = TypeOfEqualsGenericInlined(); 74 | temp = TypeOfEqualsGenericInlined(); 75 | temp = TypeOfEqualsGenericInlined(); 76 | temp = TypeOfEqualsGenericInlined(); 77 | temp = TypeOfEqualsGenericInlined(); 78 | temp = TypeOfEqualsGenericInlined(); 79 | } 80 | 81 | static bool Operator_is() => default((A, B)) is (A, A); 82 | 83 | [Benchmark] 84 | public void StructWrappedIs() 85 | { 86 | // true checks 87 | temp = Operator_is(); 88 | temp = Operator_is(); 89 | temp = Operator_is(); 90 | temp = Operator_is(); 91 | temp = Operator_is(); 92 | temp = Operator_is(); 93 | 94 | // false checks 95 | temp = Operator_is(); 96 | temp = Operator_is(); 97 | temp = Operator_is(); 98 | temp = Operator_is(); 99 | temp = Operator_is(); 100 | temp = Operator_is(); 101 | } 102 | 103 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 104 | static bool Operator_is_inlined() => default((A, B)) is (A, A); 105 | 106 | [Benchmark] 107 | public void StructWrappedIsInlined() 108 | { 109 | // true checks 110 | temp = Operator_is_inlined(); 111 | temp = Operator_is_inlined(); 112 | temp = Operator_is_inlined(); 113 | temp = Operator_is_inlined(); 114 | temp = Operator_is_inlined(); 115 | temp = Operator_is_inlined(); 116 | 117 | // false checks 118 | temp = Operator_is_inlined(); 119 | temp = Operator_is_inlined(); 120 | temp = Operator_is_inlined(); 121 | temp = Operator_is_inlined(); 122 | temp = Operator_is_inlined(); 123 | temp = Operator_is_inlined(); 124 | } 125 | 126 | static bool GetTypeEqualsGeneric() => default((A, B)).GetType() == default((A, A)).GetType(); 127 | 128 | [Benchmark] 129 | public void GetTypeEqualsGeneric() 130 | { 131 | // true checks 132 | temp = GetTypeEqualsGeneric(); 133 | temp = GetTypeEqualsGeneric(); 134 | temp = GetTypeEqualsGeneric(); 135 | temp = GetTypeEqualsGeneric(); 136 | temp = GetTypeEqualsGeneric(); 137 | temp = GetTypeEqualsGeneric(); 138 | 139 | // false checks 140 | temp = GetTypeEqualsGeneric(); 141 | temp = GetTypeEqualsGeneric(); 142 | temp = GetTypeEqualsGeneric(); 143 | temp = GetTypeEqualsGeneric(); 144 | temp = GetTypeEqualsGeneric(); 145 | temp = GetTypeEqualsGeneric(); 146 | } 147 | 148 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 149 | static bool GetTypeEqualsGenericInlined() => default((A, B)).GetType() == default((A, A)).GetType(); 150 | 151 | [Benchmark] 152 | public void GetTypeEqualsGenericInlined() 153 | { 154 | // true checks 155 | temp = GetTypeEqualsGenericInlined(); 156 | temp = GetTypeEqualsGenericInlined(); 157 | temp = GetTypeEqualsGenericInlined(); 158 | temp = GetTypeEqualsGenericInlined(); 159 | temp = GetTypeEqualsGenericInlined(); 160 | temp = GetTypeEqualsGenericInlined(); 161 | 162 | // false checks 163 | temp = GetTypeEqualsGenericInlined(); 164 | temp = GetTypeEqualsGenericInlined(); 165 | temp = GetTypeEqualsGenericInlined(); 166 | temp = GetTypeEqualsGenericInlined(); 167 | temp = GetTypeEqualsGenericInlined(); 168 | temp = GetTypeEqualsGenericInlined(); 169 | } 170 | 171 | public static class Cache 172 | { 173 | public static bool Equal = typeof(A) == typeof(B); 174 | } 175 | 176 | [Benchmark] 177 | public void CachedTypeEquality() 178 | { 179 | // true checks 180 | temp = Cache.Equal; 181 | temp = Cache.Equal; 182 | temp = Cache.Equal; 183 | temp = Cache.Equal; 184 | temp = Cache.Equal; 185 | temp = Cache.Equal; 186 | 187 | // false checks 188 | temp = Cache.Equal; 189 | temp = Cache.Equal; 190 | temp = Cache.Equal; 191 | temp = Cache.Equal; 192 | temp = Cache.Equal; 193 | temp = Cache.Equal; 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Benchmarks/TypeEqualityChecking/TypeEqualityCheckingBenchmarks.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Collections.Generic" #> 6 | <#@ output extension=".cs" #> 7 | //------------------------------------------------------------------------------ 8 | // 9 | // This code was generated from the "TypeEqualityCheckingBenchmarks.tt" T4 Text Template. 10 | // 11 | //------------------------------------------------------------------------------ 12 | <# 13 | 14 | (string A, string B)[] trueChecks = 15 | { 16 | ("string", "string"), 17 | ("int", "int"), 18 | ("double", "double"), 19 | ("decimal", "decimal"), 20 | ("byte", "byte"), 21 | ("short", "short"), 22 | }; 23 | 24 | (string A, string B)[] falseChecks = 25 | { 26 | ("string", "int"), 27 | ("int", "string"), 28 | ("double", "int"), 29 | ("decimal", "int"), 30 | ("byte", "int"), 31 | ("short", "int"), 32 | }; 33 | 34 | #> 35 | 36 | using BenchmarkDotNet.Attributes; 37 | using System.Runtime.CompilerServices; 38 | 39 | namespace TypeEqualityChecking 40 | { 41 | public class TypeEqualityCheckingBenchmarks 42 | { 43 | bool temp; 44 | 45 | [Benchmark] 46 | public void TypeOfEquals() 47 | { 48 | // true checks 49 | <# foreach (var (A, B) in trueChecks) { #> 50 | temp = typeof(<#= A #>) == typeof(<#= B #>); 51 | <# } #> 52 | 53 | // false checks 54 | <# foreach (var (A, B) in falseChecks) { #> 55 | temp = typeof(<#= A #>) == typeof(<#= B #>); 56 | <# } #> 57 | } 58 | 59 | static bool TypeOfEqualsGeneric() => typeof(A) == typeof(B); 60 | 61 | [Benchmark] 62 | public void TypeOfEqualsGeneric() 63 | { 64 | // true checks 65 | <# foreach (var (A, B) in trueChecks) { #> 66 | temp = TypeOfEqualsGeneric<<#= A #>, <#= B #>>(); 67 | <# } #> 68 | 69 | // false checks 70 | <# foreach (var (A, B) in falseChecks) { #> 71 | temp = TypeOfEqualsGeneric<<#= A #>, <#= B #>>(); 72 | <# } #> 73 | } 74 | 75 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 76 | static bool TypeOfEqualsGenericInlined() => typeof(A) == typeof(B); 77 | 78 | [Benchmark] 79 | public void TypeOfEqualsGenericInlined() 80 | { 81 | // true checks 82 | <# foreach (var (A, B) in trueChecks) { #> 83 | temp = TypeOfEqualsGenericInlined<<#= A #>, <#= B #>>(); 84 | <# } #> 85 | 86 | // false checks 87 | <# foreach (var (A, B) in falseChecks) { #> 88 | temp = TypeOfEqualsGenericInlined<<#= A #>, <#= B #>>(); 89 | <# } #> 90 | } 91 | 92 | static bool Operator_is() => default((A, B)) is (A, A); 93 | 94 | [Benchmark] 95 | public void StructWrappedIs() 96 | { 97 | // true checks 98 | <# foreach (var (A, B) in trueChecks) { #> 99 | temp = Operator_is<<#= A #>, <#= B #>>(); 100 | <# } #> 101 | 102 | // false checks 103 | <# foreach (var (A, B) in falseChecks) { #> 104 | temp = Operator_is<<#= A #>, <#= B #>>(); 105 | <# } #> 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | static bool Operator_is_inlined() => default((A, B)) is (A, A); 110 | 111 | [Benchmark] 112 | public void StructWrappedIsInlined() 113 | { 114 | // true checks 115 | <# foreach (var (A, B) in trueChecks) { #> 116 | temp = Operator_is_inlined<<#= A #>, <#= B #>>(); 117 | <# } #> 118 | 119 | // false checks 120 | <# foreach (var (A, B) in falseChecks) { #> 121 | temp = Operator_is_inlined<<#= A #>, <#= B #>>(); 122 | <# } #> 123 | } 124 | 125 | static bool GetTypeEqualsGeneric() => default((A, B)).GetType() == default((A, A)).GetType(); 126 | 127 | [Benchmark] 128 | public void GetTypeEqualsGeneric() 129 | { 130 | // true checks 131 | <# foreach (var (A, B) in trueChecks) { #> 132 | temp = GetTypeEqualsGeneric<<#= A #>, <#= B #>>(); 133 | <# } #> 134 | 135 | // false checks 136 | <# foreach (var (A, B) in falseChecks) { #> 137 | temp = GetTypeEqualsGeneric<<#= A #>, <#= B #>>(); 138 | <# } #> 139 | } 140 | 141 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 142 | static bool GetTypeEqualsGenericInlined() => default((A, B)).GetType() == default((A, A)).GetType(); 143 | 144 | [Benchmark] 145 | public void GetTypeEqualsGenericInlined() 146 | { 147 | // true checks 148 | <# foreach (var (A, B) in trueChecks) { #> 149 | temp = GetTypeEqualsGenericInlined<<#= A #>, <#= B #>>(); 150 | <# } #> 151 | 152 | // false checks 153 | <# foreach (var (A, B) in falseChecks) { #> 154 | temp = GetTypeEqualsGenericInlined<<#= A #>, <#= B #>>(); 155 | <# } #> 156 | } 157 | 158 | public static class Cache 159 | { 160 | public static bool Equal = typeof(A) == typeof(B); 161 | } 162 | 163 | [Benchmark] 164 | public void CachedTypeEquality() 165 | { 166 | // true checks 167 | <# foreach (var (A, B) in trueChecks) { #> 168 | temp = Cache<<#= A #>, <#= B #>>.Equal; 169 | <# } #> 170 | 171 | // false checks 172 | <# foreach (var (A, B) in falseChecks) { #> 173 | temp = Cache<<#= A #>, <#= B #>>.Equal; 174 | <# } #> 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Benchmarks/dotnet-benchmarks.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30406.217 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrays", "Arrays\Arrays.csproj", "{66DA2743-3D2F-469E-B71D-282C52D2F970}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MethodAbstractions", "MethodAbstractions\MethodAbstractions.csproj", "{FD30E6AB-B8DC-4665-88F7-C86C9D1F152D}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IndexedCollectionAbstractions", "IndexedCollectionAbstractions\IndexedCollectionAbstractions.csproj", "{920F4615-EB12-4D1F-BF25-CE5FACBB2747}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultipleReturnsTechniques", "MultipleReturnsTechniques\MultipleReturnsTechniques.csproj", "{D24B1C0A-3AB8-4D6C-90A4-C2EF93DE28CF}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StringFormattingTechniques", "StringFormattingTechniques\StringFormattingTechniques.csproj", "{DA088111-11D8-4507-A760-0A94B32F2658}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BranchingTechniques", "BranchingTechniques\BranchingTechniques.csproj", "{33D9064C-160D-448C-9827-BAA077CA3644}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExceptionHandlingTechniques", "ExceptionHandlingTechniques\ExceptionHandlingTechniques.csproj", "{0183986E-78D4-4C4F-AF2C-69E961A986EB}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReflectionTechniques", "ReflectionTechniques\ReflectionTechniques.csproj", "{4320629B-EB16-405E-AF83-49DA499043E9}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Boxing", "Boxing\Boxing\Boxing.csproj", "{E6C9254E-3E6A-4073-9486-C55286521575}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TypeEqualityChecking", "TypeEqualityChecking\TypeEqualityChecking.csproj", "{C37EAD36-7FBA-4F56-ACBD-D572DAF4B053}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IterationRefParametersVsReturns", "IterationRefParametersVsReturns\IterationRefParametersVsReturns.csproj", "{9A5233E6-59F3-46E5-8608-F14E67F218F8}" 27 | EndProject 28 | Global 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Release|Any CPU = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {66DA2743-3D2F-469E-B71D-282C52D2F970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {66DA2743-3D2F-469E-B71D-282C52D2F970}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {66DA2743-3D2F-469E-B71D-282C52D2F970}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {66DA2743-3D2F-469E-B71D-282C52D2F970}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {FD30E6AB-B8DC-4665-88F7-C86C9D1F152D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {FD30E6AB-B8DC-4665-88F7-C86C9D1F152D}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {FD30E6AB-B8DC-4665-88F7-C86C9D1F152D}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {FD30E6AB-B8DC-4665-88F7-C86C9D1F152D}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {920F4615-EB12-4D1F-BF25-CE5FACBB2747}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {920F4615-EB12-4D1F-BF25-CE5FACBB2747}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {920F4615-EB12-4D1F-BF25-CE5FACBB2747}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {920F4615-EB12-4D1F-BF25-CE5FACBB2747}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {D24B1C0A-3AB8-4D6C-90A4-C2EF93DE28CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {D24B1C0A-3AB8-4D6C-90A4-C2EF93DE28CF}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {D24B1C0A-3AB8-4D6C-90A4-C2EF93DE28CF}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {D24B1C0A-3AB8-4D6C-90A4-C2EF93DE28CF}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {DA088111-11D8-4507-A760-0A94B32F2658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {DA088111-11D8-4507-A760-0A94B32F2658}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {DA088111-11D8-4507-A760-0A94B32F2658}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {DA088111-11D8-4507-A760-0A94B32F2658}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {33D9064C-160D-448C-9827-BAA077CA3644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {33D9064C-160D-448C-9827-BAA077CA3644}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {33D9064C-160D-448C-9827-BAA077CA3644}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {33D9064C-160D-448C-9827-BAA077CA3644}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {0183986E-78D4-4C4F-AF2C-69E961A986EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {0183986E-78D4-4C4F-AF2C-69E961A986EB}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {0183986E-78D4-4C4F-AF2C-69E961A986EB}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {0183986E-78D4-4C4F-AF2C-69E961A986EB}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {4320629B-EB16-405E-AF83-49DA499043E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {4320629B-EB16-405E-AF83-49DA499043E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {4320629B-EB16-405E-AF83-49DA499043E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {4320629B-EB16-405E-AF83-49DA499043E9}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {E6C9254E-3E6A-4073-9486-C55286521575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 67 | {E6C9254E-3E6A-4073-9486-C55286521575}.Debug|Any CPU.Build.0 = Debug|Any CPU 68 | {E6C9254E-3E6A-4073-9486-C55286521575}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {E6C9254E-3E6A-4073-9486-C55286521575}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {C37EAD36-7FBA-4F56-ACBD-D572DAF4B053}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {C37EAD36-7FBA-4F56-ACBD-D572DAF4B053}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {C37EAD36-7FBA-4F56-ACBD-D572DAF4B053}.Release|Any CPU.ActiveCfg = Release|Any CPU 73 | {C37EAD36-7FBA-4F56-ACBD-D572DAF4B053}.Release|Any CPU.Build.0 = Release|Any CPU 74 | {9A5233E6-59F3-46E5-8608-F14E67F218F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 75 | {9A5233E6-59F3-46E5-8608-F14E67F218F8}.Debug|Any CPU.Build.0 = Debug|Any CPU 76 | {9A5233E6-59F3-46E5-8608-F14E67F218F8}.Release|Any CPU.ActiveCfg = Release|Any CPU 77 | {9A5233E6-59F3-46E5-8608-F14E67F218F8}.Release|Any CPU.Build.0 = Release|Any CPU 78 | EndGlobalSection 79 | GlobalSection(SolutionProperties) = preSolution 80 | HideSolutionNode = FALSE 81 | EndGlobalSection 82 | GlobalSection(ExtensibilityGlobals) = postSolution 83 | SolutionGuid = {B7CBE509-CBFA-484C-8247-92A07721B1A4} 84 | EndGlobalSection 85 | EndGlobal 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Angouri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Discord](https://img.shields.io/discord/642350046213439489?color=orange&label=Discord)](https://discord.gg/YWJEX7a) 2 | [![GitHub](https://img.shields.io/github/license/asc-community/dotnet-benchmarks)](./LICENSE) 3 | ![Built by community](https://img.shields.io/badge/Built%20by-Community-blue) 4 | 5 | ## Benchmark Almanac for .NET 6 | 7 | In this repo we are collecting benchmarks for different situations, as well as 8 | some thoughts and articles for developers about improving their software's performance. 9 | We hope that this project may become a useful reference for people. 10 | 11 | ### Benchmarks 12 | 13 | [![Benchmarks](https://img.shields.io/badge/Go%20to-Benchmarks-blueviolet)](./Benchmarks) 14 | 15 | Here you can see all benchmarks collected for the Almanac. The idea 16 | is to consider cases where the developer does not expected a performance issue. The current 17 | public API and the approach programmers follow doesn't let us write a very fast code, 18 | so we have to know all those issues. 19 | 20 | Most programmers who care about performance would either look at the JIT's generated 21 | assembler to see where the issues can come from, or write their own benchmarks to 22 | test different cases. 23 | 24 | Here we collect all those benchmarks in one repository, which is and will be open for everyone. 25 | Not only that, we highly encourage the community, you including, to try to add your 26 | benchmark too. 27 | 28 | To avoid the mess created by so many benchmarks, we started writing pieces of articles 29 | and advices for developers who don't want to spend a day on investigating the benchmarks. 30 | 31 | ### Articles 32 | 33 | [![Articles](https://img.shields.io/badge/Go%20to-Articles-blueviolet)](./Articles) 34 | 35 | Benchmarks tell us truth, but one could need a quick tip instead. That's why there are 36 | some articles that may help you to get through these benchmarks. 37 | 38 | As anything else on Earth, they may contain mistakes, that is why it might be 39 | safer to rely on benchmarks rather than articles that are based on them. However, 40 | why not to try the tip form the article and see for yourself whether it works or not? 41 | 42 | As well as benchmarks, it is highly encouraged to help us with those articles. All the content 43 | from the repo is open and exposed to any healthy contributions. 44 | 45 | ### Contribution 46 | 47 | If you want to add a new benchmark, either create a new folder for it, or add a file to 48 | an existing one, if your benchmark fits an existing category. 49 | 50 | If you want to add an article, just go ahead, so far we don't have any recommendations on it, 51 | we will see how it goes :). 52 | 53 | Any other contribution is welcomed as well! 54 | --------------------------------------------------------------------------------