├── .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]