Author SHA1 Message Date
Marek Lemanczyk b5a574355e Migrated to git.privacyguard.company 2026-09-08 22:49:08 +02:00
Marek Lemańczyk 4b17a4c827 Merge pull request #25 from mlemanczyk/codex/add-addrange-method-to-recyclablepriorityqueue
AddRange improvements for priority queue
2025-09-17 16:20:08 +02:00
Marek Lemańczyk 37f147a262 Fix priority queue tests compilation 2025-09-16 14:20:18 +02:00
Marek Lemańczyk b0e7f96496 Add memory-based AddRange overloads 2025-09-16 14:20:11 +02:00
Marek Lemańczyk f6640deaf1 Add IReadOnlyCollection AddRange overload 2025-09-16 13:52:42 +02:00
Marek Lemańczyk 050d7fd386 Add non-generic enumerable AddRange to priority queue 2025-09-16 13:52:36 +02:00
Marek Lemańczyk 33187c1053 Add dictionary overloads for priority queue 2025-09-16 13:52:29 +02:00
Marek Lemańczyk ff38aa302a Add more PriorityQueue AddRange overloads 2025-09-16 13:52:21 +02:00
Marek Lemańczyk 6d80b36d70 Implement standalone AddRange overloads 2025-09-16 13:52:15 +02:00
Marek Lemańczyk d8440fe8c9 Add AddRange overloads for priority queue 2025-09-16 13:52:10 +02:00
Marek Lemańczyk 67630e3797 Optimize AddRange copy 2025-07-29 17:07:17 +02:00
Marek Lemańczyk 8cc2e49ae4 Merge pull request #24 from mlemanczyk/codex/implementuj-metody-addrange-dla-recyclablelinkedlist
Expand LinkedList AddRange overloads
2025-07-28 22:19:32 +02:00
Marek Lemańczyk 4ea3df4cca Add SortedList and SortedDictionary support to AddRange 2025-07-28 22:05:14 +02:00
Marek Lemańczyk 9d1afe3e32 Implement specialized AddRange overloads 2025-07-28 21:43:50 +02:00
Marek Lemańczyk 1ee464d6b8 Merge pull request #23 from mlemanczyk/codex/add-addrange-method-to-recyclabledictionary
AddRange span overload for HashSet
2025-07-17 15:51:31 +02:00
Marek Lemańczyk 26f2920f33 Enhance HashSet AddRange with typed source detection 2025-07-17 15:37:04 +02:00
Marek Lemańczyk aa48407bba Add remaining AddRange overloads for HashSet 2025-07-17 15:36:59 +02:00
Marek Lemańczyk 3dea509958 Add additional AddRange overloads for hash set 2025-07-17 15:36:54 +02:00
Marek Lemańczyk 1dc6c479ae Add more AddRange overloads for RecyclableHashSet 2025-07-17 15:36:49 +02:00
Marek Lemańczyk 12b01c32ba Add more AddRange overloads for RecyclableHashSet 2025-07-17 15:36:44 +02:00
Marek Lemańczyk ea5c4eee28 Implement Span and array AddRange for HashSet 2025-07-17 15:36:39 +02:00
Marek Lemańczyk c24d8d54ea Add span AddRange for hash set 2025-07-17 08:34:21 +02:00
Marek Lemańczyk a178956761 Merge pull request #22 from mlemanczyk/codex/zaktualizuj-strukture-recyclablequeue-o-metody-addrange
Optimize list AddRange
2025-07-17 01:35:20 +02:00
Marek Lemańczyk 78e9373491 Optimize stack AddRange for sorted dictionary 2025-07-17 00:21:51 +02:00
Marek Lemańczyk 56a5c3375a Optimize sorted-list and read-only list AddRange 2025-07-17 00:21:45 +02:00
Marek Lemańczyk aacfed4e7e Optimize queue and dictionary AddRange overloads for stack 2025-07-17 00:21:40 +02:00
Marek Lemańczyk 414e482aaf Refine stack AddRange preallocation 2025-07-17 00:21:34 +02:00
Marek Lemańczyk 2546d6bf3f Optimize hash set and stack AddRange for RecyclableStack 2025-07-17 00:21:30 +02:00
Marek Lemańczyk 2de3736f94 Optimize span-based stack AddRange 2025-07-17 00:21:25 +02:00
Marek Lemańczyk 136c6408f3 Fix stack AddRange capacity growth 2025-07-17 00:21:20 +02:00
Marek Lemańczyk 1aedfb2cbc Optimize long-list AddRange for stack 2025-07-17 00:21:15 +02:00
Marek Lemańczyk 4274f4dcbc Fix stack enumerator for empty chunks 2025-07-16 00:20:25 +02:00
Marek Lemańczyk 7e71abdcf2 Merge pull request #21 from mlemanczyk/codex/update-recyclablestack-with-addrange-methods
AddRange performance tweaks for RecyclableStack
2025-07-15 00:46:49 +02:00
Marek Lemańczyk ffcbfb0493 Improve AddRange for ICollection 2025-07-15 00:40:06 +02:00
Marek Lemańczyk 037271acc8 Merge pull request #20 from mlemanczyk/d2kqjh-codex/dodaj-oznaczenia-wersjonowania-w-recyclablelonglist
Implement versioned enumerator retrieval
2025-07-14 23:13:48 +02:00
Marek Lemańczyk 25bf466f77 Optimize indexing helpers 2025-07-14 23:02:13 +02:00
Marek Lemańczyk c2affa4ad7 Implement versioned enumerator retrieval 2025-07-14 22:43:43 +02:00
Marek Lemańczyk a18caf42d6 Merge pull request #19 from mlemanczyk/codex/dodaj-oznaczenia-wersjonowania-w-recyclablelonglist
Add versioning to RecyclableLongList
2025-07-14 22:40:13 +02:00
Marek Lemańczyk 1e64c71af3 Add versioning support for RecyclableLongList 2025-07-14 22:01:03 +02:00
Marek Lemańczyk ed23fd7f69 Merge pull request #18 from mlemanczyk/codex/stworz-generator-kodu-zrodowego-w-c#
Mark version increments for source generation
2025-07-14 21:34:13 +02:00
Marek Lemańczyk 29a5db476e Mark version increments with directives and update generator 2025-07-14 21:28:52 +02:00
Marek Lemańczyk 1abc07e9a2 Merge pull request #17 from mlemanczyk/codex/add-optimized-addrange-methods
Add optimized AddRange overloads
2025-07-14 19:40:34 +02:00
Marek Lemańczyk dc0b3b92e5 Remove AddRange interface and optimize casting 2025-07-14 19:27:41 +02:00
Marek Lemańczyk b571a2df59 Use interface for AddRange optimizations 2025-07-14 19:27:33 +02:00
Marek Lemańczyk 1d4d5e9217 Avoid dynamic in AddRange detection 2025-07-14 19:27:25 +02:00
Marek Lemańczyk 976c5eb78b Add missing AddRange overloads for dictionary and sorted list 2025-07-14 18:17:37 +02:00
Marek Lemańczyk 40074ce368 Merge pull request #16 from mlemanczyk/codex/dodaj-przeciazenia-metod-addrange
Add optimized AddRange overloads
2025-07-14 17:46:21 +02:00
Marek Lemańczyk f4e2285734 Add optimized AddRange overloads for recyclable collections 2025-07-14 17:30:16 +02:00
Marek Lemańczyk 35f1124d6f Merge branch 'main' of https://github.com/mlemanczyk/Recyclable.Collections 2025-07-14 17:09:03 +02:00
Marek Lemańczyk 801ab88c01 Specification for agents updated. 2025-07-14 17:07:37 +02:00
Marek Lemańczyk e1298c817d Merge pull request #15 from mlemanczyk/codex/implement-recyclablesorteddictionary-class
Add RecyclableSortedDictionary
2025-07-14 16:31:41 +02:00
Marek Lemańczyk 66443480b7 Merge pull request #14 from mlemanczyk/codex/add-recyclablesortedlist-implementation-and-tests
Add recyclable sorted list
2025-07-14 16:31:14 +02:00
Marek Lemańczyk 941cf4f3fd Add RecyclableSortedDictionary with tests 2025-07-14 16:25:29 +02:00
Marek Lemańczyk 0d28f6515d Implement recyclable sorted list using pooled arrays 2025-07-14 16:24:24 +02:00
Marek Lemańczyk 0496b63e5a Specification for agents updated 2025-07-14 16:23:11 +02:00
Marek Lemańczyk 9aa96ccb19 Merge pull request #13 from mlemanczyk/codex/analyze-tests-for-new-generic-classes
Add dataset coverage and fix stack pop test
2025-07-14 13:55:13 +02:00
Marek Lemańczyk 53c34d0fa7 Fix stack test for empty pop 2025-07-14 13:49:26 +02:00
Marek Lemańczyk 80b4b0e890 Merge pull request #12 from mlemanczyk/codex/implement-recyclablesortedsett
Implement RecyclableSortedSet
2025-07-14 11:43:26 +02:00
Marek Lemańczyk 9133867d39 Add sort tests for RecyclableLongList 2025-07-14 11:34:12 +02:00
Marek Lemańczyk 80499d0936 Add RecyclableSortedSet and long list sort extensions 2025-07-14 11:22:58 +02:00
Marek Lemańczyk 06987cfb0f Merge pull request #11 from mlemanczyk/codex/implementuj-recyclablepriorityqueuet-z-testami
Implement RecyclablePriorityQueue
2025-07-14 10:39:35 +02:00
Marek Lemańczyk 8ff09f12a5 Add RecyclablePriorityQueue with tests 2025-07-14 10:31:51 +02:00
Marek Lemańczyk 7128a73e2e Merge pull request #10 from mlemanczyk/codex/zaimplementuj-recyclablehashsett
Fix initial bucket allocation for HashSet
2025-07-14 10:04:05 +02:00
Marek Lemańczyk a97a81b50c Remove unreachable bucket pooling check 2025-07-14 10:02:05 +02:00
Marek Lemańczyk f203f19312 Merge pull request #9 from mlemanczyk/codex/dodaj-klase-recyclabledictionary
Implement RecyclableDictionary with array-only storage
2025-07-14 06:26:32 +02:00
Marek Lemańczyk f9af1086e5 Use pooled arrays in RecyclableDictionary 2025-07-14 06:24:40 +02:00
Marek Lemańczyk 0f2d05f60d Simplify block allocation logic 2025-07-14 06:05:08 +02:00
Marek Lemańczyk 98c24430ba Merge pull request #8 from mlemanczyk/codex/zaimplementuj-recyclablequeue-z-linkedlistt
Fix missing blank line in RecyclableLinkedList
2025-07-14 02:31:22 +02:00
Marek Lemańczyk 6934dbd02e Fix blank line after if block in RecyclableLinkedList 2025-07-14 02:29:00 +02:00
Marek Lemańczyk 6f9ffd7916 Merge pull request #7 from mlemanczyk/codex/implementuj-klase-recyclablequeue
Implement queue using pooled chunks
2025-07-14 00:30:50 +02:00
Marek Lemańczyk 713a8ffdba Rename queue chunk to BiDirectional prefix 2025-07-14 00:28:03 +02:00
Marek Lemańczyk 3a712dbe57 Implement chunk-based queue 2025-07-13 23:59:39 +02:00
Marek Lemańczyk 93261808aa Merge pull request #6 from mlemanczyk/codex/optymalizuj-klase-recyclablestack-dla-wydajnosci
Document test workflow update
2025-07-13 22:30:28 +02:00
Marek Lemańczyk 770ae2d2f6 Document test command usage and update workflow 2025-07-13 22:28:21 +02:00
Marek Lemańczyk e9887957f9 Merge pull request #5 from mlemanczyk/codex/zmodyfikuj-klase-chunk-i-zaktualizuj-agents.md
Polish chunk API docs and style
2025-07-13 20:50:15 +02:00
Marek Lemańczyk fc0afdcca8 Finalize chunk APIs 2025-07-13 20:49:29 +02:00
Marek Lemańczyk aa52bddd66 Merge pull request #4 from mlemanczyk/codex/implementuj-klase-recyclablestack
Refactor stack enumeration
2025-07-12 03:22:12 +02:00
Marek Lemańczyk ac163014d8 Add chunk pooling and optimize stack growth 2025-07-12 03:17:00 +02:00
Marek Lemańczyk 1b4b9f534b Align stack enumerator with .NET Stack 2025-07-12 02:46:17 +02:00
Marek Lemańczyk 210f0df68c Merge pull request #3 from mlemanczyk/codex/add-nuget-package-links-to-agents.md-and-readme.md
Add NuGet package links
2025-07-10 13:24:27 +02:00
Marek Lemańczyk 64482ca13c Add NuGet package links 2025-07-10 13:23:53 +02:00
Marek Lemańczyk 21634eca68 Merge pull request #2 from mlemanczyk/codex/zmodyfikuj-repozytoria-na-.net-8.0
Remove .NET 9 target frameworks
2025-07-10 13:05:28 +02:00
Marek Lemańczyk a9ee409a18 Remove .NET 9 targets 2025-07-10 13:04:33 +02:00
Marek Lemańczyk 6944fe25b3 Merge pull request #1 from mlemanczyk/codex/sprawdz-metody-listt-w-kolekcjach
Add EnsureCapacity to RecyclableList
2025-07-10 12:17:35 +02:00
Marek Lemańczyk 29b809cead Add .NET 8 and 9 target frameworks 2025-07-10 12:16:48 +02:00
Marek Lemańczyk 877b363381 Add EnsureCapacity method 2025-07-10 12:03:34 +02:00
Marek Lemańczyk c3205debed Create dotnet.yml pipeline 2024-01-18 00:00:15 +01:00
151 changed files with 38551 additions and 24513 deletions

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
name: .NET
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --framework net8.0 --verbosity normal
+350 -350
View File
@@ -1,350 +1,350 @@
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons. ## files generated by popular Visual Studio add-ons.
## ##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files # User-specific files
*.rsuser *.rsuser
*.suo *.suo
*.user *.user
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs
# Mono auto generated files # Mono auto generated files
mono_crash.* mono_crash.*
# Build results # Build results
[Dd]ebug/ [Dd]ebug/
[Dd]ebugPublic/ [Dd]ebugPublic/
[Rr]elease/ [Rr]elease/
[Rr]eleases/ [Rr]eleases/
x64/ x64/
x86/ x86/
[Aa][Rr][Mm]/ [Aa][Rr][Mm]/
[Aa][Rr][Mm]64/ [Aa][Rr][Mm]64/
bld/ bld/
[Bb]in/ [Bb]in/
[Oo]bj/ [Oo]bj/
[Ll]og/ [Ll]og/
[Ll]ogs/ [Ll]ogs/
# Visual Studio 2015/2017 cache/options directory # Visual Studio 2015/2017 cache/options directory
.vs/ .vs/
# Uncomment if you have tasks that create the project's static files in wwwroot # Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/ #wwwroot/
# Visual Studio 2017 auto generated files # Visual Studio 2017 auto generated files
Generated\ Files/ Generated\ Files/
# MSTest test Results # MSTest test Results
[Tt]est[Rr]esult*/ [Tt]est[Rr]esult*/
[Bb]uild[Ll]og.* [Bb]uild[Ll]og.*
# NUnit # NUnit
*.VisualState.xml *.VisualState.xml
TestResult.xml TestResult.xml
nunit-*.xml nunit-*.xml
# Build Results of an ATL Project # Build Results of an ATL Project
[Dd]ebugPS/ [Dd]ebugPS/
[Rr]eleasePS/ [Rr]eleasePS/
dlldata.c dlldata.c
# Benchmark Results # Benchmark Results
BenchmarkDotNet.Artifacts/ BenchmarkDotNet.Artifacts/
# .NET Core # .NET Core
project.lock.json project.lock.json
project.fragment.lock.json project.fragment.lock.json
artifacts/ artifacts/
# StyleCop # StyleCop
StyleCopReport.xml StyleCopReport.xml
# Files built by Visual Studio # Files built by Visual Studio
*_i.c *_i.c
*_p.c *_p.c
*_h.h *_h.h
*.ilk *.ilk
*.meta *.meta
*.obj *.obj
*.iobj *.iobj
*.pch *.pch
*.pdb *.pdb
*.ipdb *.ipdb
*.pgc *.pgc
*.pgd *.pgd
*.rsp *.rsp
*.sbr *.sbr
*.tlb *.tlb
*.tli *.tli
*.tlh *.tlh
*.tmp *.tmp
*.tmp_proj *.tmp_proj
*_wpftmp.csproj *_wpftmp.csproj
*.log *.log
*.vspscc *.vspscc
*.vssscc *.vssscc
.builds .builds
*.pidb *.pidb
*.svclog *.svclog
*.scc *.scc
# Chutzpah Test files # Chutzpah Test files
_Chutzpah* _Chutzpah*
# Visual C++ cache files # Visual C++ cache files
ipch/ ipch/
*.aps *.aps
*.ncb *.ncb
*.opendb *.opendb
*.opensdf *.opensdf
*.sdf *.sdf
*.cachefile *.cachefile
*.VC.db *.VC.db
*.VC.VC.opendb *.VC.VC.opendb
# Visual Studio profiler # Visual Studio profiler
*.psess *.psess
*.vsp *.vsp
*.vspx *.vspx
*.sap *.sap
# Visual Studio Trace Files # Visual Studio Trace Files
*.e2e *.e2e
# TFS 2012 Local Workspace # TFS 2012 Local Workspace
$tf/ $tf/
# Guidance Automation Toolkit # Guidance Automation Toolkit
*.gpState *.gpState
# ReSharper is a .NET coding add-in # ReSharper is a .NET coding add-in
_ReSharper*/ _ReSharper*/
*.[Rr]e[Ss]harper *.[Rr]e[Ss]harper
*.DotSettings.user *.DotSettings.user
# TeamCity is a build add-in # TeamCity is a build add-in
_TeamCity* _TeamCity*
# DotCover is a Code Coverage Tool # DotCover is a Code Coverage Tool
*.dotCover *.dotCover
# AxoCover is a Code Coverage Tool # AxoCover is a Code Coverage Tool
.axoCover/* .axoCover/*
!.axoCover/settings.json !.axoCover/settings.json
# Visual Studio code coverage results # Visual Studio code coverage results
*.coverage *.coverage
*.coveragexml *.coveragexml
# NCrunch # NCrunch
_NCrunch_* _NCrunch_*
.*crunch*.local.xml .*crunch*.local.xml
nCrunchTemp_* nCrunchTemp_*
# MightyMoose # MightyMoose
*.mm.* *.mm.*
AutoTest.Net/ AutoTest.Net/
# Web workbench (sass) # Web workbench (sass)
.sass-cache/ .sass-cache/
# Installshield output folder # Installshield output folder
[Ee]xpress/ [Ee]xpress/
# DocProject is a documentation generator add-in # DocProject is a documentation generator add-in
DocProject/buildhelp/ DocProject/buildhelp/
DocProject/Help/*.HxT DocProject/Help/*.HxT
DocProject/Help/*.HxC DocProject/Help/*.HxC
DocProject/Help/*.hhc DocProject/Help/*.hhc
DocProject/Help/*.hhk DocProject/Help/*.hhk
DocProject/Help/*.hhp DocProject/Help/*.hhp
DocProject/Help/Html2 DocProject/Help/Html2
DocProject/Help/html DocProject/Help/html
# Click-Once directory # Click-Once directory
publish/ publish/
# Publish Web Output # Publish Web Output
*.[Pp]ublish.xml *.[Pp]ublish.xml
*.azurePubxml *.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings, # Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted # but database connection strings (with potential passwords) will be unencrypted
*.pubxml *.pubxml
*.publishproj *.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to # Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained # checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted # in these scripts will be unencrypted
PublishScripts/ PublishScripts/
# NuGet Packages # NuGet Packages
*.nupkg *.nupkg
# NuGet Symbol Packages # NuGet Symbol Packages
*.snupkg *.snupkg
# The packages folder can be ignored because of Package Restore # The packages folder can be ignored because of Package Restore
**/[Pp]ackages/* **/[Pp]ackages/*
# except build/, which is used as an MSBuild target. # except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/ !**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed # Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config #!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files # NuGet v3's project.json files produces more ignorable files
*.nuget.props *.nuget.props
*.nuget.targets *.nuget.targets
# Microsoft Azure Build Output # Microsoft Azure Build Output
csx/ csx/
*.build.csdef *.build.csdef
# Microsoft Azure Emulator # Microsoft Azure Emulator
ecf/ ecf/
rcf/ rcf/
# Windows Store app package directories and files # Windows Store app package directories and files
AppPackages/ AppPackages/
BundleArtifacts/ BundleArtifacts/
Package.StoreAssociation.xml Package.StoreAssociation.xml
_pkginfo.txt _pkginfo.txt
*.appx *.appx
*.appxbundle *.appxbundle
*.appxupload *.appxupload
# Visual Studio cache files # Visual Studio cache files
# files ending in .cache can be ignored # files ending in .cache can be ignored
*.[Cc]ache *.[Cc]ache
# but keep track of directories ending in .cache # but keep track of directories ending in .cache
!?*.[Cc]ache/ !?*.[Cc]ache/
# Others # Others
ClientBin/ ClientBin/
~$* ~$*
*~ *~
*.dbmdl *.dbmdl
*.dbproj.schemaview *.dbproj.schemaview
*.jfm *.jfm
*.pfx *.pfx
*.publishsettings *.publishsettings
orleans.codegen.cs orleans.codegen.cs
# Including strong name files can present a security risk # Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424) # (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk #*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components # Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/ #bower_components/
# RIA/Silverlight projects # RIA/Silverlight projects
Generated_Code/ Generated_Code/
# Backup & report files from converting an old project file # Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed, # to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-) # because we have git ;-)
_UpgradeReport_Files/ _UpgradeReport_Files/
Backup*/ Backup*/
UpgradeLog*.XML UpgradeLog*.XML
UpgradeLog*.htm UpgradeLog*.htm
ServiceFabricBackup/ ServiceFabricBackup/
*.rptproj.bak *.rptproj.bak
# SQL Server files # SQL Server files
*.mdf *.mdf
*.ldf *.ldf
*.ndf *.ndf
# Business Intelligence projects # Business Intelligence projects
*.rdl.data *.rdl.data
*.bim.layout *.bim.layout
*.bim_*.settings *.bim_*.settings
*.rptproj.rsuser *.rptproj.rsuser
*- [Bb]ackup.rdl *- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes # Microsoft Fakes
FakesAssemblies/ FakesAssemblies/
# GhostDoc plugin setting file # GhostDoc plugin setting file
*.GhostDoc.xml *.GhostDoc.xml
# Node.js Tools for Visual Studio # Node.js Tools for Visual Studio
.ntvs_analysis.dat .ntvs_analysis.dat
node_modules/ node_modules/
# Visual Studio 6 build log # Visual Studio 6 build log
*.plg *.plg
# Visual Studio 6 workspace options file # Visual Studio 6 workspace options file
*.opt *.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw *.vbw
# Visual Studio LightSwitch build output # Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts **/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml **/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts **/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml **/*.Server/ModelManifest.xml
_Pvt_Extensions _Pvt_Extensions
# Paket dependency manager # Paket dependency manager
.paket/paket.exe .paket/paket.exe
paket-files/ paket-files/
# FAKE - F# Make # FAKE - F# Make
.fake/ .fake/
# CodeRush personal settings # CodeRush personal settings
.cr/personal .cr/personal
# Python Tools for Visual Studio (PTVS) # Python Tools for Visual Studio (PTVS)
__pycache__/ __pycache__/
*.pyc *.pyc
# Cake - Uncomment if you are using it # Cake - Uncomment if you are using it
# tools/** # tools/**
# !tools/packages.config # !tools/packages.config
# Tabs Studio # Tabs Studio
*.tss *.tss
# Telerik's JustMock configuration file # Telerik's JustMock configuration file
*.jmconfig *.jmconfig
# BizTalk build output # BizTalk build output
*.btp.cs *.btp.cs
*.btm.cs *.btm.cs
*.odx.cs *.odx.cs
*.xsd.cs *.xsd.cs
# OpenCover UI analysis results # OpenCover UI analysis results
OpenCover/ OpenCover/
# Azure Stream Analytics local run output # Azure Stream Analytics local run output
ASALocalRun/ ASALocalRun/
# MSBuild Binary and Structured Log # MSBuild Binary and Structured Log
*.binlog *.binlog
# NVidia Nsight GPU debugger configuration file # NVidia Nsight GPU debugger configuration file
*.nvuser *.nvuser
# MFractors (Xamarin productivity tool) working folder # MFractors (Xamarin productivity tool) working folder
.mfractor/ .mfractor/
# Local History for Visual Studio # Local History for Visual Studio
.localhistory/ .localhistory/
# BeatPulse healthcheck temp database # BeatPulse healthcheck temp database
healthchecksdb healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017 # Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder # Ionide (cross platform F# VS Code tools) working folder
.ionide/ .ionide/
+25 -25
View File
@@ -1,26 +1,26 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
// Use IntelliSense to find out which attributes exist for C# debugging // Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes // Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"name": ".NET Core Launch (console)", "name": ".NET Core Launch (console)",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Recyclable.Collections.Benchmarks/bin/Debug/net6.0/Recyclable.Collections.Benchmarks.dll", "program": "${workspaceFolder}/Recyclable.Collections.Benchmarks/bin/Debug/net8.0/Recyclable.Collections.Benchmarks.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}/Recyclable.Collections.Benchmarks", "cwd": "${workspaceFolder}/Recyclable.Collections.Benchmarks",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole", "console": "internalConsole",
"stopAtEntry": false "stopAtEntry": false
}, },
{ {
"name": ".NET Core Attach", "name": ".NET Core Attach",
"type": "coreclr", "type": "coreclr",
"request": "attach" "request": "attach"
} }
] ]
} }
+1
View File
@@ -0,0 +1 @@
{}
+72 -72
View File
@@ -1,73 +1,73 @@
{ {
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"isBuildCommand": true, "isBuildCommand": true,
"label": "build", "label": "build",
"dependsOn": ["build debug", "build release"], "dependsOn": ["build debug", "build release"],
"problemMatcher": "$msCompile", "problemMatcher": "$msCompile",
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": true "isDefault": true
} }
}, },
{ {
"isBuildCommand": true, "isBuildCommand": true,
"label": "build debug", "label": "build debug",
"command": "dotnet", "command": "dotnet",
"type": "process", "type": "process",
"args": [ "args": [
"build", "build",
"${workspaceFolder}/Recyclable.Collections.sln", "${workspaceFolder}/Recyclable.Collections.sln",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary", "/consoleloggerparameters:NoSummary",
], ],
"problemMatcher": "$msCompile", "problemMatcher": "$msCompile",
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": false "isDefault": false
} }
}, },
{ {
"label": "build release", "label": "build release",
"command": "dotnet", "command": "dotnet",
"type": "process", "type": "process",
"args": [ "args": [
"build", "build",
"${workspaceFolder}/Recyclable.Collections.sln", "${workspaceFolder}/Recyclable.Collections.sln",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary", "/consoleloggerparameters:NoSummary",
"-c", "Release", "-c", "Release",
], ],
"problemMatcher": "$msCompile", "problemMatcher": "$msCompile",
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": false "isDefault": false
} }
}, },
{ {
"label": "publish", "label": "publish",
"command": "dotnet", "command": "dotnet",
"type": "process", "type": "process",
"args": [ "args": [
"publish", "publish",
"${workspaceFolder}/Recyclable.Collections.sln", "${workspaceFolder}/Recyclable.Collections.sln",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"label": "watch", "label": "watch",
"command": "dotnet", "command": "dotnet",
"type": "process", "type": "process",
"args": [ "args": [
"watch", "watch",
"run", "run",
"--project", "--project",
"${workspaceFolder}/Recyclable.Collections.sln" "${workspaceFolder}/Recyclable.Collections.sln"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
} }
] ]
} }
+29
View File
@@ -0,0 +1,29 @@
# AGENTS
This repository hosts various libraries distributed as NuGet packages. Keep these links up to date so contributors and tools can easily locate them.
Only the .NET SDK **8.0** is available in the execution environment. Use this version when building or running tests.
## Implementation Guidelines
1. All `*.csproj` files should enable nullable reference types with `<Nullable>enable</Nullable>`.
1. Implementations should be optimized for maximum performance even if that introduces some code duplication.
1. Consider applying attributes such as `MethodImplOptions.AggressiveInlining` or faster alternatives to common operations when appropriate. Another would be modulo "%" or division "/" operations, which could be often replaced with faster alternatives, possibly under specific conditions.
## Running Tests
1. When running `dotnet test`, do **not** pass `--no-build` because that leads to missing assemblies, test failures without logs and other issues.
1. When running `dotnet test` specify the `--framework` argument matching the SDK available in the environment (currently `net8.0`). If you omit this parameter, the tests will fail running with no logs etc.
1. The tests run for a very long time, because there are 61_000+ unit tests for all the collections. Set timeout when running them to 15 mis.
1. All tests must use **xUnit** together with **FluentAssertions**.
## Source Code Formatting Guidelines
1. Indent code using four spaces or a single tab per level.
1. Place one blank line after each closing curly brace of a code block, except when several closing braces appear sequentially.
1. Always use braces for `if` and `else` bodies.
## NuGet packages
- [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections)
- [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List)
- [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent)
- [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq)
- [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching)
- [Recyclable.Collections.TestData](https://github.com/mlemanczyk/Recyclable.Collections.TestData)
+9 -9
View File
@@ -1,10 +1,10 @@
# License # License
The project &amp; source code is free for educational &amp; personal use. **Any kind** of commercial use requires a donation, as prescribed below. The project &amp; source code is free for educational &amp; personal use. **Any kind** of commercial use requires a donation, as prescribed below.
# Donation requirements for a life-time, commercial use # Donation requirements for a life-time, commercial use
* Commercial license is paid one time. * Commercial license is paid one time.
* It can be obtained by donaiting $10 for each developer seat, using the link provided below. If you hire 2 developers, you need to donate $20 for a life-time, commercial use of this package. * It can be obtained by donaiting $10 for each developer seat, using the link provided below. If you hire 2 developers, you need to donate $20 for a life-time, commercial use of this package.
* Your license stays with you, if your developer moves somewhere else. * Your license stays with you, if your developer moves somewhere else.
# Link for donations # Link for donations
[![Donate](buy-me-a-coffee.svg)](https://www.buymeacoffee.com/mlemanczyk) [![Donate](buy-me-a-coffee.svg)](https://www.buymeacoffee.com/mlemanczyk)
+433 -425
View File
@@ -1,425 +1,433 @@
# Recyclable.Collections # Recyclable.Collections
`Recyclable.Collections` project is an open source framework for operating dynamic lists at performance close to raw arrays, but fairly unlimited in size. It aims at providing minimal memory footprint. It implements `IList<T>` interface and is targeted as direct replacements of `List<T>`, `SortableList<T>`, `PriorityQueue<T>` & similar. `Recyclable.Collections` project is an open source framework for operating dynamic lists at performance close to raw arrays, but fairly unlimited in size. It aims at providing minimal memory footprint. It implements `IList<T>` interface and is targeted as direct replacements of `List<T>`, `SortableList<T>`, `PriorityQueue<T>` & similar.
## Included ## Included
* `RecyclableList<T>` * `RecyclableList<T>`
* `RecyclableLongList<T>` * `RecyclableLongList<T>`
# Milestones ## NuGet packages
1. ✅ Create basic classes - [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections)
1. ✅ `RecyclableList<T>` - [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List)
1. ✅ `RecyclableLongList<T>` - [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent)
1. 🅿️ `RecyclableQueue<T>` - [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq)
1. 🅿️ `RecyclableSortedList<T>` - [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching)
1. 🅿️ `RecyclableStack<T>` - [Recyclable.Collections.TestData](https://github.com/mlemanczyk/Recyclable.Collections.TestData)
1. 🅿️ `RecyclableUnorderedList<T>`
1. ✅ Create basic unit tests # Milestones
1. ✅ `RecyclableList<T>` 1. ✅ Create basic classes
1. ✅ `RecyclableLongList<T>` 1. ✅ `RecyclableList<T>`
1. 🅿️ `RecyclableQueue<T>` 1. ✅ `RecyclableLongList<T>`
1. 🅿️ `RecyclableSortedList<T>` 1. 🅿️ `RecyclableQueue<T>`
1. 🅿️ `RecyclableStack<T>` 1. 🅿️ `RecyclableSortedList<T>`
1. 🅿️ `RecyclableUnorderedList<T>` 1. 🅿️ `RecyclableStack<T>`
1. ✅ Optimize `RecyclableList<T>` 1. 🅿️ `RecyclableUnorderedList<T>`
1. ✅ `Add` 1. ✅ Create basic unit tests
1. ✅ `AddRange` 1. ✅ `RecyclableList<T>`
1. ✅ when source is `array<T>` 1. ✅ `RecyclableLongList<T>`
1. ✅ when source is `List<T>` 1. 🅿️ `RecyclableQueue<T>`
1. ✅ when source is `IList<T>` 1. 🅿️ `RecyclableSortedList<T>`
1. ✅ when source is `RecyclableList<T>` 1. 🅿️ `RecyclableStack<T>`
1. ✅ when source is `RecyclableLongList<T>` 1. 🅿️ `RecyclableUnorderedList<T>`
1. ✅ when source is `IEnumerable<T>` 1. ✅ Optimize `RecyclableList<T>`
1. ✅ when source has non-enumerated count 1. ✅ `Add`
1. ✅ `Clear` 1. ✅ `AddRange`
1. ✅ `Contains` 1. ✅ when source is `array<T>`
1. ✅ `CopyTo` 1. ✅ when source is `List<T>`
1. ✅ `Dispose` 1. ✅ when source is `IList<T>`
1. ✅ `EnsureCapacity` 1. ✅ when source is `RecyclableList<T>`
1. ✅ `GetEnumerator` 1. ✅ when source is `RecyclableLongList<T>`
1. ✅ `IndexOf` 1. ✅ when source is `IEnumerable<T>`
1. ✅ `Insert` 1. ✅ when source has non-enumerated count
1. ✅ `Remove` 1. ✅ `Clear`
1. ✅ `RemoveAt` 1. ✅ `Contains`
1. ✅ `Resize` 1. ✅ `CopyTo`
1. ✅ `this[int index]` 1. ✅ `Dispose`
1. ✅ Port `RecyclableList<T>` implementation to `RecyclableLongList<T>` 1. ✅ `EnsureCapacity`
1. 👉 Optimize `RecyclableLongList<T>` 1. ✅ `GetEnumerator`
1. ✅ `Add` 1. ✅ `IndexOf`
1. ✅ `AddRange` 1. ✅ `Insert`
1. ✅ when source is `array<T>` 1. ✅ `Remove`
1. ✅ when source is `List<T>` 1. ✅ `RemoveAt`
1. ✅ when source is `IList<T>` 1. ✅ `Resize`
1. ✅ when source is `RecyclableList<T>` 1. ✅ `this[int index]`
1. ✅ when source is `RecyclableLongList<T>` 1. ✅ Port `RecyclableList<T>` implementation to `RecyclableLongList<T>`
1. ✅ when source is `IEnumerable<T>` 1. 👉 Optimize `RecyclableLongList<T>`
1. ✅ when source has non-enumerated count 1. ✅ `Add`
1. ✅ `Clear` 1. ✅ `AddRange`
1. ✅ `Contains` 1. ✅ when source is `array<T>`
1. 🅿️ `CopyTo` 1. ✅ when source is `List<T>`
1. ✅ `Dispose` 1. ✅ when source is `IList<T>`
1. ✅ `EnsureCapacity` 1. ✅ when source is `RecyclableList<T>`
1. ✅ `GetEnumerator` 1. ✅ when source is `RecyclableLongList<T>`
1. ✅ `IndexOf` 1. ✅ when source is `IEnumerable<T>`
1. ✅ `LongIndexOf` 1. ✅ when source has non-enumerated count
1. ✅ `Insert` 1. ✅ `Clear`
1. ✅ `Remove` 1. ✅ `Contains`
1. ✅ `RemoveAt(int index)` 1. 🅿️ `CopyTo`
1. ✅ `RemoveAt(long index)` 1. ✅ `Dispose`
1. ✅ `RemoveAt` 1. ✅ `EnsureCapacity`
1. ✅ `Resize` 1. ✅ `GetEnumerator`
1. ✅ `this[int index]` 1. ✅ `IndexOf`
1. ✅ `this[long index]` 1. ✅ `LongIndexOf`
1. ✅ Rename `RecyclableList<T>` to `RecyclableLongList<T>` 1. ✅ `Insert`
1. ✅ Rename `RecyclableArrayList<T>` to `RecyclableList<T>` 1. ✅ `Remove`
1. ✅ Fix failing tests 1. ✅ `RemoveAt(int index)`
1. ✅ Hide not ready classes 1. ✅ `RemoveAt(long index)`
1. ✅ `RecyclableQueue<T>` 1. ✅ `RemoveAt`
1. ✅ `RecyclableSortedList<T>` 1. ✅ `Resize`
1. ✅ `RecyclableStack<T>` 1. ✅ `this[int index]`
1. ✅ `RecyclableUnorderedList<T>` 1. ✅ `this[long index]`
1. ✅ Add support for `ReadOnlySpan<T>` 1. ✅ Rename `RecyclableList<T>` to `RecyclableLongList<T>`
1. ✅ Release 0.0.3-alpha 1. ✅ Rename `RecyclableArrayList<T>` to `RecyclableList<T>`
1. ✅ Implement `List<T>` interfaces 1. ✅ Fix failing tests
1. ✅ `IList<T>` 1. ✅ Hide not ready classes
1. ✅ `ICollection<T>` 1. ✅ `RecyclableQueue<T>`
1. ✅ `IEnumerable<T>` 1. ✅ `RecyclableSortedList<T>`
1. ✅ `IEnumerable` 1. ✅ `RecyclableStack<T>`
1. ✅ `IReadOnlyList<T>` 1. ✅ `RecyclableUnorderedList<T>`
1. ✅ `IReadOnlyCollection<T>` 1. ✅ Add support for `ReadOnlySpan<T>`
1. ✅ `IList` 1. ✅ Release 0.0.3-alpha
1. ✅ `ICollection` 1. ✅ Implement `List<T>` interfaces
1. ✅ Implement list versioning to allow data change identification 1. ✅ `IList<T>`
1. ✅ Make sure that `NeedsClearing` is used & items are cleared in 1. ✅ `ICollection<T>`
1. ✅ `RecyclableList<T>` 1. ✅ `IEnumerable<T>`
1. ✅ `Clear` 1. ✅ `IEnumerable`
1. ✅ `Dispose` 1. ✅ `IReadOnlyList<T>`
1. ✅ `Remove` 1. ✅ `IReadOnlyCollection<T>`
1. ✅ `RemoveAt` 1. ✅ `IList`
1. ✅ Properties 1. ✅ `ICollection`
1. ✅ Non-versioned & Versioned Enumerators 1. ✅ Implement list versioning to allow data change identification
1. ✅ `RecyclableListHelpers` 1. ✅ Make sure that `NeedsClearing` is used & items are cleared in
1. ✅ `EnsureCapacity` 1. ✅ `RecyclableList<T>`
1. ✅ `Resize` 1. ✅ `Clear`
1. ✅ `IList<T>` implementation 1. ✅ `Dispose`
1. ✅ `RecyclableLongList<T>` 1. ✅ `Remove`
1. ✅ `Clear` 1. ✅ `RemoveAt`
1. ✅ `Dispose` 1. ✅ Properties
1. ✅ `Remove` 1. ✅ Non-versioned & Versioned Enumerators
1. ✅ `RemoveAt` 1. ✅ `RecyclableListHelpers`
1. ✅ `RemoveBlock` 1. ✅ `EnsureCapacity`
1. ✅ Properties 1. ✅ `Resize`
1. ✅ Non-versioned & Versioned Enumerators 1. ✅ `IList<T>` implementation
1. ✅ `RecyclableLongListHelpers` 1. ✅ `RecyclableLongList<T>`
1. ✅ `CopyFollowingItems` 1. ✅ `Clear`
1. ✅ `CopyTo` 1. ✅ `Dispose`
1. ✅ `EnsureCapacity` 1. ✅ `Remove`
1. ✅ `Resize` 1. ✅ `RemoveAt`
1. ✅ `RecyclableLongList<T>.IndexOfHelpers` 1. ✅ `RemoveBlock`
1. ✅ Complete the implementation of `RecyclableLongList<T>` 1. ✅ Properties
1. ✅ `InsertAt` to allow inserting items anywhere 1. ✅ Non-versioned & Versioned Enumerators
1. ✅ `RemoveAt` to allow removing item at any index 1. ✅ `RecyclableLongListHelpers`
1. ✅ Release 0.0.4 1. ✅ `CopyFollowingItems`
1. ✅ Extend unit tests 1. ✅ `CopyTo`
1. ✅ `.Add` / `.AddRange` must accept `null` values 1. ✅ `EnsureCapacity`
1. ✅ `.AddRange` must accept empty collections 1. ✅ `Resize`
1. ✅ `.Remove` / `.RemoveAt` / `.Clear` / `.Dispose` must clear reference when reference type 1. ✅ `RecyclableLongList<T>.IndexOfHelpers`
1. ✅ `.Clear` / `.Dispose` must return arrays to pools 1. ✅ Complete the implementation of `RecyclableLongList<T>`
1. ✅ `int` / `long` variants of `.Count` / `.LongCount`, `.IndexOf` / `.LongIndexOf`, `.Insert`, `.RemoveAt`, `this[]` 1. ✅ `InsertAt` to allow inserting items anywhere
1. ✅ Add `.ToRecyclableList` / `.ToRecyclableLongList` variants for all supported collection types 1. ✅ `RemoveAt` to allow removing item at any index
1. ✅ `Array` 1. ✅ Release 0.0.4
1. ✅ `ICollection` 1. ✅ Extend unit tests
1. ✅ `ICollection<T>` 1. ✅ `.Add` / `.AddRange` must accept `null` values
1. ✅ `IEnumerable` 1. ✅ `.AddRange` must accept empty collections
1. ✅ `IEnumerable<T>` 1. ✅ `.Remove` / `.RemoveAt` / `.Clear` / `.Dispose` must clear reference when reference type
1. ✅ `IReadOnlyList<T>` 1. ✅ `.Clear` / `.Dispose` must return arrays to pools
1. ✅ `ReadOnlySpan<T>` 1. ✅ `int` / `long` variants of `.Count` / `.LongCount`, `.IndexOf` / `.LongIndexOf`, `.Insert`, `.RemoveAt`, `this[]`
1. ✅ `RecyclableList` 1. ✅ Add `.ToRecyclableList` / `.ToRecyclableLongList` variants for all supported collection types
1. ✅ `RecyclableLongList` 1. ✅ `Array`
1. ✅ `Span<T>` 1. ✅ `ICollection`
1. ✅ `T[]` 1. ✅ `ICollection<T>`
1. ✅ Release 0.0.5 1. ✅ `IEnumerable`
1. ✅ Implement `List<T>` compatibility pack for `RecyclableList<T>` 1. ✅ `IEnumerable<T>`
1. ✅ Overflow review 1. ✅ `IReadOnlyList<T>`
1. ✅ Add type casting to `long` for `<<` & `>>` operations, where required 1. ✅ `ReadOnlySpan<T>`
1. ✅ Make type castings `checked`, where required 1. ✅ `RecyclableList`
1. ✅ Release 0.0.6 1. ✅ `RecyclableLongList`
1. 👉 Implement `List<T>` compatibility pack for `RecyclableLongList<T>` 1. ✅ `Span<T>`
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableList<T>` 1. ✅ `T[]`
1. `IndexOf` parallelization 1. ✅ Release 0.0.5
1. 🅿️ Release 0.0.7 1. ✅ Implement `List<T>` compatibility pack for `RecyclableList<T>`
1. 🅿️ Implement `ILongList<T>` interface 1. ✅ Overflow review
1. 🅿️ `RecyclableList<T>` 1. ✅ Add type casting to `long` for `<<` & `>>` operations, where required
1. 🅿️ `RecyclableLongList<T>` 1. ✅ Make type castings `checked`, where required
1. 🅿️ Implement `RecyclableQueue<T>` 1. ✅ Release 0.0.6
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableQueue<T>` 1. 👉 Implement `List<T>` compatibility pack for `RecyclableLongList<T>`
1. 🅿️ Release 0.0.8 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableList<T>`
1. 🅿️ Implement `RecyclableStack<T>` 1. `IndexOf` parallelization
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableStack<T>` 1. 🅿️ Release 0.0.7
1. 🅿️ Release 0.0.9 1. 🅿️ Implement `ILongList<T>` interface
1. 🅿️ Implement `RecyclableSortedList<T>` 1. 🅿️ `RecyclableList<T>`
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableSortedList<T>` 1. 🅿️ `RecyclableLongList<T>`
1. 🅿️ Release 0.0.10 1. 🅿️ Implement `RecyclableQueue<T>`
1. 🅿️ Implement `RecyclableUnorderedList<T>` 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableQueue<T>`
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableUnorderedList<T>` 1. 🅿️ Release 0.0.8
1. 🅿️ Release 0.0.11 1. 🅿️ Implement `RecyclableStack<T>`
1. 🅿️ Implement `RecyclableVersionedList<T>` 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableStack<T>`
1. 🅿️ Port `RecyclableList<T>` to `RecyclableVersionedList<T>` 1. 🅿️ Release 0.0.9
1. 🅿️ Port `RecyclableLongList<T>` to `RecyclableVersionedLongList<T>` 1. 🅿️ Implement `RecyclableSortedList<T>`
1. 🅿️ Release 0.0.12 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableSortedList<T>`
1. 🅿️ Optimize `OneSizeArrayPool` 1. 🅿️ Release 0.0.10
1. 🅿️ Review locks 1. 🅿️ Implement `RecyclableUnorderedList<T>`
1. 🅿️ Measure multi-threading performance 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableUnorderedList<T>`
1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario 1. 🅿️ Release 0.0.11
1. 🅿️ Optimize `RecyclableCollectionVersionObjectPool` 1. 🅿️ Implement `RecyclableVersionedList<T>`
1. 🅿️ Implement custom `ObjectPool<T>` 1. 🅿️ Port `RecyclableList<T>` to `RecyclableVersionedList<T>`
1. 🅿️ Measure multi-threading performance 1. 🅿️ Port `RecyclableLongList<T>` to `RecyclableVersionedLongList<T>`
1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario 1. 🅿️ Release 0.0.12
1. 🅿️ Review `RecyclableArrayPool` 1. 🅿️ Optimize `OneSizeArrayPool`
1. 🅿️ Review locks 1. 🅿️ Review locks
1. 🅿️ Measure multi-threading performance 1. 🅿️ Measure multi-threading performance
1. 🅿️ Implement array disposal in high RAM pressure scenario 1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario
1. 🅿️ Optimize `MemoryBucket<T>` 1. 🅿️ Optimize `RecyclableCollectionVersionObjectPool`
1. 🅿️ Convert to `struct`, if possible 1. 🅿️ Implement custom `ObjectPool<T>`
1. 🅿️ Find out if there are better replacements 1. 🅿️ Measure multi-threading performance
1. 🅿️ Release 0.0.13 1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario
1. 🅿️ Optimize 1. 🅿️ Review `RecyclableArrayPool`
1. 🅿️ `IndexOfSynchronizationContext` 1. 🅿️ Review locks
1. 🅿️ `IndexOfSynchronizationContextPool` 1. 🅿️ Measure multi-threading performance
1. 🅿️ `ManualResetEventSlimmer` 1. 🅿️ Implement array disposal in high RAM pressure scenario
1. 🅿️ Multi-threading benchmarks 1. 🅿️ Optimize `MemoryBucket<T>`
1. 🅿️ `ManualResetEventSlimmerPool` 1. 🅿️ Convert to `struct`, if possible
1. 🅿️ Multi-threading benchmarks 1. 🅿️ Find out if there are better replacements
1. 🅿️ `SpinLockSlimmer` 1. 🅿️ Release 0.0.13
1. 🅿️ Multi-threading benchmarks 1. 🅿️ Optimize
1. 🅿️ Remove range operators, where possible 1. 🅿️ `IndexOfSynchronizationContext`
1. 🅿️ Release 0.0.14 1. 🅿️ `IndexOfSynchronizationContextPool`
1. 🅿️ Extend unit tests 1. 🅿️ `ManualResetEventSlimmer`
1. 🅿️ `RecyclableLongListExtensions.CopyTo` 1. 🅿️ Multi-threading benchmarks
1. 🅿️ `RecyclableLongList` cases for large items count (&gt;`int.MaxValue`) 1. 🅿️ `ManualResetEventSlimmerPool`
1. 🅿️ Pools 1. 🅿️ Multi-threading benchmarks
1. 🅿️ Multi-threading 1. 🅿️ `SpinLockSlimmer`
1. 🅿️ Add comparison tests vs `T[]` / `List<T>` equivalents 1. 🅿️ Multi-threading benchmarks
1. 🅿️ Add test cases for indexes beyond items count 1. 🅿️ Remove range operators, where possible
1. 🅿️ Cover border cases 1. 🅿️ Release 0.0.14
1. 🅿️ Non-existing items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()` 1. 🅿️ Extend unit tests
1. 🅿️ Duplicated items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()` 1. 🅿️ `RecyclableLongListExtensions.CopyTo`
1. 🅿️ Append test data in reversed order in `.AddRange()`, `.InsertRange()` and similar tests 1. 🅿️ `RecyclableLongList` cases for large items count (&gt;`int.MaxValue`)
1. 🅿️ Switch tests to use Source / Target data variants in `.AddRange()`, `.InsertRange()` and similar tests 1. 🅿️ Pools
1. 🅿️ Cleanup 1. 🅿️ Multi-threading
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field 1. 🅿️ Add comparison tests vs `T[]` / `List<T>` equivalents
1. 🅿️ Replace all internal properties refs with fields, where possible 1. 🅿️ Add test cases for indexes beyond items count
1. ✅ `RecyclableLongListExtensions` 1. 🅿️ Cover border cases
1. ✅ `ListExtensions` 1. 🅿️ Non-existing items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()`
1. 🅿️ `MathUtils` 1. 🅿️ Duplicated items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()`
1. 🅿️ Resolve TODOs 1. 🅿️ Append test data in reversed order in `.AddRange()`, `.InsertRange()` and similar tests
1. 🅿️ Remove obsolete methods 1. 🅿️ Switch tests to use Source / Target data variants in `.AddRange()`, `.InsertRange()` and similar tests
1. 🅿️ Add`RecyclableLongListHelpers.Enumerate` to benchmarks 1. 🅿️ Cleanup
1. 🅿️ Review all classes & add `volatile`, where needed 1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
1. 🅿️ Remove `expectedItemsCount` parameter from `RecyclableLongList`'s constructor 1. 🅿️ Replace all internal properties refs with fields, where possible
1. 🅿️ Remove `array` slicing in favor for indexing, like in `RecyclableCompatibilityListExtensions` 1. ✅ `RecyclableLongListExtensions`
1. 🅿️ Release 0.0.15 1. ✅ `ListExtensions`
1. 🅿️ Optimize 1. 🅿️ `MathUtils`
1. 🅿️ `ListExtensions` 1. 🅿️ Resolve TODOs
1. 🅿️ `MathUtils` 1. 🅿️ Remove obsolete methods
1. 🅿️ `SystemRandomNumberGenerator` 1. 🅿️ Add`RecyclableLongListHelpers.Enumerate` to benchmarks
1. 🅿️ `RecyclableLongListExtensions` 1. 🅿️ Review all classes & add `volatile`, where needed
1. 🅿️ Memory release in pools to utilize GC events in case of memory pressure 1. 🅿️ Remove `expectedItemsCount` parameter from `RecyclableLongList`'s constructor
1. 🅿️ Release 0.0.16 1. 🅿️ Remove `array` slicing in favor for indexing, like in `RecyclableCompatibilityListExtensions`
1. ✅ Review and remove warnings & hints 1. 🅿️ Release 0.0.15
1. ✅ Warnings 1. 🅿️ Optimize
1. ✅ Hints 1. 🅿️ `ListExtensions`
1. Documentation 1. 🅿️ `MathUtils`
1. 🅿️ Document the most efficient iteration over array collections 1. 🅿️ `SystemRandomNumberGenerator`
1. 🅿️ Document the most efficient iteration over blocked collections 1. 🅿️ `RecyclableLongListExtensions`
1. 🅿️ Document differences in behavior 1. 🅿️ Memory release in pools to utilize GC events in case of memory pressure
1. 🅿️ Document other specifics 1. 🅿️ Release 0.0.16
1. 🅿️ Release 1.0.0 1. ✅ Review and remove warnings & hints
1. 🅿️ Implement source code generators 1. ✅ Warnings
1. 🅿️ Add attributes 1. ✅ Hints
1. 🅿️ `GeneratorBaseClassAttribute` for marking base class used for generation 1. Documentation
1. 🅿️ `VersionedAttribute` for marking classes as versioned 1. 🅿️ Document the most efficient iteration over array collections
1. 🅿️ `IncVersionAttribute` for marking methods & setters as resulting in version increase 1. 🅿️ Document the most efficient iteration over blocked collections
1. 🅿️ Implement source code generator 1. 🅿️ Document differences in behavior
1. 🅿️ Generate partial class for classes marked with `GeneratorBaseClassAttribute` 1. 🅿️ Document other specifics
1. 🅿️ Add support for fields 1. 🅿️ Release 1.0.0
1. 🅿️ Add support for properties 1. 🅿️ Implement source code generators
1. 🅿️ Add support for methods 1. 🅿️ Add attributes
1. 🅿️ Add support for constructor 1. 🅿️ `GeneratorBaseClassAttribute` for marking base class used for generation
1. 🅿️ Skip base fields, methods, properties etc. when they're overridden in the child class 1. 🅿️ `VersionedAttribute` for marking classes as versioned
1. 🅿️ Add support for `ulong` indexing 1. 🅿️ `IncVersionAttribute` for marking methods & setters as resulting in version increase
1. 🅿️ Convert `_memoryBlocks` to `Array` to allow `ulong` lengths 1. 🅿️ Implement source code generator
1. 🅿️ Convert block indexes from `int` to `ulong` or `long` 1. 🅿️ Generate partial class for classes marked with `GeneratorBaseClassAttribute`
1. 🅿️ Optimize 1. 🅿️ Add support for fields
1. 🅿️ `RecyclableLongList<T>.Resize` 1. 🅿️ Add support for properties
1. 🅿️ `RecyclableLongList<T>.CopyTo` 1. 🅿️ Add support for methods
1. 🅿️ Check if we can benefit from Sse2 in `.IndexOf`/`.Contains` methods as given in [MS blog](https://devblogs.microsoft.com/dotnet/hardware-intrinsics-in-net-core/). 1. 🅿️ Add support for constructor
1. 🅿️ Memory pools 1. 🅿️ Skip base fields, methods, properties etc. when they're overridden in the child class
1. 🅿️ `OneSizeArrayPool<T>` 1. 🅿️ Add support for `ulong` indexing
1. 🅿️ `RecyclableArrayPool<T>` 1. 🅿️ Convert `_memoryBlocks` to `Array` to allow `ulong` lengths
1. 🅿️ Add support for `ICollection<T>` interface in `.AddRange` & `constructor` 1. 🅿️ Convert block indexes from `int` to `ulong` or `long`
1. 🅿️ Final optimizations 1. 🅿️ Optimize
1. 🅿️ Replace `Math` class usages with `if` statements 1. 🅿️ `RecyclableLongList<T>.Resize`
1. 🅿️ Replace `a - b > 0` & `a - b < 0` comparisons with `a > b` & `a < b` 1. 🅿️ `RecyclableLongList<T>.CopyTo`
1. 🅿️ Replace `a + b > 0` & `a + b < 0` comparisons with `a > b` & `a < b` 1. 🅿️ Check if we can benefit from Sse2 in `.IndexOf`/`.Contains` methods as given in [MS blog](https://devblogs.microsoft.com/dotnet/hardware-intrinsics-in-net-core/).
1. 🅿️ Replace `a / b` & `a * b` calculations with equivalents, where possible 1. 🅿️ Memory pools
1. 🅿️ Replace virtual calls with static calls 1. 🅿️ `OneSizeArrayPool<T>`
1. 🅿️ Separate `++number`, `number++`, `--number` & `number--` operations to their own lines (they add extra variable) 1. 🅿️ `RecyclableArrayPool<T>`
1. 🅿️ Replace `blockSize` sums by powers of 2, minus 1 1. 🅿️ Add support for `ICollection<T>` interface in `.AddRange` & `constructor`
1. 🅿️ Remove type castings, if possible 1. 🅿️ Final optimizations
1. 🅿️ Convert generic methods to non-generic 1. 🅿️ Replace `Math` class usages with `if` statements
1. 🅿️ Replace integer comparisons with comparisons to 0 / 1, if possible 1. 🅿️ Replace `a - b > 0` & `a - b < 0` comparisons with `a > b` & `a < b`
1. 🅿️ Release 2.0.0 1. 🅿️ Replace `a + b > 0` & `a + b < 0` comparisons with `a > b` & `a < b`
1. 🅿️ Replace `a / b` & `a * b` calculations with equivalents, where possible
# Characteristics of the classes 1. 🅿️ Replace virtual calls with static calls
1. 🅿️ Separate `++number`, `number++`, `--number` & `number--` operations to their own lines (they add extra variable)
## Common 1. 🅿️ Replace `blockSize` sums by powers of 2, minus 1
* All classes implement `IDisposable` interface & SHOULD be disposed after use. That's to return block arrays taken from the shared pool. It may be foreseen as an issue for replacement in existing code, which obviously is missing `using` clause. But considering that `Dispose` will be called by `GC` anyway, it should cause issues in specific scenarios, only. In either case the fix is one-word addition of `using`. 1. 🅿️ Remove type castings, if possible
* Memory blocks are created in 2 ways, depending on `blockSize` value 1. 🅿️ Convert generic methods to non-generic
* when `blockSize < 128` ⇨ blocks are created as regular `arrays`. 1. 🅿️ Replace integer comparisons with comparisons to 0 / 1, if possible
* when `blockSize >= 128` ⇨ blocks are taken from and returned to `ArrayPool<T>.Shared`, when blocks are removed. 1. 🅿️ Release 2.0.0
* maximum `blockSize` value is `2_147_483_591`, which corresponds more or less to the maximum array size.
* `blockSize` MUST be a power of 2. It will be rounded up to the closest power of 2, if needed. That is due to high performance gain on some operations, like the calculation of item index in a block. # Characteristics of the classes
* Array pools are shared between the same `T` type. I.e. `List<int>` will use a different pool from `List<short>` and so on. For high concurrency environments you may want to provide your own pools, when this feature becomes available in the upcoming releases.
* Trying to access `this[int index]`, `this[long index]`, `Count`, `LongCount` etc. when `Capacity == 0` will raise `NullReferenceException`. This is by design to remove all non-critical code from the constructor. ## Common
* ⚠️ The default enumerators don't check if the collection was modified. That's by design due to high performance hit of the check. That should be fine in most use-cases. If you need to check for modifications, you're welcomed to use `.GetVersionedEnumerator()` / `IRecyclableVersionedXxxList<T>` type-casting, e.g. in `foreach` loops. * All classes implement `IDisposable` interface & SHOULD be disposed after use. That's to return block arrays taken from the shared pool. It may be foreseen as an issue for replacement in existing code, which obviously is missing `using` clause. But considering that `Dispose` will be called by `GC` anyway, it should cause issues in specific scenarios, only. In either case the fix is one-word addition of `using`.
```csharp * Memory blocks are created in 2 ways, depending on `blockSize` value
foreach (var item in (IRecyclableVersionedList<long>)recyclableList) * when `blockSize < 128` ⇨ blocks are created as regular `arrays`.
{ * when `blockSize >= 128` ⇨ blocks are taken from and returned to `ArrayPool<T>.Shared`, when blocks are removed.
... * maximum `blockSize` value is `2_147_483_591`, which corresponds more or less to the maximum array size.
... * `blockSize` MUST be a power of 2. It will be rounded up to the closest power of 2, if needed. That is due to high performance gain on some operations, like the calculation of item index in a block.
... * Array pools are shared between the same `T` type. I.e. `List<int>` will use a different pool from `List<short>` and so on. For high concurrency environments you may want to provide your own pools, when this feature becomes available in the upcoming releases.
} * Trying to access `this[int index]`, `this[long index]`, `Count`, `LongCount` etc. when `Capacity == 0` will raise `NullReferenceException`. This is by design to remove all non-critical code from the constructor.
* ⚠️ The default enumerators don't check if the collection was modified. That's by design due to high performance hit of the check. That should be fine in most use-cases. If you need to check for modifications, you're welcomed to use `.GetVersionedEnumerator()` / `IRecyclableVersionedXxxList<T>` type-casting, e.g. in `foreach` loops.
foreach (var item in (IRecyclableVersionedLongList<long>)recyclableLongList) ```csharp
{ foreach (var item in (IRecyclableVersionedList<long>)recyclableList)
... {
... ...
... ...
} ...
``` }
* ⚠️ The state of the default enumerators before the first & after the last call to `.MoveNext()` is undefined, but no exception is raised. That's by design due to high performance hit of the checks. This behavior is compatible with `foreach` loops, which will never access `.Current` property
* when the collection is empty, or foreach (var item in (IRecyclableVersionedLongList<long>)recyclableLongList)
* when `.MoveNext()` returned `false`, after reaching the end of the collection. {
...
* ⚠️ If you decide to use `.GetEnumerator()` / `.GetVersionedEnumerator()`, instead of relying on `foreach` loops ...
* you MUST ensure that you always respect the result of `.MoveNext()` call, and ...
* you MUST NOT access `.Current` property before calling `.MoveNext()` or after reaching the end of the collection. }
```
\ * ⚠️ The state of the default enumerators before the first & after the last call to `.MoveNext()` is undefined, but no exception is raised. That's by design due to high performance hit of the checks. This behavior is compatible with `foreach` loops, which will never access `.Current` property
**Failing to do so WILL result in unpredictable behavior.** * when the collection is empty, or
* when `.MoveNext()` returned `false`, after reaching the end of the collection.
### [Benchmark results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md)
* ⚠️ If you decide to use `.GetEnumerator()` / `.GetVersionedEnumerator()`, instead of relying on `foreach` loops
## `RecyclableList<T>` * you MUST ensure that you always respect the result of `.MoveNext()` call, and
* Range: `int` * you MUST NOT access `.Current` property before calling `.MoveNext()` or after reaching the end of the collection.
* Interfaces: `IList<T>`, `IEnumerable<T>`, `IDisposable`\
\ \
This is the direct equivalent of `List<T>` class, except that the arrays are taken from the shared pool, instead of directly being allocated. It proves to out perform `List<T>` & the standard arrays in certain operations and is fairly close in others. But they may perform worse in certain scenarios. If you're interested in details, please refer to benchmarks. **Failing to do so WILL result in unpredictable behavior.**
## `RecyclableLongList<T>`, `RecyclableSortableList<T>`, `RecyclableQueue<T>`, `RecyclableStack<T>` ### [Benchmark results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md)
* Range: `long`
* Interfaces: `IList<T>`, `ILongList<T>`, `IEnumerable<T>`, `IDisposable` ## `RecyclableList<T>`
* All classes provide large storage capabilities, in practice limited only by the memory available in your system. * Range: `int`
* All data is stored in blocks, with the provided `int blockSize` or the default, currently being `10,240` items in each block. It's recommended to use smaller numbers, if you foresee storing low no. of items on the list. * Interfaces: `IList<T>`, `IEnumerable<T>`, `IDisposable`\
* There is no memory copying to increase the capacity of the recyclable classes, unless the no. of items exceeds the no. of allocated blocks. In that case, a new array of blocks is created to accommodate . If more capacity is needed, a new memory block is allocate & added to the internal list of blocks. This is the only list that may re-allocate memory block & copy their content to grow. That shouldn't be an issue considering limited no. of blocks stored by each class in practical scenarios. Currently I'm not planning any works around that. Shall there be a need to eliminate it, you're welcomed to file a PR with the proposed changes. \
* ⚠️ `IndexOf` will automatically switch to parallel search mode. Collections bigger than (currently) 850_000 items will be scanned using parallelization. In either case it'll return the index of the first matching item. This note also applies to `Contains`, because it internally uses `IndexOf`. The value of 850_000 may become configurable in the future, unless it proves to result in much worse performance. This is the direct equivalent of `List<T>` class, except that the arrays are taken from the shared pool, instead of directly being allocated. It proves to out perform `List<T>` & the standard arrays in certain operations and is fairly close in others. But they may perform worse in certain scenarios. If you're interested in details, please refer to benchmarks.
The parallelization utilizes default `Task` scheduling, resulting in non-deterministic order in which search tasks are executed. In most cases it resulted in significant performance improvements. Below you can find the benchmarks for various item counts. ## `RecyclableLongList<T>`, `RecyclableSortableList<T>`, `RecyclableQueue<T>`, `RecyclableStack<T>`
* Range: `long`
As `RecyclableLongList<T>` is foreseen for use-cases with big data, you should automatically benefit from it in many use-cases. * Interfaces: `IList<T>`, `ILongList<T>`, `IEnumerable<T>`, `IDisposable`
* All classes provide large storage capabilities, in practice limited only by the memory available in your system.
### [Benchmark Results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md) * All data is stored in blocks, with the provided `int blockSize` or the default, currently being `10,240` items in each block. It's recommended to use smaller numbers, if you foresee storing low no. of items on the list.
* There is no memory copying to increase the capacity of the recyclable classes, unless the no. of items exceeds the no. of allocated blocks. In that case, a new array of blocks is created to accommodate . If more capacity is needed, a new memory block is allocate & added to the internal list of blocks. This is the only list that may re-allocate memory block & copy their content to grow. That shouldn't be an issue considering limited no. of blocks stored by each class in practical scenarios. Currently I'm not planning any works around that. Shall there be a need to eliminate it, you're welcomed to file a PR with the proposed changes.
# Architecture Decisions * ⚠️ `IndexOf` will automatically switch to parallel search mode. Collections bigger than (currently) 850_000 items will be scanned using parallelization. In either case it'll return the index of the first matching item. This note also applies to `Contains`, because it internally uses `IndexOf`. The value of 850_000 may become configurable in the future, unless it proves to result in much worse performance.
The architecture of the classes included in this project is driven primarily by performance & memory footprints. The following decisions were made to support it.
The parallelization utilizes default `Task` scheduling, resulting in non-deterministic order in which search tasks are executed. In most cases it resulted in significant performance improvements. Below you can find the benchmarks for various item counts.
## Code duplication
There is high level of code duplication in each of the classes. When you look at them, you will see they are alike & some of the code could be extracted to extension methods. It has been done & has proven to perform worse. For this reason, properties & methods expected to be on the hot-path, duplicate much of the code to get the most out of it. As `RecyclableLongList<T>` is foreseen for use-cases with big data, you should automatically benefit from it in many use-cases.
## Common base - `ILongList<T>` ### [Benchmark Results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md)
Having a common base class for all the classes looks very attractive. But it is also proved to perform worse, if you start making properties & methods virtual & there are subsequent calls to `base`. For this reason, each of the classes is the root class & there are hardly any virtual methods & properties. All classes are meant to be `final`, but I've not marked them as `sealed`. If you feel that you'd benefit from it, you're welcomed to inherit from them. The protected stuff is there for your use.
# Architecture Decisions
If you desire to have a common base for calls, each of the classes implements `IList<T>` & `ILongList<T>` interfaces. `ILongList<T>` is a new interface, made alike `IList<T>`, but unlocking full support for `long` indexing & use. Considering the characteristics of the classes I feel that the generic name is justified, instead of `IRecyclableList<T>` what you could expect. The architecture of the classes included in this project is driven primarily by performance & memory footprints. The following decisions were made to support it.
## Protection against incorrect use & state ## Code duplication
Having protection against invalid state of the object is an important part of software engineering. The classes included in this project provide the same level of protection as the regular `array` & `List` class. They will raise exceptions when invalid index is given. But there is no additional protection that the content & state of the objects internally used by these classes are valid. You can break them, if you want to. There is high level of code duplication in each of the classes. When you look at them, you will see they are alike & some of the code could be extracted to extension methods. It has been done & has proven to perform worse. For this reason, properties & methods expected to be on the hot-path, duplicate much of the code to get the most out of it.
This decision is driven by performance measures. Considering wide use of the lists on the hot-paths, each additional check & operation starts counting. I've decided to eliminate all the checks for the sake of performance. If you feel that you need additional protection, feel free to inherit or reuse any of the provided classes to provide that. ## Common base - `ILongList<T>`
Having a common base class for all the classes looks very attractive. But it is also proved to perform worse, if you start making properties & methods virtual & there are subsequent calls to `base`. For this reason, each of the classes is the root class & there are hardly any virtual methods & properties. All classes are meant to be `final`, but I've not marked them as `sealed`. If you feel that you'd benefit from it, you're welcomed to inherit from them. The protected stuff is there for your use.
## `this[int index]`, `Count`, `IndexOf(T item)` etc. may overflow, but...
All the classes included in this package support `long` indexing and addressing. The `LongCount` property will always provide the total no. of items stored on the list. Since `Count` is just `LongCount` type-casted to `int`, its value may overflow. If you desire to have a common base for calls, each of the classes implements `IList<T>` & `ILongList<T>` interfaces. `ILongList<T>` is a new interface, made alike `IList<T>`, but unlocking full support for `long` indexing & use. Considering the characteristics of the classes I feel that the generic name is justified, instead of `IRecyclableList<T>` what you could expect.
But `List<T>` addressing is kind of currently limited to `int` ranges anyway in practical scenarios. As such `Count` overflowing shouldn't be an issue when using the classes with existing code. If you use `Count` in your loops, it's recommended to replace it with `LongCount`, tough. You don't need to make any changes in `foreach` or other iterator loops. They are expected to transparently use the new classes & yield all items, beyond `int.MaxValue` range. ## Protection against incorrect use & state
Having protection against invalid state of the object is an important part of software engineering. The classes included in this project provide the same level of protection as the regular `array` & `List` class. They will raise exceptions when invalid index is given. But there is no additional protection that the content & state of the objects internally used by these classes are valid. You can break them, if you want to.
This is by design, for the best performance. Initially there have been checks in place to prevent overflowing & return `int.MaxValue` instead, but they proved to slow down the operations. Considering low risk for existing code, I've decided to take it out.
This decision is driven by performance measures. Considering wide use of the lists on the hot-paths, each additional check & operation starts counting. I've decided to eliminate all the checks for the sake of performance. If you feel that you need additional protection, feel free to inherit or reuse any of the provided classes to provide that.
If you foresee the overflowing as an issue, you're welcomed to propose an improvement & file a PR with proposed solution.
## `this[int index]`, `Count`, `IndexOf(T item)` etc. may overflow, but...
# Auto-properties, new language features & code style All the classes included in this package support `long` indexing and addressing. The `LongCount` property will always provide the total no. of items stored on the list. Since `Count` is just `LongCount` type-casted to `int`, its value may overflow.
When you look at the code you may note, that it looks like in early c# days. Where the new language features yield the same or better performance, they are & will be used.
But `List<T>` addressing is kind of currently limited to `int` ranges anyway in practical scenarios. As such `Count` overflowing shouldn't be an issue when using the classes with existing code. If you use `Count` in your loops, it's recommended to replace it with `LongCount`, tough. You don't need to make any changes in `foreach` or other iterator loops. They are expected to transparently use the new classes & yield all items, beyond `int.MaxValue` range.
However, many of the new language features have proved to provide worse performance at some point in my testing. It includes auto-properties, for which I could see getters & setters generated in IL, instead of fields. Because all the classes are expected to be on the hot-paths, I've eliminated most of them.
This is by design, for the best performance. Initially there have been checks in place to prevent overflowing & return `int.MaxValue` instead, but they proved to slow down the operations. Considering low risk for existing code, I've decided to take it out.
Similarly, you will find places in code with hints or warnings disabled. The decision was always driven by performance measures. The use case & business logic was carefully reviewed to ensure it's safe to take shortcuts, before making the change. If you run into failing scenario, please feel free to create a work item against it and/or file a PR to fix it.
If you foresee the overflowing as an issue, you're welcomed to propose an improvement & file a PR with proposed solution.
Finally, you'll find places with code commented out. That's to easily track what has been tried & what proved to perform worse.
# Auto-properties, new language features & code style
At any time, if you know & can show in your benchmarks that the code can be simplified without impacting the performance, feel free to file a PR with proposed changes. Please include benchmark results from your testing to speed up things. When you look at the code you may note, that it looks like in early c# days. Where the new language features yield the same or better performance, they are & will be used.
# Thread Safety However, many of the new language features have proved to provide worse performance at some point in my testing. It includes auto-properties, for which I could see getters & setters generated in IL, instead of fields. Because all the classes are expected to be on the hot-paths, I've eliminated most of them.
The `public static` members of the classes are thread safe. Any instance members are not guaranteed to be thread safe. You need to implement locking mechanism to safely use the classes in a multi-threading environment.
Similarly, you will find places in code with hints or warnings disabled. The decision was always driven by performance measures. The use case & business logic was carefully reviewed to ensure it's safe to take shortcuts, before making the change. If you run into failing scenario, please feel free to create a work item against it and/or file a PR to fix it.
# Use
All the classes included in this package are meant to be direct replacements of their corresponding system classes. Finally, you'll find places with code commented out. That's to easily track what has been tried & what proved to perform worse.
* `IList<T>` ⇨ `ILongList<T>`
* `List<T>` ⇨ `RecyclableLongList<T>` At any time, if you know & can show in your benchmarks that the code can be simplified without impacting the performance, feel free to file a PR with proposed changes. Please include benchmark results from your testing to speed up things.
* `Queue<T>` ⇨ `RecyclableQueue<T>`
* `PriorityQueue<T>` ⇨ `RecyclableSortedList<T>` # Thread Safety
* `SortedList<T>` ⇨ `RecyclableSortedList<T>` The `public static` members of the classes are thread safe. Any instance members are not guaranteed to be thread safe. You need to implement locking mechanism to safely use the classes in a multi-threading environment.
* `Stack<T>` ⇨ `RecyclableStack<T>`
# Use
Examples of each of the above classes are provided below. Please refer automated tests for more. All the classes included in this package are meant to be direct replacements of their corresponding system classes.
* `IList<T>` ⇨ `ILongList<T>`
## `RecyclableList<T>` * `List<T>` ⇨ `RecyclableLongList<T>`
It's the closest equivalent to `List<T>` in regards to functionality. It supports items' removal & delivers the best performance in most scenarios. But it's limited to `int` range, tough. If you find that acceptable, my recommendation is to use this class as the direct replacement for `List<T>`. * `Queue<T>` ⇨ `RecyclableQueue<T>`
* `PriorityQueue<T>` ⇨ `RecyclableSortedList<T>`
Do note, that it implements `ILongList<T>` interface, too. This is to allow having common code base regardless, if you use the basic `RecyclableList<T>` or it's `long`-range version - `RecyclableLongList<T>`. By doing that you can switch between them with highly limited changes in code. * `SortedList<T>` ⇨ `RecyclableSortedList<T>`
* `Stack<T>` ⇨ `RecyclableStack<T>`
## `RecyclableLongList<T>`
Examples of each of the above classes are provided below. Please refer automated tests for more.
```CSharp
public void RecyclableListExample() ## `RecyclableList<T>`
{ It's the closest equivalent to `List<T>` in regards to functionality. It supports items' removal & delivers the best performance in most scenarios. But it's limited to `int` range, tough. If you find that acceptable, my recommendation is to use this class as the direct replacement for `List<T>`.
int[] testNumbers = new[] { 1, 2, 2, 3 };
Do note, that it implements `ILongList<T>` interface, too. This is to allow having common code base regardless, if you use the basic `RecyclableList<T>` or it's `long`-range version - `RecyclableLongList<T>`. By doing that you can switch between them with highly limited changes in code.
// Create
using var list = new RecyclableLongList<int>(); ## `RecyclableLongList<T>`
// Add items ```CSharp
foreach (var index in testNumbers) public void RecyclableListExample()
{ {
list.Add(index); int[] testNumbers = new[] { 1, 2, 2, 3 };
}
// Create
foreach (var item in list) using var list = new RecyclableLongList<int>();
{
Console.WriteLine(item); // Add items
} foreach (var index in testNumbers)
{
// This will iterate over this[long index] list.Add(index);
for (var itemIdx = 0; itemIdx <= list.LongCount; itemIdx++) }
{
Console.WriteLine(list[itemIdx]); foreach (var item in list)
} {
Console.WriteLine(item);
// This will iterate over this[int index] and may overflow if there are > int.MaxValue no. of items on the list. See: Architecture Decisions }
for (var itemIdx = 0; itemIdx <= list.LongCount; itemIdx++)
{ // This will iterate over this[long index]
Console.WriteLine(list[itemIdx]); for (var itemIdx = 0; itemIdx <= list.LongCount; itemIdx++)
} {
Console.WriteLine(list[itemIdx]);
list.Clear(); }
}
``` // This will iterate over this[int index] and may overflow if there are > int.MaxValue no. of items on the list. See: Architecture Decisions
for (var itemIdx = 0; itemIdx <= list.LongCount; itemIdx++)
### [Benchmark results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md) {
Console.WriteLine(list[itemIdx]);
}
list.Clear();
}
```
### [Benchmark results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md)
@@ -1,62 +1,62 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_Add() public void Array_Add()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
var list = new long[dataCount]; var list = new long[dataCount];
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list[i] = data[i]; list[i] = data[i];
} }
} }
public void List_Add() public void List_Add()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
var list = new List<long>(); var list = new List<long>();
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.Add(data[i]); list.Add(data[i]);
} }
} }
public void PooledList_Add() public void PooledList_Add()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
using var list = new PooledList<long>(ClearMode.Auto); using var list = new PooledList<long>(ClearMode.Auto);
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.Add(data[i]); list.Add(data[i]);
} }
} }
public void RecyclableList_Add() public void RecyclableList_Add()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
using var list = new RecyclableList<long>(); using var list = new RecyclableList<long>();
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.Add(data[i]); list.Add(data[i]);
} }
} }
public void RecyclableLongList_Add() public void RecyclableLongList_Add()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
using var list = new RecyclableLongList<long>(); using var list = new RecyclableLongList<long>();
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.Add(data[i]); list.Add(data[i]);
} }
} }
} }
} }
@@ -1,42 +1,42 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_AddRangeWhenSourceIsArray() public void Array_AddRangeWhenSourceIsArray()
{ {
var data = TestObjects; var data = TestObjects;
var list = new long[TestObjectCount]; var list = new long[TestObjectCount];
data.CopyTo(list, 0); data.CopyTo(list, 0);
} }
public void List_AddRangeWhenSourceIsArray() public void List_AddRangeWhenSourceIsArray()
{ {
var data = TestObjects; var data = TestObjects;
var list = new List<long>(); var list = new List<long>();
list.AddRange(data); list.AddRange(data);
} }
public void PooledList_AddRangeWhenSourceIsArray() public void PooledList_AddRangeWhenSourceIsArray()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new PooledList<long>(ClearMode.Auto); using var list = new PooledList<long>(ClearMode.Auto);
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableList_AddRangeWhenSourceIsArray() public void RecyclableList_AddRangeWhenSourceIsArray()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableList<long>(); using var list = new RecyclableList<long>();
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableLongList_AddRangeWhenSourceIsArray() public void RecyclableLongList_AddRangeWhenSourceIsArray()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize); using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
list.AddRange(data); list.AddRange(data);
} }
} }
} }
@@ -1,47 +1,47 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_AddRangeWhenSourceIsIEnumerable() public void Array_AddRangeWhenSourceIsIEnumerable()
{ {
var data = TestObjectsAsIEnumerable; var data = TestObjectsAsIEnumerable;
var list = new long[TestObjectCount]; var list = new long[TestObjectCount];
int itemIndex = 0; int itemIndex = 0;
foreach (var item in data) foreach (var item in data)
{ {
list[itemIndex++] = item; list[itemIndex++] = item;
} }
} }
public void List_AddRangeWhenSourceIsIEnumerable() public void List_AddRangeWhenSourceIsIEnumerable()
{ {
var data = TestObjectsAsIEnumerable; var data = TestObjectsAsIEnumerable;
var list = new List<long>(); var list = new List<long>();
list.AddRange(data); list.AddRange(data);
} }
public void PooledList_AddRangeWhenSourceIsIEnumerable() public void PooledList_AddRangeWhenSourceIsIEnumerable()
{ {
var data = TestObjectsAsIEnumerable; var data = TestObjectsAsIEnumerable;
using var list = new PooledList<long>(ClearMode.Auto); using var list = new PooledList<long>(ClearMode.Auto);
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableList_AddRangeWhenSourceIsIEnumerable() public void RecyclableList_AddRangeWhenSourceIsIEnumerable()
{ {
var data = TestObjectsAsIEnumerable; var data = TestObjectsAsIEnumerable;
using var list = new RecyclableList<long>(); using var list = new RecyclableList<long>();
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableLongList_AddRangeWhenSourceIsIEnumerable() public void RecyclableLongList_AddRangeWhenSourceIsIEnumerable()
{ {
var data = TestObjectsAsIEnumerable; var data = TestObjectsAsIEnumerable;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize); using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
list.AddRange(data); list.AddRange(data);
} }
} }
} }
@@ -1,42 +1,42 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_AddRangeWhenSourceIsIList() public void Array_AddRangeWhenSourceIsIList()
{ {
var data = (IList<long>)TestObjectsAsRecyclableLongList; var data = (IList<long>)TestObjectsAsRecyclableLongList;
var list = new long[TestObjectCount]; var list = new long[TestObjectCount];
data.CopyTo(list, 0); data.CopyTo(list, 0);
} }
public void List_AddRangeWhenSourceIsIList() public void List_AddRangeWhenSourceIsIList()
{ {
var data = (IList<long>)TestObjectsAsRecyclableLongList; var data = (IList<long>)TestObjectsAsRecyclableLongList;
var list = new List<long>(); var list = new List<long>();
list.AddRange(data); list.AddRange(data);
} }
public void PooledList_AddRangeWhenSourceIsIList() public void PooledList_AddRangeWhenSourceIsIList()
{ {
var data = (IList<long>)TestObjectsAsRecyclableLongList; var data = (IList<long>)TestObjectsAsRecyclableLongList;
using var list = new PooledList<long>(ClearMode.Auto); using var list = new PooledList<long>(ClearMode.Auto);
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableList_AddRangeWhenSourceIsIList() public void RecyclableList_AddRangeWhenSourceIsIList()
{ {
var data = (IList<long>)TestObjectsAsRecyclableLongList; var data = (IList<long>)TestObjectsAsRecyclableLongList;
using var list = new RecyclableList<long>(); using var list = new RecyclableList<long>();
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableLongList_AddRangeWhenSourceIsIList() public void RecyclableLongList_AddRangeWhenSourceIsIList()
{ {
var data = (IList<long>)TestObjectsAsRecyclableLongList; var data = (IList<long>)TestObjectsAsRecyclableLongList;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize); using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
list.AddRange(data); list.AddRange(data);
} }
} }
} }
@@ -1,42 +1,42 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_AddRangeWhenSourceIsList() public void Array_AddRangeWhenSourceIsList()
{ {
var data = TestObjectsAsList; var data = TestObjectsAsList;
var list = new long[TestObjectCount]; var list = new long[TestObjectCount];
data.CopyTo(list, 0); data.CopyTo(list, 0);
} }
public void List_AddRangeWhenSourceIsList() public void List_AddRangeWhenSourceIsList()
{ {
var data = TestObjectsAsList; var data = TestObjectsAsList;
var list = new List<long>(); var list = new List<long>();
list.AddRange(data); list.AddRange(data);
} }
public void PooledList_AddRangeWhenSourceIsList() public void PooledList_AddRangeWhenSourceIsList()
{ {
var data = TestObjectsAsList; var data = TestObjectsAsList;
using var list = new PooledList<long>(ClearMode.Auto); using var list = new PooledList<long>(ClearMode.Auto);
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableList_AddRangeWhenSourceIsList() public void RecyclableList_AddRangeWhenSourceIsList()
{ {
var data = TestObjectsAsList; var data = TestObjectsAsList;
using var list = new RecyclableList<long>(); using var list = new RecyclableList<long>();
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableLongList_AddRangeWhenSourceIsList() public void RecyclableLongList_AddRangeWhenSourceIsList()
{ {
var data = TestObjectsAsList; var data = TestObjectsAsList;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize); using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
list.AddRange(data); list.AddRange(data);
} }
} }
} }
@@ -1,28 +1,28 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void PooledList_AddRangeWhenSourceIsPooledList() public void PooledList_AddRangeWhenSourceIsPooledList()
{ {
var data = TestObjectsAsPooledList; var data = TestObjectsAsPooledList;
using var list = new PooledList<long>(capacity: TestObjectCount, ClearMode.Auto); using var list = new PooledList<long>(capacity: TestObjectCount, ClearMode.Auto);
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableList_AddRangeWhenSourceIsRecyclableList() public void RecyclableList_AddRangeWhenSourceIsRecyclableList()
{ {
var data = TestObjectsAsRecyclableList; var data = TestObjectsAsRecyclableList;
using var list = new RecyclableList<long>(TestObjectCount); using var list = new RecyclableList<long>(TestObjectCount);
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableLongList_AddRangeWhenSourceIsRecyclableLongList() public void RecyclableLongList_AddRangeWhenSourceIsRecyclableLongList()
{ {
var data = TestObjectsAsRecyclableList; var data = TestObjectsAsRecyclableList;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: TestObjectCount); using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: TestObjectCount);
list.AddRange(data); list.AddRange(data);
} }
} }
} }
@@ -1,47 +1,47 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_AddRange_WithCapacity() public void Array_AddRange_WithCapacity()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
var list = new long[dataCount]; var list = new long[dataCount];
data.CopyTo(list, 0); data.CopyTo(list, 0);
} }
public void List_AddRange_WithCapacity() public void List_AddRange_WithCapacity()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
var list = new List<long>(dataCount); var list = new List<long>(dataCount);
list.AddRange(data); list.AddRange(data);
} }
public void PooledList_AddRange_WithCapacity() public void PooledList_AddRange_WithCapacity()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
using var list = new PooledList<long>(capacity: dataCount, ClearMode.Auto); using var list = new PooledList<long>(capacity: dataCount, ClearMode.Auto);
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableList_AddRange_WithCapacity() public void RecyclableList_AddRange_WithCapacity()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
using var list = new RecyclableList<long>(dataCount); using var list = new RecyclableList<long>(dataCount);
list.AddRange(data); list.AddRange(data);
} }
public void RecyclableLongList_AddRange_WithCapacity() public void RecyclableLongList_AddRange_WithCapacity()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: dataCount); using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: dataCount);
list.AddRange(data); list.AddRange(data);
} }
} }
} }
@@ -1,51 +1,51 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void List_Add_WithCapacity() public void List_Add_WithCapacity()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
var list = new List<long>(dataCount); var list = new List<long>(dataCount);
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.Add(data[i]); list.Add(data[i]);
} }
} }
public void PooledList_Add_WithCapacity() public void PooledList_Add_WithCapacity()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
using var list = new PooledList<long>(capacity: dataCount, ClearMode.Auto); using var list = new PooledList<long>(capacity: dataCount, ClearMode.Auto);
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.Add(data[i]); list.Add(data[i]);
} }
} }
public void RecyclableList_Add_WithCapacity() public void RecyclableList_Add_WithCapacity()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
using var list = new RecyclableList<long>(dataCount); using var list = new RecyclableList<long>(dataCount);
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.Add(data[i]); list.Add(data[i]);
} }
} }
public void RecyclableLongList_Add_WithCapacity() public void RecyclableLongList_Add_WithCapacity()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: dataCount); using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: dataCount);
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.Add(data[i]); list.Add(data[i]);
} }
} }
} }
} }
@@ -1,65 +1,65 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_BinarySearch_BestAndWorstCases() public void Array_BinarySearch_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(Array.BinarySearch(list, data[i])); DoNothing(Array.BinarySearch(list, data[i]));
DoNothing(Array.BinarySearch(list, data[^(i + 1)])); DoNothing(Array.BinarySearch(list, data[^(i + 1)]));
} }
} }
public void List_BinarySearch_BestAndWorstCases() public void List_BinarySearch_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.BinarySearch(data[i])); DoNothing(list.BinarySearch(data[i]));
DoNothing(list.BinarySearch(data[^(i + 1)])); DoNothing(list.BinarySearch(data[^(i + 1)]));
} }
} }
public void PooledList_BinarySearch_BestAndWorstCases() public void PooledList_BinarySearch_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsPooledList; var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.BinarySearch(data[i])); DoNothing(list.BinarySearch(data[i]));
DoNothing(list.BinarySearch(data[^(i + 1)])); DoNothing(list.BinarySearch(data[^(i + 1)]));
} }
} }
public void RecyclableList_BinarySearch_BestAndWorstCases() public void RecyclableList_BinarySearch_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.BinarySearch(data[i])); DoNothing(list.BinarySearch(data[i]));
DoNothing(list.BinarySearch(data[^(i + 1)])); DoNothing(list.BinarySearch(data[^(i + 1)]));
} }
} }
// public void RecyclableLongList_BinarySearch_BestAndWorstCases() // public void RecyclableLongList_BinarySearch_BestAndWorstCases()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList; // var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods; // var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// DoNothing(list.BinarySearch(data[i])); // DoNothing(list.BinarySearch(data[i]));
// DoNothing(list.BinarySearch(data[^(i + 1)])); // DoNothing(list.BinarySearch(data[^(i + 1)]));
// } // }
// } // }
} }
} }
@@ -1,60 +1,60 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_Contains_FirstItems() public void Array_Contains_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(Array.IndexOf(list, data[i]) >= 0); DoNothing(Array.IndexOf(list, data[i]) >= 0);
} }
} }
public void List_Contains_FirstItems() public void List_Contains_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Contains(data[i])); DoNothing(list.Contains(data[i]));
} }
} }
public void PooledList_Contains_FirstItems() public void PooledList_Contains_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsPooledList; var list = TestObjectsAsPooledList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Contains(data[i])); DoNothing(list.Contains(data[i]));
} }
} }
public void RecyclableList_Contains_FirstItems() public void RecyclableList_Contains_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Contains(data[i])); DoNothing(list.Contains(data[i]));
} }
} }
public void RecyclableLongList_Contains_FirstItems() public void RecyclableLongList_Contains_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableLongList; var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Contains(data[i])); DoNothing(list.Contains(data[i]));
} }
} }
} }
} }
@@ -1,60 +1,60 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_Contains_LastItems() public void Array_Contains_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Contains(data[^(i + 1)])); DoNothing(list.Contains(data[^(i + 1)]));
} }
} }
public void List_Contains_LastItems() public void List_Contains_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Contains(data[^(i + 1)])); DoNothing(list.Contains(data[^(i + 1)]));
} }
} }
public void PooledList_Contains_LastItems() public void PooledList_Contains_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsPooledList; var list = TestObjectsAsPooledList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Contains(data[^(i + 1)])); DoNothing(list.Contains(data[^(i + 1)]));
} }
} }
public void RecyclableList_Contains_LastItems() public void RecyclableList_Contains_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Contains(data[^(i + 1)])); DoNothing(list.Contains(data[^(i + 1)]));
} }
} }
public void RecyclableLongList_Contains_LastItems() public void RecyclableLongList_Contains_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableLongList; var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Contains(data[^(i + 1)])); DoNothing(list.Contains(data[^(i + 1)]));
} }
} }
} }
} }
@@ -1,48 +1,48 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_ConvertAll() public void Array_ConvertAll()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
DoNothing(Array.ConvertAll(list, static (item) => (long) item)); DoNothing(Array.ConvertAll(list, static (item) => (long) item));
} }
public void List_ConvertAll() public void List_ConvertAll()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
DoNothing(list.ConvertAll(static (item) => (long)item)); DoNothing(list.ConvertAll(static (item) => (long)item));
} }
public void PooledList_ConvertAll() public void PooledList_ConvertAll()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsPooledList; var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
using var converted = list.ConvertAll(static (item) => (long)item); using var converted = list.ConvertAll(static (item) => (long)item);
DoNothing(converted); DoNothing(converted);
} }
public void RecyclableList_ConvertAll() public void RecyclableList_ConvertAll()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item); using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
DoNothing(converted); DoNothing(converted);
} }
// public void RecyclableLongList_ConvertAll() // public void RecyclableLongList_ConvertAll()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList; // var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods; // var dataCount = TestObjectCountForSlowMethods;
// using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item); // using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
// DoNothing(converted); // DoNothing(converted);
// } // }
} }
} }
@@ -1,35 +1,35 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_Count() public void Array_Count()
{ {
var data = TestObjects; var data = TestObjects;
DoNothing(data.Length); DoNothing(data.Length);
} }
public void List_Count() public void List_Count()
{ {
var data = TestObjectsAsList; var data = TestObjectsAsList;
DoNothing(data.Count); DoNothing(data.Count);
} }
public void PooledList_Count() public void PooledList_Count()
{ {
var data = TestObjectsAsPooledList; var data = TestObjectsAsPooledList;
DoNothing(data.Count); DoNothing(data.Count);
} }
public void RecyclableList_Count() public void RecyclableList_Count()
{ {
var data = TestObjectsAsRecyclableList; var data = TestObjectsAsRecyclableList;
DoNothing(data.Count); DoNothing(data.Count);
} }
public void RecyclableLongList_Count() public void RecyclableLongList_Count()
{ {
var data = TestObjectsAsRecyclableLongList; var data = TestObjectsAsRecyclableLongList;
DoNothing(data.Count); DoNothing(data.Count);
} }
} }
} }
@@ -1,39 +1,39 @@
using Collections.Pooled; using Collections.Pooled;
#pragma warning disable CA1822, CA1825 #pragma warning disable CA1822, CA1825
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_Create() public void Array_Create()
{ {
var list = new long[0]; var list = new long[0];
DoNothing(list); DoNothing(list);
} }
public void List_Create() public void List_Create()
{ {
var list = new List<long>(); var list = new List<long>();
DoNothing(list); DoNothing(list);
} }
public void PooledList_Create() public void PooledList_Create()
{ {
using var list = new PooledList<long>(ClearMode.Auto); using var list = new PooledList<long>(ClearMode.Auto);
DoNothing(list); DoNothing(list);
} }
public void RecyclableList_Create() public void RecyclableList_Create()
{ {
using var list = new RecyclableList<long>(); using var list = new RecyclableList<long>();
DoNothing(list); DoNothing(list);
} }
public void RecyclableLongList_Create() public void RecyclableLongList_Create()
{ {
using var list = new RecyclableLongList<long>(); using var list = new RecyclableLongList<long>();
DoNothing(list); DoNothing(list);
} }
} }
} }
@@ -1,37 +1,37 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_Create_WithCapacity() public void Array_Create_WithCapacity()
{ {
var data = new long[TestObjectCount]; var data = new long[TestObjectCount];
DoNothing(data); DoNothing(data);
} }
public void List_Create_WithCapacity() public void List_Create_WithCapacity()
{ {
var list = new List<long>(TestObjectCount); var list = new List<long>(TestObjectCount);
DoNothing(list); DoNothing(list);
} }
public void PooledList_Create_WithCapacity() public void PooledList_Create_WithCapacity()
{ {
using var list = new PooledList<long>(capacity: TestObjectCount, ClearMode.Auto); using var list = new PooledList<long>(capacity: TestObjectCount, ClearMode.Auto);
DoNothing(list); DoNothing(list);
} }
public void RecyclableList_Create_WithCapacity() public void RecyclableList_Create_WithCapacity()
{ {
using var list = new RecyclableList<long>(TestObjectCount); using var list = new RecyclableList<long>(TestObjectCount);
DoNothing(list); DoNothing(list);
} }
public void RecyclableLongList_Create_WithCapacity() public void RecyclableLongList_Create_WithCapacity()
{ {
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: TestObjectCount); using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: TestObjectCount);
DoNothing(list); DoNothing(list);
} }
} }
} }
@@ -1,65 +1,65 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_Exists_BestAndWorstCases() public void Array_Exists_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(Array.Exists(list, (x) => x == data[i])); DoNothing(Array.Exists(list, (x) => x == data[i]));
DoNothing(Array.Exists(list, (x) => x == data[^(i + 1)])); DoNothing(Array.Exists(list, (x) => x == data[^(i + 1)]));
} }
} }
public void List_Exists_BestAndWorstCases() public void List_Exists_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Exists((x) => x == data[i])); DoNothing(list.Exists((x) => x == data[i]));
DoNothing(list.Exists((x) => x == data[^(i + 1)])); DoNothing(list.Exists((x) => x == data[^(i + 1)]));
} }
} }
public void PooledList_Exists_BestAndWorstCases() public void PooledList_Exists_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsPooledList; var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Exists((x) => x == data[i])); DoNothing(list.Exists((x) => x == data[i]));
DoNothing(list.Exists((x) => x == data[^(i + 1)])); DoNothing(list.Exists((x) => x == data[^(i + 1)]));
} }
} }
public void RecyclableList_Exists_BestAndWorstCases() public void RecyclableList_Exists_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Exists((x) => x == data[i])); DoNothing(list.Exists((x) => x == data[i]));
DoNothing(list.Exists((x) => x == data[^(i + 1)])); DoNothing(list.Exists((x) => x == data[^(i + 1)]));
} }
} }
// public void RecyclableLongList_Exists_BestAndWorstCases() // public void RecyclableLongList_Exists_BestAndWorstCases()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList; // var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods; // var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// DoNothing(list.Exists((x) => x == data[i])); // DoNothing(list.Exists((x) => x == data[i]));
// DoNothing(list.Exists((x) => x == data[^(i + 1)])); // DoNothing(list.Exists((x) => x == data[^(i + 1)]));
// } // }
// } // }
} }
} }
@@ -1,80 +1,80 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_FindAll_BestAndWorstCases() public void Array_FindAll_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(Array.FindAll(list, (item) => item == data[i])); DoNothing(Array.FindAll(list, (item) => item == data[i]));
DoNothing(Array.FindAll(list, (item) => item == data[^(i + 1)])); DoNothing(Array.FindAll(list, (item) => item == data[^(i + 1)]));
} }
} }
public void List_FindAll_BestAndWorstCases() public void List_FindAll_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.FindAll((item) => item == data[i])); DoNothing(list.FindAll((item) => item == data[i]));
DoNothing(list.FindAll((item) => item == data[^(i + 1)])); DoNothing(list.FindAll((item) => item == data[^(i + 1)]));
} }
} }
public void PooledList_FindAll_BestAndWorstCases() public void PooledList_FindAll_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsPooledList; var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
var temp = list.FindAll((item) => item == data[i]); var temp = list.FindAll((item) => item == data[i]);
DoNothing(temp); DoNothing(temp);
temp.Dispose(); temp.Dispose();
temp = list.FindAll((item) => item == data[^(i + 1)]); temp = list.FindAll((item) => item == data[^(i + 1)]);
DoNothing(temp); DoNothing(temp);
temp.Dispose(); temp.Dispose();
} }
} }
public void RecyclableList_FindAll_BestAndWorstCases() public void RecyclableList_FindAll_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
var temp = list.FindAll((item) => item == data[i]); var temp = list.FindAll((item) => item == data[i]);
DoNothing(temp); DoNothing(temp);
temp.Dispose(); temp.Dispose();
temp = list.FindAll((item) => item == data[^(i + 1)]); temp = list.FindAll((item) => item == data[^(i + 1)]);
DoNothing(temp); DoNothing(temp);
temp.Dispose(); temp.Dispose();
} }
} }
// public void RecyclableLongList_FindAll_BestAndWorstCases() // public void RecyclableLongList_FindAll_BestAndWorstCases()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList; // var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods; // var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// var temp = list.FindAll((item) => item == data[i]); // var temp = list.FindAll((item) => item == data[i]);
// DoNothing(temp); // DoNothing(temp);
// temp.Dispose(); // temp.Dispose();
// temp = list.FindAll((item) => item == data[^(i + 1)]); // temp = list.FindAll((item) => item == data[^(i + 1)]);
// DoNothing(temp); // DoNothing(temp);
// temp.Dispose(); // temp.Dispose();
// } // }
// } // }
} }
} }
@@ -1,63 +1,63 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_FindLastIndex_BestAndWorstCases() public void Array_FindLastIndex_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}"); // Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}"); // Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
// Console.WriteLine($"data.Length={data.Length}"); // Console.WriteLine($"data.Length={data.Length}");
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}"); // Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}"); // Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[i])); DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[i]));
DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[^(i + 1)])); DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[^(i + 1)]));
} }
} }
public void List_FindLastIndex_BestAndWorstCases() public void List_FindLastIndex_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}"); // Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}"); // Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
// Console.WriteLine($"data.Length={data.Length}"); // Console.WriteLine($"data.Length={data.Length}");
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}"); // Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}"); // Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
DoNothing(list.FindLastIndex(0, (x) => x == data[i])); DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)])); DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
} }
} }
public void RecyclableList_FindLastIndex_BestAndWorstCases() public void RecyclableList_FindLastIndex_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.FindLastIndex(0, (x) => x == data[i])); DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)])); DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
} }
} }
// public void RecyclableLongList_FindLastIndex_BestAndWorstCases() // public void RecyclableLongList_FindLastIndex_BestAndWorstCases()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList; // var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods; // var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// DoNothing(list.FindLastIndex(0, (x) => x == data[i])); // DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
// DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)])); // DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
// } // }
// } // }
} }
} }
@@ -1,53 +1,53 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_FindLast_BestAndWorstCases() public void Array_FindLast_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(Array.FindLast(list, (x) => x == data[i])); DoNothing(Array.FindLast(list, (x) => x == data[i]));
DoNothing(Array.FindLast(list, (x) => x == data[^(i + 1)])); DoNothing(Array.FindLast(list, (x) => x == data[^(i + 1)]));
} }
} }
public void List_FindLast_BestAndWorstCases() public void List_FindLast_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.FindLast((x) => x == data[i])); DoNothing(list.FindLast((x) => x == data[i]));
DoNothing(list.FindLast((x) => x == data[^(i + 1)])); DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
} }
} }
public void RecyclableList_FindLast_BestAndWorstCases() public void RecyclableList_FindLast_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.FindLast((x) => x == data[i])); DoNothing(list.FindLast((x) => x == data[i]));
DoNothing(list.FindLast((x) => x == data[^(i + 1)])); DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
} }
} }
// public void RecyclableLongList_FindLast_BestAndWorstCases() // public void RecyclableLongList_FindLast_BestAndWorstCases()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList; // var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods; // var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// DoNothing(list.FindLast((x) => x == data[i])); // DoNothing(list.FindLast((x) => x == data[i]));
// DoNothing(list.FindLast((x) => x == data[^(i + 1)])); // DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
// } // }
// } // }
} }
} }
@@ -1,53 +1,53 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_Find_BestAndWorstCases() public void Array_Find_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(Array.Find(list, (x) => x == data[i])); DoNothing(Array.Find(list, (x) => x == data[i]));
DoNothing(Array.Find(list, (x) => x == data[^(i + 1)])); DoNothing(Array.Find(list, (x) => x == data[^(i + 1)]));
} }
} }
public void List_Find_BestAndWorstCases() public void List_Find_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Find((x) => x == data[i])); DoNothing(list.Find((x) => x == data[i]));
DoNothing(list.Find((x) => x == data[^(i + 1)])); DoNothing(list.Find((x) => x == data[^(i + 1)]));
} }
} }
public void RecyclableList_Find_BestAndWorstCases() public void RecyclableList_Find_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Find((x) => x == data[i])); DoNothing(list.Find((x) => x == data[i]));
DoNothing(list.Find((x) => x == data[^(i + 1)])); DoNothing(list.Find((x) => x == data[^(i + 1)]));
} }
} }
// public void RecyclableLongList_Find_BestAndWorstCases() // public void RecyclableLongList_Find_BestAndWorstCases()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList; // var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods; // var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// DoNothing(list.Find((x) => x == data[i])); // DoNothing(list.Find((x) => x == data[i]));
// DoNothing(list.Find((x) => x == data[^(i + 1)])); // DoNothing(list.Find((x) => x == data[^(i + 1)]));
// } // }
// } // }
} }
} }
@@ -1,85 +1,85 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_ForEach() public void Array_ForEach()
{ {
foreach (var item in TestObjects) foreach (var item in TestObjects)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void Array_VersionedForEach() public void Array_VersionedForEach()
{ {
foreach (var item in TestObjects) foreach (var item in TestObjects)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void List_ForEach() public void List_ForEach()
{ {
foreach (var item in TestObjectsAsList) foreach (var item in TestObjectsAsList)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void List_VersionedForEach() public void List_VersionedForEach()
{ {
foreach (var item in TestObjectsAsList) foreach (var item in TestObjectsAsList)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void PooledList_ForEach() public void PooledList_ForEach()
{ {
foreach (var item in TestObjectsAsPooledList) foreach (var item in TestObjectsAsPooledList)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void PooledList_VersionedForEach() public void PooledList_VersionedForEach()
{ {
foreach (var item in TestObjectsAsPooledList) foreach (var item in TestObjectsAsPooledList)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void RecyclableList_ForEach() public void RecyclableList_ForEach()
{ {
foreach (var item in TestObjectsAsRecyclableList) foreach (var item in TestObjectsAsRecyclableList)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void RecyclableList_VersionedForEach() public void RecyclableList_VersionedForEach()
{ {
foreach (var item in (IRecyclableVersionedList<long>)TestObjectsAsRecyclableList) foreach (var item in (IRecyclableVersionedList<long>)TestObjectsAsRecyclableList)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void RecyclableLongList_ForEach() public void RecyclableLongList_ForEach()
{ {
foreach (var item in TestObjectsAsRecyclableLongList) foreach (var item in TestObjectsAsRecyclableLongList)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void RecyclableLongList_VersionedForEach() public void RecyclableLongList_VersionedForEach()
{ {
foreach (var item in (IRecyclableVersionedLongList<long>)TestObjectsAsRecyclableLongList) foreach (var item in (IRecyclableVersionedLongList<long>)TestObjectsAsRecyclableLongList)
{ {
DoNothing(item); DoNothing(item);
} }
} }
} }
} }
@@ -1,158 +1,158 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_GetItem() public void Array_GetItem()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(data[i]); DoNothing(data[i]);
} }
} }
public void List_GetItem() public void List_GetItem()
{ {
var data = TestObjectsAsList; var data = TestObjectsAsList;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(data[i]); DoNothing(data[i]);
} }
} }
public void PooledList_GetItem() public void PooledList_GetItem()
{ {
var data = TestObjectsAsPooledList; var data = TestObjectsAsPooledList;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(data[i]); DoNothing(data[i]);
} }
} }
public void RecyclableList_GetItem() public void RecyclableList_GetItem()
{ {
var data = TestObjectsAsRecyclableList; var data = TestObjectsAsRecyclableList;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(data[i]); DoNothing(data[i]);
} }
} }
public void RecyclableLongList_GetItem() public void RecyclableLongList_GetItem()
{ {
var data = TestObjectsAsRecyclableLongList; var data = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(data[i]); DoNothing(data[i]);
} }
} }
public void RecyclableLongList_GetItem_AsSpan() public void RecyclableLongList_GetItem_AsSpan()
{ {
var data = TestObjectsAsRecyclableLongList; var data = TestObjectsAsRecyclableLongList;
int blockSize = data.BlockSize; int blockSize = data.BlockSize;
int nextItemBlockIndex = data.NextItemBlockIndex; int nextItemBlockIndex = data.NextItemBlockIndex;
var memoryBlocks = new Span<long[]>(data.AsArray, 0, data.LastBlockWithData + 1); var memoryBlocks = new Span<long[]>(data.AsArray, 0, data.LastBlockWithData + 1);
for (var blockIndex = 0; blockIndex < nextItemBlockIndex; blockIndex++) for (var blockIndex = 0; blockIndex < nextItemBlockIndex; blockIndex++)
{ {
var memoryBlock = new Span<long>(memoryBlocks[blockIndex], 0, blockSize); var memoryBlock = new Span<long>(memoryBlocks[blockIndex], 0, blockSize);
for (var itemIndex = 0; itemIndex < blockSize; itemIndex++) for (var itemIndex = 0; itemIndex < blockSize; itemIndex++)
{ {
DoNothing(memoryBlock[itemIndex]); DoNothing(memoryBlock[itemIndex]);
} }
} }
if (nextItemBlockIndex == data.LastBlockWithData) if (nextItemBlockIndex == data.LastBlockWithData)
{ {
var memoryBlock = new Span<long>(memoryBlocks[nextItemBlockIndex], 0, data.NextItemIndex); var memoryBlock = new Span<long>(memoryBlocks[nextItemBlockIndex], 0, data.NextItemIndex);
for (var itemIndex = 0; itemIndex < memoryBlock.Length; itemIndex++) for (var itemIndex = 0; itemIndex < memoryBlock.Length; itemIndex++)
{ {
DoNothing(memoryBlock[itemIndex]); DoNothing(memoryBlock[itemIndex]);
} }
} }
} }
public void RecyclableLongList_GetItem_AsArray() public void RecyclableLongList_GetItem_AsArray()
{ {
var data = TestObjectsAsRecyclableLongList; var data = TestObjectsAsRecyclableLongList;
int blockSize = data.BlockSize; int blockSize = data.BlockSize;
int nextItemBlockIndex = data.NextItemBlockIndex; int nextItemBlockIndex = data.NextItemBlockIndex;
var memoryBlocks = data.AsArray; var memoryBlocks = data.AsArray;
for (var blockIndex = 0; blockIndex < nextItemBlockIndex; blockIndex++) for (var blockIndex = 0; blockIndex < nextItemBlockIndex; blockIndex++)
{ {
var memoryBlock = memoryBlocks[blockIndex]; var memoryBlock = memoryBlocks[blockIndex];
for (var itemIndex = 0; itemIndex < blockSize; itemIndex++) for (var itemIndex = 0; itemIndex < blockSize; itemIndex++)
{ {
DoNothing(memoryBlock[itemIndex]); DoNothing(memoryBlock[itemIndex]);
} }
} }
if (nextItemBlockIndex == data.LastBlockWithData) if (nextItemBlockIndex == data.LastBlockWithData)
{ {
var memoryBlock = memoryBlocks[nextItemBlockIndex]; var memoryBlock = memoryBlocks[nextItemBlockIndex];
blockSize = data.NextItemIndex; blockSize = data.NextItemIndex;
for (var itemIndex = 0; itemIndex < blockSize; itemIndex++) for (var itemIndex = 0; itemIndex < blockSize; itemIndex++)
{ {
DoNothing(memoryBlock[itemIndex]); DoNothing(memoryBlock[itemIndex]);
} }
} }
} }
public void RecyclableLongList_GetItem_AsForEach() public void RecyclableLongList_GetItem_AsForEach()
{ {
var data = TestObjectsAsRecyclableLongList; var data = TestObjectsAsRecyclableLongList;
foreach (var item in data) foreach (var item in data)
{ {
DoNothing(item); DoNothing(item);
} }
} }
public void RecyclableLongList_GetItem_AsWhile() public void RecyclableLongList_GetItem_AsWhile()
{ {
var data = TestObjectsAsRecyclableLongList; var data = TestObjectsAsRecyclableLongList;
long dataCount = TestObjectCount; long dataCount = TestObjectCount;
if (dataCount == 0) if (dataCount == 0)
{ {
return; return;
} }
int blockSize = data.BlockSize; int blockSize = data.BlockSize;
int nextItemBlockIndex = data.NextItemBlockIndex; int nextItemBlockIndex = data.NextItemBlockIndex;
int itemIndex = 0; int itemIndex = 0;
int blockIndex = 0; int blockIndex = 0;
var memoryBlocks = data.AsArray; var memoryBlocks = data.AsArray;
var memoryBlock = memoryBlocks[0]; var memoryBlock = memoryBlocks[0];
while (blockIndex < nextItemBlockIndex) while (blockIndex < nextItemBlockIndex)
{ {
DoNothing(memoryBlock[itemIndex]); DoNothing(memoryBlock[itemIndex]);
if (itemIndex + 1 < blockSize) if (itemIndex + 1 < blockSize)
{ {
itemIndex++; itemIndex++;
} }
else else
{ {
blockIndex++; blockIndex++;
memoryBlock = memoryBlocks[blockIndex]; memoryBlock = memoryBlocks[blockIndex];
itemIndex = 0; itemIndex = 0;
} }
} }
if (nextItemBlockIndex == data.LastBlockWithData) if (nextItemBlockIndex == data.LastBlockWithData)
{ {
memoryBlock = memoryBlocks[nextItemBlockIndex]; memoryBlock = memoryBlocks[nextItemBlockIndex];
blockSize = data.NextItemIndex; blockSize = data.NextItemIndex;
while (itemIndex < blockSize) while (itemIndex < blockSize)
{ {
DoNothing(memoryBlock[itemIndex++]); DoNothing(memoryBlock[itemIndex++]);
} }
} }
} }
} }
} }
@@ -1,77 +1,77 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_IndexOf_BestAndWorstCases() public void Array_IndexOf_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(Array.IndexOf(list, data[i])); DoNothing(Array.IndexOf(list, data[i]));
DoNothing(Array.IndexOf(list, data[^(i + 1)])); DoNothing(Array.IndexOf(list, data[^(i + 1)]));
} }
} }
public void List_IndexOf_BestAndWorstCases() public void List_IndexOf_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[i])); DoNothing(list.IndexOf(data[i]));
DoNothing(list.IndexOf(data[^(i + 1)])); DoNothing(list.IndexOf(data[^(i + 1)]));
} }
} }
public void PooledList_IndexOf_BestAndWorstCases() public void PooledList_IndexOf_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsPooledList; var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[i])); DoNothing(list.IndexOf(data[i]));
DoNothing(list.IndexOf(data[^(i + 1)])); DoNothing(list.IndexOf(data[^(i + 1)]));
} }
} }
public void RecyclableList_IndexOf_BestAndWorstCases() public void RecyclableList_IndexOf_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[i])); DoNothing(list.IndexOf(data[i]));
DoNothing(list.IndexOf(data[^(i + 1)])); DoNothing(list.IndexOf(data[^(i + 1)]));
} }
} }
public void RecyclableLongList_IndexOf_BestAndWorstCases() public void RecyclableLongList_IndexOf_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableLongList; var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[i])); DoNothing(list.IndexOf(data[i]));
DoNothing(list.IndexOf(data[^(i + 1)])); DoNothing(list.IndexOf(data[^(i + 1)]));
} }
} }
public void RecyclableLongList_LongIndexOf_BestAndWorstCases() public void RecyclableLongList_LongIndexOf_BestAndWorstCases()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableLongList; var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[i])); DoNothing(list.IndexOf(data[i]));
DoNothing(list.LongIndexOf(data[^(i + 1)])); DoNothing(list.LongIndexOf(data[^(i + 1)]));
} }
} }
} }
} }
@@ -1,71 +1,71 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_IndexOf_FirstItems() public void Array_IndexOf_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(Array.IndexOf(list, data[i])); DoNothing(Array.IndexOf(list, data[i]));
} }
} }
public void List_IndexOf_FirstItems() public void List_IndexOf_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[i])); DoNothing(list.IndexOf(data[i]));
} }
} }
public void PooledList_IndexOf_FirstItems() public void PooledList_IndexOf_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsPooledList; var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[i])); DoNothing(list.IndexOf(data[i]));
} }
} }
public void RecyclableList_IndexOf_FirstItems() public void RecyclableList_IndexOf_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[i])); DoNothing(list.IndexOf(data[i]));
} }
} }
public void RecyclableLongList_IndexOf_FirstItems() public void RecyclableLongList_IndexOf_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableLongList; var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[i])); DoNothing(list.IndexOf(data[i]));
} }
} }
public void RecyclableLongList_LongIndexOf_FirstItems() public void RecyclableLongList_LongIndexOf_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableLongList; var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.LongIndexOf(data[i])); DoNothing(list.LongIndexOf(data[i]));
} }
} }
} }
} }
@@ -1,71 +1,71 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_IndexOf_LastItems() public void Array_IndexOf_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjects; var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(Array.IndexOf(list, data[^(i + 1)])); DoNothing(Array.IndexOf(list, data[^(i + 1)]));
} }
} }
public void List_IndexOf_LastItems() public void List_IndexOf_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsList; var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[^(i + 1)])); DoNothing(list.IndexOf(data[^(i + 1)]));
} }
} }
public void PooledList_IndexOf_LastItems() public void PooledList_IndexOf_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsPooledList; var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[^(i + 1)])); DoNothing(list.IndexOf(data[^(i + 1)]));
} }
} }
public void RecyclableList_IndexOf_LastItems() public void RecyclableList_IndexOf_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableList; var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[^(i + 1)])); DoNothing(list.IndexOf(data[^(i + 1)]));
} }
} }
public void RecyclableLongList_IndexOf_LastItems() public void RecyclableLongList_IndexOf_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableLongList; var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.IndexOf(data[^(i + 1)])); DoNothing(list.IndexOf(data[^(i + 1)]));
} }
} }
public void RecyclableLongList_LongIndexOf_LastItems() public void RecyclableLongList_LongIndexOf_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = TestObjectsAsRecyclableLongList; var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods; var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.LongIndexOf(data[^(i + 1)])); DoNothing(list.LongIndexOf(data[^(i + 1)]));
} }
} }
} }
} }
@@ -1,17 +1,17 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_LongCount() public void Array_LongCount()
{ {
var data = TestObjects; var data = TestObjects;
DoNothing(data.LongLength); DoNothing(data.LongLength);
} }
public void RecyclableLongList_LongCount() public void RecyclableLongList_LongCount()
{ {
var data = TestObjectsAsRecyclableLongList; var data = TestObjectsAsRecyclableLongList;
DoNothing(data.LongCount); DoNothing(data.LongCount);
} }
} }
} }
@@ -1,102 +1,102 @@
using System.Reflection; using System.Reflection;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public enum RecyclableCollectionsBenchmarkType public enum RecyclableCollectionsBenchmarkType
{ {
Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable, Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, BinarySearch_BestAndWorstCases, AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, BinarySearch_BestAndWorstCases,
Contains_FirstItems, Contains_LastItems, ConvertAll, Count, Create_WithCapacity, Create, Exists_BestAndWorstCases, Contains_FirstItems, Contains_LastItems, ConvertAll, Count, Create_WithCapacity, Create, Exists_BestAndWorstCases,
Find_BestAndWorstCases, FindAll_BestAndWorstCases, FindLast_BestAndWorstCases, FindLastIndex_BestAndWorstCases, Find_BestAndWorstCases, FindAll_BestAndWorstCases, FindLast_BestAndWorstCases, FindLastIndex_BestAndWorstCases,
ForEach, GetItem, IndexOf_BestAndWorstCases,IndexOf_FirstItems, IndexOf_LastItems, LongCount, ForEach, GetItem, IndexOf_BestAndWorstCases,IndexOf_FirstItems, IndexOf_LastItems, LongCount,
Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems, Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems,
SetItem, VersionedForEach SetItem, VersionedForEach
} }
public partial class RecyclableCollectionsBenchmarks : RecyclableBenchmarkBase<RecyclableCollectionsBenchmarkSource> public partial class RecyclableCollectionsBenchmarks : RecyclableBenchmarkBase<RecyclableCollectionsBenchmarkSource>
{ {
public RecyclableCollectionsBenchmarks() public RecyclableCollectionsBenchmarks()
{ {
TestObjectCount = 1; TestObjectCount = 1;
//BaseDataType = RecyclableCollectionsBenchmarkSource.List; //BaseDataType = RecyclableCollectionsBenchmarkSource.List;
DataType = RecyclableCollectionsBenchmarkSource.RecyclableList; DataType = RecyclableCollectionsBenchmarkSource.RecyclableList;
} }
[Params [Params
( (
RecyclableCollectionsBenchmarkType.Add_WithCapacity, RecyclableCollectionsBenchmarkType.Add_WithCapacity,
RecyclableCollectionsBenchmarkType.Add, RecyclableCollectionsBenchmarkType.Add,
RecyclableCollectionsBenchmarkType.AddRange_WithCapacity, RecyclableCollectionsBenchmarkType.AddRange_WithCapacity,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsArray, RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsArray,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIEnumerable, RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIEnumerable,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList, RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList, RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType, RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
RecyclableCollectionsBenchmarkType.BinarySearch_BestAndWorstCases, RecyclableCollectionsBenchmarkType.BinarySearch_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.Contains_FirstItems, RecyclableCollectionsBenchmarkType.Contains_FirstItems,
RecyclableCollectionsBenchmarkType.Contains_LastItems, RecyclableCollectionsBenchmarkType.Contains_LastItems,
RecyclableCollectionsBenchmarkType.ConvertAll, RecyclableCollectionsBenchmarkType.ConvertAll,
RecyclableCollectionsBenchmarkType.Count, RecyclableCollectionsBenchmarkType.Count,
RecyclableCollectionsBenchmarkType.Create_WithCapacity, RecyclableCollectionsBenchmarkType.Create_WithCapacity,
RecyclableCollectionsBenchmarkType.Create, RecyclableCollectionsBenchmarkType.Create,
RecyclableCollectionsBenchmarkType.Exists_BestAndWorstCases, RecyclableCollectionsBenchmarkType.Exists_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.Find_BestAndWorstCases, RecyclableCollectionsBenchmarkType.Find_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.FindAll_BestAndWorstCases, RecyclableCollectionsBenchmarkType.FindAll_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.FindLast_BestAndWorstCases, RecyclableCollectionsBenchmarkType.FindLast_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.FindLastIndex_BestAndWorstCases, RecyclableCollectionsBenchmarkType.FindLastIndex_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.ForEach, RecyclableCollectionsBenchmarkType.ForEach,
RecyclableCollectionsBenchmarkType.GetItem, RecyclableCollectionsBenchmarkType.GetItem,
RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases, RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.IndexOf_FirstItems, RecyclableCollectionsBenchmarkType.IndexOf_FirstItems,
RecyclableCollectionsBenchmarkType.IndexOf_LastItems, RecyclableCollectionsBenchmarkType.IndexOf_LastItems,
RecyclableCollectionsBenchmarkType.LongCount, RecyclableCollectionsBenchmarkType.LongCount,
RecyclableCollectionsBenchmarkType.Remove_FirstItems, RecyclableCollectionsBenchmarkType.Remove_FirstItems,
RecyclableCollectionsBenchmarkType.Remove_LastItems, RecyclableCollectionsBenchmarkType.Remove_LastItems,
RecyclableCollectionsBenchmarkType.RemoveAt_FirstItems, RecyclableCollectionsBenchmarkType.RemoveAt_FirstItems,
RecyclableCollectionsBenchmarkType.RemoveAt_LastItems, RecyclableCollectionsBenchmarkType.RemoveAt_LastItems,
RecyclableCollectionsBenchmarkType.SetItem, RecyclableCollectionsBenchmarkType.SetItem,
RecyclableCollectionsBenchmarkType.VersionedForEach RecyclableCollectionsBenchmarkType.VersionedForEach
)] )]
public RecyclableCollectionsBenchmarkType TestCase { get; set; } = RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases; public RecyclableCollectionsBenchmarkType TestCase { get; set; } = RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases;
[Params [Params
( (
RecyclableCollectionsBenchmarkSource.List RecyclableCollectionsBenchmarkSource.List
)] )]
public override RecyclableCollectionsBenchmarkSource BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; } public override RecyclableCollectionsBenchmarkSource BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
[Params [Params
( (
RecyclableCollectionsBenchmarkSource.Array, RecyclableCollectionsBenchmarkSource.Array,
RecyclableCollectionsBenchmarkSource.List, RecyclableCollectionsBenchmarkSource.List,
RecyclableCollectionsBenchmarkSource.PooledList, RecyclableCollectionsBenchmarkSource.PooledList,
RecyclableCollectionsBenchmarkSource.RecyclableList, RecyclableCollectionsBenchmarkSource.RecyclableList,
RecyclableCollectionsBenchmarkSource.RecyclableLongList RecyclableCollectionsBenchmarkSource.RecyclableLongList
)] )]
public override RecyclableCollectionsBenchmarkSource DataType { get => base.DataType; set => base.DataType = value; } public override RecyclableCollectionsBenchmarkSource DataType { get => base.DataType; set => base.DataType = value; }
protected override Action? GetTestMethod(RecyclableCollectionsBenchmarkSource benchmarkType) protected override Action? GetTestMethod(RecyclableCollectionsBenchmarkSource benchmarkType)
=> TestCase switch => TestCase switch
{ {
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType => (Action?)GetType().GetMethod($"{benchmarkType}_AddRangeWhenSourceIs{benchmarkType}", BindingFlags.Instance | BindingFlags.Public)?.CreateDelegate(typeof(Action), this), RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType => (Action?)GetType().GetMethod($"{benchmarkType}_AddRangeWhenSourceIs{benchmarkType}", BindingFlags.Instance | BindingFlags.Public)?.CreateDelegate(typeof(Action), this),
_ => (Action?) GetType().GetMethod($"{benchmarkType}_{TestCase}", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)?.CreateDelegate(typeof(Action), this) ?? throw new InvalidOperationException($"Method {benchmarkType}_{TestCase} not found in class {GetType().FullName}") _ => (Action?) GetType().GetMethod($"{benchmarkType}_{TestCase}", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)?.CreateDelegate(typeof(Action), this) ?? throw new InvalidOperationException($"Method {benchmarkType}_{TestCase} not found in class {GetType().FullName}")
}; };
public override void Setup() public override void Setup()
{ {
base.Setup(); base.Setup();
switch(TestCase) switch(TestCase)
{ {
case RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList: case RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList:
PrepareData(RecyclableCollectionsBenchmarkSource.RecyclableLongList); PrepareData(RecyclableCollectionsBenchmarkSource.RecyclableLongList);
break; break;
case RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType: case RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType:
PrepareData(RecyclableCollectionsBenchmarkSource.RecyclableList); PrepareData(RecyclableCollectionsBenchmarkSource.RecyclableList);
break; break;
} }
} }
} }
} }
@@ -1,51 +1,51 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void List_RemoveAt_FirstItems() public void List_RemoveAt_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = new List<long>(data); var list = new List<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.RemoveAt(0); list.RemoveAt(0);
} }
} }
public void PooledList_RemoveAt_FirstItems() public void PooledList_RemoveAt_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new PooledList<long>(data, ClearMode.Auto); using var list = new PooledList<long>(data, ClearMode.Auto);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.RemoveAt(0); list.RemoveAt(0);
} }
} }
public void RecyclableList_RemoveAt_FirstItems() public void RecyclableList_RemoveAt_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableList<long>(data); using var list = new RecyclableList<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.RemoveAt(0); list.RemoveAt(0);
} }
} }
public void RecyclableLongList_RemoveAt_FirstItems() public void RecyclableLongList_RemoveAt_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount); using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
list.RemoveAt(0); list.RemoveAt(0);
} }
} }
} }
} }
@@ -1,51 +1,51 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void List_RemoveAt_LastItems() public void List_RemoveAt_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = new List<long>(data); var list = new List<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--) for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{ {
list.RemoveAt(i); list.RemoveAt(i);
} }
} }
public void PooledList_RemoveAt_LastItems() public void PooledList_RemoveAt_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new PooledList<long>(data, ClearMode.Auto); using var list = new PooledList<long>(data, ClearMode.Auto);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--) for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{ {
list.RemoveAt(i); list.RemoveAt(i);
} }
} }
public void RecyclableList_RemoveAt_LastItems() public void RecyclableList_RemoveAt_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableList<long>(data); using var list = new RecyclableList<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--) for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{ {
list.RemoveAt(i); list.RemoveAt(i);
} }
} }
public void RecyclableLongList_RemoveAt_LastItems() public void RecyclableLongList_RemoveAt_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount); using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--) for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{ {
list.RemoveAt(i); list.RemoveAt(i);
} }
} }
} }
} }
@@ -1,51 +1,51 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void List_Remove_FirstItems() public void List_Remove_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = new List<long>(data); var list = new List<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Remove(data[i])); DoNothing(list.Remove(data[i]));
} }
} }
public void PooledList_Remove_FirstItems() public void PooledList_Remove_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new PooledList<long>(data, ClearMode.Auto); using var list = new PooledList<long>(data, ClearMode.Auto);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Remove(data[i])); DoNothing(list.Remove(data[i]));
} }
} }
public void RecyclableList_Remove_FirstItems() public void RecyclableList_Remove_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableList<long>(data); using var list = new RecyclableList<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Remove(data[i])); DoNothing(list.Remove(data[i]));
} }
} }
public void RecyclableLongList_Remove_FirstItems() public void RecyclableLongList_Remove_FirstItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount); using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
DoNothing(list.Remove(data[i])); DoNothing(list.Remove(data[i]));
} }
} }
} }
} }
@@ -1,51 +1,51 @@
using Collections.Pooled; using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void List_Remove_LastItems() public void List_Remove_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
var list = new List<long>(data); var list = new List<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--) for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{ {
DoNothing(list.Remove(data[i])); DoNothing(list.Remove(data[i]));
} }
} }
public void PooledList_Remove_LastItems() public void PooledList_Remove_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new PooledList<long>(data, ClearMode.Auto); using var list = new PooledList<long>(data, ClearMode.Auto);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--) for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{ {
DoNothing(list.Remove(data[i])); DoNothing(list.Remove(data[i]));
} }
} }
public void RecyclableList_Remove_LastItems() public void RecyclableList_Remove_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableList<long>(data); using var list = new RecyclableList<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--) for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{ {
DoNothing(list.Remove(data[i])); DoNothing(list.Remove(data[i]));
} }
} }
public void RecyclableLongList_Remove_LastItems() public void RecyclableLongList_Remove_LastItems()
{ {
var data = TestObjects; var data = TestObjects;
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity:base.TestObjectCount); using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity:base.TestObjectCount);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount; var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--) for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{ {
DoNothing(list.Remove(data[i])); DoNothing(list.Remove(data[i]));
} }
} }
} }
} }
@@ -1,56 +1,56 @@
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public partial class RecyclableCollectionsBenchmarks public partial class RecyclableCollectionsBenchmarks
{ {
public void Array_SetItem() public void Array_SetItem()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
long index = 0; long index = 0;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
data[index] = TestObjects[index++]; data[index] = TestObjects[index++];
} }
} }
public void List_SetItem() public void List_SetItem()
{ {
var data = TestObjectsAsList; var data = TestObjectsAsList;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
data[i] = TestObjects[i]; data[i] = TestObjects[i];
} }
} }
public void PooledList_SetItem() public void PooledList_SetItem()
{ {
var data = TestObjectsAsPooledList; var data = TestObjectsAsPooledList;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
data[i] = TestObjects[i]; data[i] = TestObjects[i];
} }
} }
public void RecyclableList_SetItem() public void RecyclableList_SetItem()
{ {
var data = TestObjectsAsRecyclableList; var data = TestObjectsAsRecyclableList;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
data[i] = TestObjects[i]; data[i] = TestObjects[i];
} }
} }
public void RecyclableLongList_SetItem() public void RecyclableLongList_SetItem()
{ {
var data = TestObjectsAsRecyclableLongList; var data = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++) for (var i = 0; i < dataCount; i++)
{ {
data[i] = TestObjects[i]; data[i] = TestObjects[i];
} }
} }
} }
} }
@@ -1,19 +1,19 @@
namespace Recyclable.Collections.Benchmarks.Core namespace Recyclable.Collections.Benchmarks.Core
{ {
public static class DataGenerator public static class DataGenerator
{ {
public static long[] EnumerateTestObjects(long testObjectCount) public static long[] EnumerateTestObjects(long testObjectCount)
{ {
var result = new long[testObjectCount]; var result = new long[testObjectCount];
Console.WriteLine("******* TEST DATA ARRAY CREATED *******"); Console.WriteLine("******* TEST DATA ARRAY CREATED *******");
Span<long> resultSpan = result; Span<long> resultSpan = result;
for (int i = 0; i < testObjectCount; i++) for (int i = 0; i < testObjectCount; i++)
{ {
resultSpan[i] = i; resultSpan[i] = i;
} }
return result; return result;
} }
} }
} }
@@ -1,70 +1,70 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using System.Buffers; using System.Buffers;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public enum ArraySizeLimitPocBenchmarkType public enum ArraySizeLimitPocBenchmarkType
{ {
ArraySizeLimit ArraySizeLimit
} }
public class ArraySizeLimitPocBenchmarks : PocBenchmarkBase<ArraySizeLimitPocBenchmarkType> public class ArraySizeLimitPocBenchmarks : PocBenchmarkBase<ArraySizeLimitPocBenchmarkType>
{ {
public static void Run() public static void Run()
{ {
var benchmark = new ArraySizeLimitPocBenchmarks(); var benchmark = new ArraySizeLimitPocBenchmarks();
benchmark.Setup(); benchmark.Setup();
FindArraySizeLimit(); FindArraySizeLimit();
benchmark.Cleanup(); benchmark.Cleanup();
} }
[Params(2_147_483_605)] [Params(2_147_483_605)]
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; } public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
[Benchmark(OperationsPerInvoke = 1)] [Benchmark(OperationsPerInvoke = 1)]
public void Max_Allowed_Size_2_147_483_605() public void Max_Allowed_Size_2_147_483_605()
{ {
long[] arr = ArrayPool<long>.Shared.Rent(TestObjectCount); long[] arr = ArrayPool<long>.Shared.Rent(TestObjectCount);
DoNothing(arr); DoNothing(arr);
} }
[Benchmark(OperationsPerInvoke = 1)] [Benchmark(OperationsPerInvoke = 1)]
public static void FindArraySizeLimit() public static void FindArraySizeLimit()
{ {
var pool = ArrayPool<long>.Shared; var pool = ArrayPool<long>.Shared;
var bufferSize = int.MaxValue / 2; var bufferSize = int.MaxValue / 2;
var oldValue = 0; var oldValue = 0;
while (true) while (true)
{ {
try try
{ {
Console.Write($"Allocating {bufferSize}..."); Console.Write($"Allocating {bufferSize}...");
var arr = pool.Rent(bufferSize); var arr = pool.Rent(bufferSize);
Console.WriteLine("success"); Console.WriteLine("success");
pool.Return(arr); pool.Return(arr);
arr = null; arr = null;
var nextValue = bufferSize - oldValue; var nextValue = bufferSize - oldValue;
oldValue = bufferSize; oldValue = bufferSize;
bufferSize = checked(bufferSize + nextValue); bufferSize = checked(bufferSize + nextValue);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
} }
catch (Exception) catch (Exception)
{ {
Console.WriteLine("error"); Console.WriteLine("error");
var nextValue = (bufferSize - oldValue) >> 1; var nextValue = (bufferSize - oldValue) >> 1;
if (nextValue == 0) if (nextValue == 0)
{ {
break; break;
} }
bufferSize = oldValue + nextValue; bufferSize = oldValue + nextValue;
} }
} }
Console.WriteLine("All done"); Console.WriteLine("All done");
throw new Exception($"Everything was successful. You can create {oldValue} byte big arrays. Exception is logged to prevent more benchmarks."); throw new Exception($"Everything was successful. You can create {oldValue} byte big arrays. Exception is logged to prevent more benchmarks.");
} }
} }
} }
@@ -1,63 +1,63 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public enum BoolOrComparePocBenchmarkType public enum BoolOrComparePocBenchmarkType
{ {
Bool, Bool,
Compare, Compare,
} }
[MemoryDiagnoser] [MemoryDiagnoser]
public class BoolOrComparePocBenchmarks : PocBenchmarkBase<BoolOrComparePocBenchmarkType> public class BoolOrComparePocBenchmarks : PocBenchmarkBase<BoolOrComparePocBenchmarkType>
{ {
[Params(BoolOrComparePocBenchmarkType.Bool, BoolOrComparePocBenchmarkType.Compare)] [Params(BoolOrComparePocBenchmarkType.Bool, BoolOrComparePocBenchmarkType.Compare)]
public override BoolOrComparePocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; } public override BoolOrComparePocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
[Params(8)] [Params(8)]
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; } public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
[Params(true)] [Params(true)]
public bool TestBool = true; public bool TestBool = true;
public static void Run() public static void Run()
{ {
var benchmark = new BoolOrComparePocBenchmarks(); var benchmark = new BoolOrComparePocBenchmarks();
benchmark.Setup(); benchmark.Setup();
benchmark.Bool(); benchmark.Bool();
benchmark.Compare(); benchmark.Compare();
benchmark.Cleanup(); benchmark.Cleanup();
} }
public void Bool() public void Bool()
{ {
if (TestBool) if (TestBool)
{ {
DoNothing(TestBool); DoNothing(TestBool);
} }
else else
{ {
DoNothing(TestBool); DoNothing(TestBool);
} }
} }
public void Compare() public void Compare()
{ {
if (TestObjectCount >= RecyclableDefaults.MinPooledArrayLength) if (TestObjectCount >= RecyclableDefaults.MinPooledArrayLength)
{ {
DoNothing(TestBool); DoNothing(TestBool);
} }
else else
{ {
DoNothing(TestBool); DoNothing(TestBool);
} }
} }
protected override Action? GetTestMethod(BoolOrComparePocBenchmarkType benchmarkType) => benchmarkType switch protected override Action? GetTestMethod(BoolOrComparePocBenchmarkType benchmarkType) => benchmarkType switch
{ {
BoolOrComparePocBenchmarkType.Bool => Bool, BoolOrComparePocBenchmarkType.Bool => Bool,
BoolOrComparePocBenchmarkType.Compare => Compare, BoolOrComparePocBenchmarkType.Compare => Compare,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType), _ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
}; };
} }
} }
@@ -1,346 +1,346 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public enum DelegateVsComparerPocBenchmarkType public enum DelegateVsComparerPocBenchmarkType
{ {
Comparer, Comparer,
Delegate, Delegate,
Func, Func,
ListOfBoxedStructs, ListOfBoxedStructs,
ListOfClasses, ListOfClasses,
ListOfComparers, ListOfComparers,
ListOfDelegates, ListOfDelegates,
ListOfFuncs, ListOfFuncs,
ListOfLocalFuncs, ListOfLocalFuncs,
ListOfStructs, ListOfStructs,
ListOfUnboxedReadOnlyStructs, ListOfUnboxedReadOnlyStructs,
ListOfUnboxedStructs, ListOfUnboxedStructs,
StaticFunc, StaticFunc,
} }
[MemoryDiagnoser] [MemoryDiagnoser]
public class DelegateVsComparerPocBenchmarks : PocBenchmarkBase<DelegateVsComparerPocBenchmarkType> public class DelegateVsComparerPocBenchmarks : PocBenchmarkBase<DelegateVsComparerPocBenchmarkType>
{ {
[Params [Params
( (
DelegateVsComparerPocBenchmarkType.Delegate, DelegateVsComparerPocBenchmarkType.Delegate,
DelegateVsComparerPocBenchmarkType.Func, DelegateVsComparerPocBenchmarkType.Func,
DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs, DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs,
DelegateVsComparerPocBenchmarkType.ListOfClasses, DelegateVsComparerPocBenchmarkType.ListOfClasses,
DelegateVsComparerPocBenchmarkType.ListOfComparers, DelegateVsComparerPocBenchmarkType.ListOfComparers,
DelegateVsComparerPocBenchmarkType.ListOfDelegates, DelegateVsComparerPocBenchmarkType.ListOfDelegates,
DelegateVsComparerPocBenchmarkType.ListOfFuncs, DelegateVsComparerPocBenchmarkType.ListOfFuncs,
DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs, DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs,
DelegateVsComparerPocBenchmarkType.ListOfStructs, DelegateVsComparerPocBenchmarkType.ListOfStructs,
DelegateVsComparerPocBenchmarkType.ListOfUnboxedReadOnlyStructs, DelegateVsComparerPocBenchmarkType.ListOfUnboxedReadOnlyStructs,
DelegateVsComparerPocBenchmarkType.ListOfUnboxedStructs, DelegateVsComparerPocBenchmarkType.ListOfUnboxedStructs,
DelegateVsComparerPocBenchmarkType.StaticFunc DelegateVsComparerPocBenchmarkType.StaticFunc
)] )]
public override DelegateVsComparerPocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; } public override DelegateVsComparerPocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
[Params(DelegateVsComparerPocBenchmarkType.Comparer)] [Params(DelegateVsComparerPocBenchmarkType.Comparer)]
public override DelegateVsComparerPocBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; } public override DelegateVsComparerPocBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
#region Dummy classes #region Dummy classes
private delegate T Compare<T>(T x, T y); private delegate T Compare<T>(T x, T y);
public readonly struct MyComparerReadOnlyStruct<T> : IComparer<T> public readonly struct MyComparerReadOnlyStruct<T> : IComparer<T>
{ {
public int Compare(T? x, T? y) => Comparer<T>.Default.Compare(x, y); public int Compare(T? x, T? y) => Comparer<T>.Default.Compare(x, y);
} }
private struct MyComparerStruct<T> : IComparer<T> private struct MyComparerStruct<T> : IComparer<T>
{ {
public readonly int Compare(T? x, T? y) => Comparer<T>.Default.Compare(x, y); public readonly int Compare(T? x, T? y) => Comparer<T>.Default.Compare(x, y);
} }
private class MyComparer : IComparer<long> private class MyComparer : IComparer<long>
{ {
public int Compare(long x, long y) => (x - y) switch public int Compare(long x, long y) => (x - y) switch
{ {
> 0 => 1, > 0 => 1,
< 0 => -1, < 0 => -1,
_ => 0, _ => 0,
}; };
} }
#endregion #endregion
public static void Run() public static void Run()
{ {
var benchmark = new DelegateVsComparerPocBenchmarks(); var benchmark = new DelegateVsComparerPocBenchmarks();
benchmark.Setup(); benchmark.Setup();
benchmark.Comparer(); benchmark.Comparer();
benchmark.Delegate(); benchmark.Delegate();
benchmark.Func(); benchmark.Func();
benchmark.ListOfBoxedStructs(); benchmark.ListOfBoxedStructs();
benchmark.ListOfClasses(); benchmark.ListOfClasses();
benchmark.ListOfComparers(); benchmark.ListOfComparers();
benchmark.ListOfDelegates(); benchmark.ListOfDelegates();
benchmark.ListOfFuncs(); benchmark.ListOfFuncs();
benchmark.ListOfLocalFuncs(); benchmark.ListOfLocalFuncs();
benchmark.ListOfStructs(); benchmark.ListOfStructs();
benchmark.ListOfUnboxedReadOnlyStructs(); benchmark.ListOfUnboxedReadOnlyStructs();
benchmark.ListOfUnboxedStructs(); benchmark.ListOfUnboxedStructs();
benchmark.StaticFunc(); benchmark.StaticFunc();
benchmark.Cleanup(); benchmark.Cleanup();
} }
private static readonly Comparer<long> _comparer = Comparer<long>.Default; private static readonly Comparer<long> _comparer = Comparer<long>.Default;
private IEnumerable<long>? _testObjects; private IEnumerable<long>? _testObjects;
private IEnumerable<Func<long, long, int>>? _testListOfFunctions; private IEnumerable<Func<long, long, int>>? _testListOfFunctions;
private IEnumerable<IComparer<long>>? _testListOfComparers; private IEnumerable<IComparer<long>>? _testListOfComparers;
private IEnumerable<Compare<long>>? _testListOfDelegates; private IEnumerable<Compare<long>>? _testListOfDelegates;
private IEnumerable<Func<long, long, int>>? _testListOfLocalFunctions; private IEnumerable<Func<long, long, int>>? _testListOfLocalFunctions;
private IEnumerable<MyComparer>? _testListOfClasses; private IEnumerable<MyComparer>? _testListOfClasses;
private IEnumerable<MyComparerStruct<long>>? _testListOfStructs; private IEnumerable<MyComparerStruct<long>>? _testListOfStructs;
private IEnumerable<IComparer<long>>? _testListOfBoxedStructs; private IEnumerable<IComparer<long>>? _testListOfBoxedStructs;
private IEnumerable<TComparer> TestListOfUnboxedStructs<TComparer, TValue>() where TComparer : IComparer<TValue>, new() private IEnumerable<TComparer> TestListOfUnboxedStructs<TComparer, TValue>() where TComparer : IComparer<TValue>, new()
{ {
foreach (var _ in _testObjects!) foreach (var _ in _testObjects!)
{ {
yield return new TComparer(); yield return new TComparer();
} }
} }
private static IEnumerable<Func<long, long, int>> GetLocalFunctions(IEnumerable<long> testObjects) private static IEnumerable<Func<long, long, int>> GetLocalFunctions(IEnumerable<long> testObjects)
{ {
static int compareFunc(long x, long y) static int compareFunc(long x, long y)
{ {
return (x - y) switch return (x - y) switch
{ {
> 0 => 1, > 0 => 1,
< 0 => -1, < 0 => -1,
_ => 0 _ => 0
}; };
} }
foreach (var _ in testObjects) foreach (var _ in testObjects)
{ {
yield return compareFunc; yield return compareFunc;
} }
} }
private void TestWithComparer(IComparer<long> comparer) private void TestWithComparer(IComparer<long> comparer)
{ {
var testData = _testObjects!.ToArray(); var testData = _testObjects!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{ {
_ = comparer.Compare(testData[itemIdx], testData[itemIdx + 1]); _ = comparer.Compare(testData[itemIdx], testData[itemIdx + 1]);
} }
} }
private void TestWithDelegate(Compare<long> compareFunc) private void TestWithDelegate(Compare<long> compareFunc)
{ {
var testData = _testObjects!.ToArray(); var testData = _testObjects!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{ {
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]); _ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
} }
} }
private void TestWithFunc(Func<long, long, int> compareFunc) private void TestWithFunc(Func<long, long, int> compareFunc)
{ {
var testData = _testObjects!.ToArray(); var testData = _testObjects!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{ {
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]); _ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
} }
} }
public void Comparer() public void Comparer()
{ {
TestWithComparer(_comparer); TestWithComparer(_comparer);
} }
public void Func() public void Func()
{ {
TestWithFunc((x, y) => (x - y) switch TestWithFunc((x, y) => (x - y) switch
{ {
> 0 => 1, > 0 => 1,
< 0 => -1, < 0 => -1,
_ => 0 _ => 0
}); });
} }
public void StaticFunc() public void StaticFunc()
{ {
TestWithFunc(static (x, y) => (x - y) switch TestWithFunc(static (x, y) => (x - y) switch
{ {
> 0 => 1, > 0 => 1,
< 0 => -1, < 0 => -1,
_ => 0 _ => 0
}); });
} }
public void Delegate() public void Delegate()
{ {
TestWithDelegate((x, y) => (x - y) switch TestWithDelegate((x, y) => (x - y) switch
{ {
> 0 => 1, > 0 => 1,
< 0 => -1, < 0 => -1,
_ => 0 _ => 0
}); });
} }
public void ListOfFuncs() public void ListOfFuncs()
{ {
const long x = 2, y = 5; const long x = 2, y = 5;
var testData = _testListOfFunctions!.ToArray(); var testData = _testListOfFunctions!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{ {
var compareFunc = testData[itemIdx]; var compareFunc = testData[itemIdx];
_ = compareFunc(x, y); _ = compareFunc(x, y);
} }
} }
public void ListOfLocalFuncs() public void ListOfLocalFuncs()
{ {
const long x = 2, y = 5; const long x = 2, y = 5;
var testData = _testListOfLocalFunctions!.ToArray(); var testData = _testListOfLocalFunctions!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{ {
var compareFunc = testData[itemIdx]; var compareFunc = testData[itemIdx];
_ = compareFunc(x, y); _ = compareFunc(x, y);
} }
} }
public void ListOfComparers() public void ListOfComparers()
{ {
const long x = 2, y = 5; const long x = 2, y = 5;
var testData = _testListOfComparers!.ToArray(); var testData = _testListOfComparers!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{ {
var comparer = testData[itemIdx]; var comparer = testData[itemIdx];
_ = comparer.Compare(x, y); _ = comparer.Compare(x, y);
} }
} }
public void ListOfClasses() public void ListOfClasses()
{ {
const long x = 2, y = 5; const long x = 2, y = 5;
var testData = _testListOfClasses!.ToArray(); var testData = _testListOfClasses!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{ {
var comparer = testData[itemIdx]; var comparer = testData[itemIdx];
_ = comparer.Compare(x, y); _ = comparer.Compare(x, y);
} }
} }
public void ListOfStructs() public void ListOfStructs()
{ {
const long x = 2, y = 5; const long x = 2, y = 5;
var testData = _testListOfStructs!.ToArray(); var testData = _testListOfStructs!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{ {
var comparer = testData[itemIdx]; var comparer = testData[itemIdx];
_ = comparer.Compare(x, y); _ = comparer.Compare(x, y);
} }
} }
public void ListOfBoxedStructs() public void ListOfBoxedStructs()
{ {
const long x = 2, y = 5; const long x = 2, y = 5;
var testData = _testListOfBoxedStructs!.ToArray(); var testData = _testListOfBoxedStructs!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{ {
var comparer = testData[itemIdx]; var comparer = testData[itemIdx];
_ = comparer.Compare(x, y); _ = comparer.Compare(x, y);
} }
} }
public void ListOfUnboxedStructs() public void ListOfUnboxedStructs()
{ {
const long x = 2, y = 5; const long x = 2, y = 5;
var testData = TestListOfUnboxedStructs<MyComparerStruct<long>, long>().ToArray(); var testData = TestListOfUnboxedStructs<MyComparerStruct<long>, long>().ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{ {
var comparer = testData[itemIdx]; var comparer = testData[itemIdx];
_ = comparer.Compare(x, y); _ = comparer.Compare(x, y);
} }
} }
public void ListOfUnboxedReadOnlyStructs() public void ListOfUnboxedReadOnlyStructs()
{ {
const long x = 2, y = 5; const long x = 2, y = 5;
var testData = TestListOfUnboxedStructs<MyComparerReadOnlyStruct<long>, long>().ToArray(); var testData = TestListOfUnboxedStructs<MyComparerReadOnlyStruct<long>, long>().ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{ {
var comparer = testData[itemIdx]; var comparer = testData[itemIdx];
_ = comparer.Compare(x, y); _ = comparer.Compare(x, y);
} }
} }
public void ListOfDelegates() public void ListOfDelegates()
{ {
const long x = 2, y = 5; const long x = 2, y = 5;
var testData = _testListOfDelegates!.ToArray(); var testData = _testListOfDelegates!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++) for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{ {
var compareDelegate = testData[itemIdx]; var compareDelegate = testData[itemIdx];
_ = compareDelegate(x, y); _ = compareDelegate(x, y);
} }
} }
protected override Action? GetTestMethod(DelegateVsComparerPocBenchmarkType benchmarkType) => benchmarkType switch protected override Action? GetTestMethod(DelegateVsComparerPocBenchmarkType benchmarkType) => benchmarkType switch
{ {
DelegateVsComparerPocBenchmarkType.Comparer => Comparer, DelegateVsComparerPocBenchmarkType.Comparer => Comparer,
DelegateVsComparerPocBenchmarkType.Delegate => Delegate, DelegateVsComparerPocBenchmarkType.Delegate => Delegate,
DelegateVsComparerPocBenchmarkType.Func => Func, DelegateVsComparerPocBenchmarkType.Func => Func,
DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs => ListOfBoxedStructs, DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs => ListOfBoxedStructs,
DelegateVsComparerPocBenchmarkType.ListOfClasses => ListOfClasses, DelegateVsComparerPocBenchmarkType.ListOfClasses => ListOfClasses,
DelegateVsComparerPocBenchmarkType.ListOfComparers => ListOfComparers, DelegateVsComparerPocBenchmarkType.ListOfComparers => ListOfComparers,
DelegateVsComparerPocBenchmarkType.ListOfDelegates => ListOfDelegates, DelegateVsComparerPocBenchmarkType.ListOfDelegates => ListOfDelegates,
DelegateVsComparerPocBenchmarkType.ListOfFuncs => ListOfFuncs, DelegateVsComparerPocBenchmarkType.ListOfFuncs => ListOfFuncs,
DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs => ListOfLocalFuncs, DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs => ListOfLocalFuncs,
DelegateVsComparerPocBenchmarkType.ListOfStructs => ListOfStructs, DelegateVsComparerPocBenchmarkType.ListOfStructs => ListOfStructs,
DelegateVsComparerPocBenchmarkType.ListOfUnboxedReadOnlyStructs => ListOfUnboxedReadOnlyStructs, DelegateVsComparerPocBenchmarkType.ListOfUnboxedReadOnlyStructs => ListOfUnboxedReadOnlyStructs,
DelegateVsComparerPocBenchmarkType.ListOfUnboxedStructs => ListOfUnboxedStructs, DelegateVsComparerPocBenchmarkType.ListOfUnboxedStructs => ListOfUnboxedStructs,
DelegateVsComparerPocBenchmarkType.StaticFunc => StaticFunc, DelegateVsComparerPocBenchmarkType.StaticFunc => StaticFunc,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType), _ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
}; };
public override void Setup() public override void Setup()
{ {
Console.WriteLine("******* SETTING UP COMMON TEST DATA *******"); Console.WriteLine("******* SETTING UP COMMON TEST DATA *******");
_testObjects = Enumerable.Range(1, TestObjectCount).Select(x => (long)x); _testObjects = Enumerable.Range(1, TestObjectCount).Select(x => (long)x);
base.Setup(); base.Setup();
} }
public override void Cleanup() public override void Cleanup()
{ {
_testObjects = null; _testObjects = null;
base.Cleanup(); base.Cleanup();
} }
protected override void PrepareData<T>(T benchmarkType) protected override void PrepareData<T>(T benchmarkType)
{ {
switch (benchmarkType) switch (benchmarkType)
{ {
case DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs: case DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs:
_testListOfBoxedStructs = _testObjects!.Select(_ => (IComparer<long>)new MyComparerStruct<long>()); _testListOfBoxedStructs = _testObjects!.Select(_ => (IComparer<long>)new MyComparerStruct<long>());
break; break;
case DelegateVsComparerPocBenchmarkType.ListOfClasses: case DelegateVsComparerPocBenchmarkType.ListOfClasses:
_testListOfClasses = _testObjects!.Select(_ => new MyComparer()); _testListOfClasses = _testObjects!.Select(_ => new MyComparer());
break; break;
case DelegateVsComparerPocBenchmarkType.ListOfComparers: case DelegateVsComparerPocBenchmarkType.ListOfComparers:
_testListOfComparers = _testObjects!.Select(_ => (IComparer<long>)new MyComparer()); _testListOfComparers = _testObjects!.Select(_ => (IComparer<long>)new MyComparer());
break; break;
case DelegateVsComparerPocBenchmarkType.ListOfDelegates: case DelegateVsComparerPocBenchmarkType.ListOfDelegates:
_testListOfDelegates = _testObjects!.Select(_ => new Compare<long>((x, y) => new MyComparer().Compare(x, y))); _testListOfDelegates = _testObjects!.Select(_ => new Compare<long>((x, y) => new MyComparer().Compare(x, y)));
break; break;
case DelegateVsComparerPocBenchmarkType.ListOfFuncs: case DelegateVsComparerPocBenchmarkType.ListOfFuncs:
_testListOfFunctions = _testObjects!.Select<long, Func<long, long, int>>(static _ => new MyComparer().Compare); _testListOfFunctions = _testObjects!.Select<long, Func<long, long, int>>(static _ => new MyComparer().Compare);
break; break;
case DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs: case DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs:
_testListOfLocalFunctions = GetLocalFunctions(_testObjects!); _testListOfLocalFunctions = GetLocalFunctions(_testObjects!);
break; break;
case DelegateVsComparerPocBenchmarkType.ListOfStructs: case DelegateVsComparerPocBenchmarkType.ListOfStructs:
_testListOfStructs = _testObjects!.Select(_ => new MyComparerStruct<long>()); _testListOfStructs = _testObjects!.Select(_ => new MyComparerStruct<long>());
break; break;
} }
} }
} }
} }
@@ -1,40 +1,40 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using FluentAssertions; using FluentAssertions;
using Recyclable.CollectionsTests; using Recyclable.CollectionsTests;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public class EqualVsContainsInConsecutiveOrderBenchmarks : IDisposable public class EqualVsContainsInConsecutiveOrderBenchmarks : IDisposable
{ {
private const int TestItemsCount = 10000; private const int TestItemsCount = 10000;
private readonly IEnumerable<long> _list1 = RecyclableLongListTestData.CreateTestData(TestItemsCount).ToRecyclableList(); private readonly IEnumerable<long> _list1 = RecyclableLongListTestData.CreateTestData(TestItemsCount).ToRecyclableList();
private readonly IEnumerable<long> _list2 = RecyclableLongListTestData.CreateTestData(TestItemsCount).ToArray(); private readonly IEnumerable<long> _list2 = RecyclableLongListTestData.CreateTestData(TestItemsCount).ToArray();
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void Equal() public void Equal()
{ {
_ = _list1.Should().Equal(_list2); _ = _list1.Should().Equal(_list2);
} }
[Benchmark] [Benchmark]
public void ContainsInConsecutiveOrder() public void ContainsInConsecutiveOrder()
{ {
_ = _list1.Should().ContainInConsecutiveOrder(_list2); _ = _list1.Should().ContainInConsecutiveOrder(_list2);
} }
public void Dispose() public void Dispose()
{ {
if (_list1 is IDisposable disposable1) if (_list1 is IDisposable disposable1)
{ {
disposable1.Dispose(); disposable1.Dispose();
} }
if (_list2 is IDisposable disposable2) if (_list2 is IDisposable disposable2)
{ {
disposable2.Dispose(); disposable2.Dispose();
} }
} }
} }
} }
@@ -1,57 +1,57 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
#pragma warning disable CA1822 #pragma warning disable CA1822
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public class GreaterOperatorVsDoesntEqualOperatorBenchmarks public class GreaterOperatorVsDoesntEqualOperatorBenchmarks
{ {
private static readonly long num = RecyclableDefaults.MinItemsCountForParallelization; private static readonly long num = RecyclableDefaults.MinItemsCountForParallelization;
public void Setup() public void Setup()
{ {
Console.WriteLine($"*** We're comparing if {num} is < 0 ***"); Console.WriteLine($"*** We're comparing if {num} is < 0 ***");
} }
public void Cleanup() { } public void Cleanup() { }
#pragma warning disable RCS1163, IDE0060 #pragma warning disable RCS1163, IDE0060
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
private static void DoNothing<T>(T item) private static void DoNothing<T>(T item)
{ {
} }
#pragma warning restore RCS1163 #pragma warning restore RCS1163
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void CompareWithGreater() public void CompareWithGreater()
{ {
DoNothing(num > 0 ? 1 : 0); DoNothing(num > 0 ? 1 : 0);
} }
[Benchmark] [Benchmark]
public void CompareWithGreaterEqual() public void CompareWithGreaterEqual()
{ {
DoNothing(num >= 0 ? 1 : 0); DoNothing(num >= 0 ? 1 : 0);
} }
[Benchmark] [Benchmark]
public void CompareWithDoesntEqual() public void CompareWithDoesntEqual()
{ {
DoNothing(num != 0 ? 1 : 0); DoNothing(num != 0 ? 1 : 0);
} }
[Benchmark] [Benchmark]
public void CompareWithLess() public void CompareWithLess()
{ {
DoNothing(num < 0 ? 1 : 0); DoNothing(num < 0 ? 1 : 0);
} }
[Benchmark] [Benchmark]
public void CompareWithEqual() public void CompareWithEqual()
{ {
DoNothing(num == 0 ? 0 : 1); DoNothing(num == 0 ? 0 : 1);
} }
} }
} }
@@ -1,54 +1,54 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
#pragma warning disable CA1822 #pragma warning disable CA1822
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public class LessOperatorVsAndOperatorBenchmarks public class LessOperatorVsAndOperatorBenchmarks
{ {
private const int bitShift = 1 << 31; private const int bitShift = 1 << 31;
private static readonly Random _rnd = new(1); private static readonly Random _rnd = new(1);
private static int num = -Math.Abs(_rnd.Next()); private static int num = -Math.Abs(_rnd.Next());
public void Setup() public void Setup()
{ {
num = -1; num = -1;
Console.WriteLine($"*** We're comparing if {num} is < 0 ***"); Console.WriteLine($"*** We're comparing if {num} is < 0 ***");
} }
public void Cleanup() { } public void Cleanup() { }
#pragma warning disable RCS1163, IDE0060 #pragma warning disable RCS1163, IDE0060
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
private static void DoNothing<T>(T item) private static void DoNothing<T>(T item)
{ {
} }
#pragma warning restore RCS1163 #pragma warning restore RCS1163
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void CompareWithLess() public void CompareWithLess()
{ {
DoNothing(num < 0); DoNothing(num < 0);
} }
[Benchmark] [Benchmark]
public void CompareWithAnd() public void CompareWithAnd()
{ {
DoNothing((num & bitShift) != 0); DoNothing((num & bitShift) != 0);
} }
[Benchmark] [Benchmark]
public void CompareWithEqual() public void CompareWithEqual()
{ {
DoNothing(num == -1); DoNothing(num == -1);
} }
[Benchmark] [Benchmark]
public void CompareToZero() public void CompareToZero()
{ {
DoNothing(num == 0); DoNothing(num == 0);
} }
} }
} }
@@ -1,376 +1,376 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#pragma warning disable xUnit1026, RCS1163, IDE0060 #pragma warning disable xUnit1026, RCS1163, IDE0060
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
public class ModuloPocBenchmarks public class ModuloPocBenchmarks
{ {
public static void Run() public static void Run()
{ {
var benchmark = new ModuloPocBenchmarks(); var benchmark = new ModuloPocBenchmarks();
benchmark.Setup(); benchmark.Setup();
benchmark.AddWithFor(); benchmark.AddWithFor();
benchmark.AddWithForOptimized(); benchmark.AddWithForOptimized();
benchmark.DividerAndModulusWithOperator(); benchmark.DividerAndModulusWithOperator();
benchmark.MathDivRem(); benchmark.MathDivRem();
benchmark.MixedApproachWithForLoopOptimized(); benchmark.MixedApproachWithForLoopOptimized();
benchmark.MixedApproachWithSubtractWithStackalloc(); benchmark.MixedApproachWithSubtractWithStackalloc();
benchmark.MixedApproachWithSubtractWithWhile(); benchmark.MixedApproachWithSubtractWithWhile();
benchmark.MixedApproachWithSubtractWithWhileAsVector(); benchmark.MixedApproachWithSubtractWithWhileAsVector();
benchmark.MixedApproachWithSubtractWithWhileWithSse2(); benchmark.MixedApproachWithSubtractWithWhileWithSse2();
benchmark.SubtractWithWhile(); benchmark.SubtractWithWhile();
benchmark.SubtractWithWhileWithSse2(); benchmark.SubtractWithWhileWithSse2();
benchmark.XDivYMulX(); benchmark.XDivYMulX();
benchmark.XDivYMulXWithLocalVar(); benchmark.XDivYMulXWithLocalVar();
} }
private long _cutoff; private long _cutoff;
private Vector128<long> _increment; private Vector128<long> _increment;
private long _lastCaseIndex; private long _lastCaseIndex;
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
_cutoff = checked((long)BlockSize << 4); _cutoff = checked((long)BlockSize << 4);
_increment = Vector128.Create(-BlockSize, 1L); _increment = Vector128.Create(-BlockSize, 1L);
_lastCaseIndex = 0; _lastCaseIndex = 0;
} }
private static void DoNothing<T>(T result, T remainder, [CallerMemberName] string? callerName = null) private static void DoNothing<T>(T result, T remainder, [CallerMemberName] string? callerName = null)
{ {
//Console.WriteLine($"{callerName}: result = {result}, remainder = {remainder}"); //Console.WriteLine($"{callerName}: result = {result}, remainder = {remainder}");
} }
//[Params(123, 10, 2, int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 10, int.MaxValue / 15, int.MaxValue / 20)] //[Params(123, 10, 2, int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 10, int.MaxValue / 15, int.MaxValue / 20)]
[Params(int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 8, int.MaxValue / 10, int.MaxValue / 100)] [Params(int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 8, int.MaxValue / 10, int.MaxValue / 100)]
//[Params(int.MaxValue / 10, int.MaxValue / 20, int.MaxValue / 100)] //[Params(int.MaxValue / 10, int.MaxValue / 20, int.MaxValue / 100)]
public int BlockSize { get; set; } = int.MaxValue; public int BlockSize { get; set; } = int.MaxValue;
[Params(int.MaxValue, int.MaxValue / 1000, int.MaxValue / 10000)] [Params(int.MaxValue, int.MaxValue / 1000, int.MaxValue / 10000)]
//[Params(int.MaxValue)] //[Params(int.MaxValue)]
//[Params(int.MaxValue * 100L)] //[Params(int.MaxValue * 100L)]
public long TestNumber { get; set; } = int.MaxValue; public long TestNumber { get; set; } = int.MaxValue;
private void CalculateDividerAndModulusWithOperator(long i) private void CalculateDividerAndModulusWithOperator(long i)
{ {
DoNothing(i / BlockSize, i % BlockSize); DoNothing(i / BlockSize, i % BlockSize);
} }
private void CalculateWithMathDivRem(long i) private void CalculateWithMathDivRem(long i)
{ {
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder); DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
} }
private void CalculateWithAddWithFor(long i) private void CalculateWithAddWithFor(long i)
{ {
int result = 0; int result = 0;
int remainder = 0; int remainder = 0;
int blockSize = BlockSize; int blockSize = BlockSize;
for (long iteration = 0; iteration < i; iteration++) for (long iteration = 0; iteration < i; iteration++)
{ {
if (remainder > blockSize - 1) if (remainder > blockSize - 1)
{ {
remainder = 0; remainder = 0;
result++; result++;
} }
remainder++; remainder++;
} }
DoNothing(result, remainder); DoNothing(result, remainder);
} }
private void CalculateWithAddWithForOptimized(long i) private void CalculateWithAddWithForOptimized(long i)
{ {
long total = 0; long total = 0;
int result = 0; int result = 0;
int blockSize = BlockSize; int blockSize = BlockSize;
while (total + blockSize <= i) while (total + blockSize <= i)
{ {
total += blockSize; total += blockSize;
result++; result++;
} }
DoNothing(result, i - total); DoNothing(result, i - total);
} }
private void CalculateWithXDivYMulX(long i) private void CalculateWithXDivYMulX(long i)
{ {
DoNothing(i / BlockSize, i - (i / BlockSize * BlockSize)); DoNothing(i / BlockSize, i - (i / BlockSize * BlockSize));
} }
private void CalculateWithXDivYMulXWithLocalVar(long i) private void CalculateWithXDivYMulXWithLocalVar(long i)
{ {
int blockSize = BlockSize; int blockSize = BlockSize;
int result = (int)(i / blockSize); int result = (int)(i / blockSize);
DoNothing(result, i - (result * blockSize)); DoNothing(result, i - (result * blockSize));
} }
private void CalculateWithSubtractWithWhile(long i) private void CalculateWithSubtractWithWhile(long i)
{ {
int result = 0; int result = 0;
long remainder = i; long remainder = i;
int blockSize = BlockSize; int blockSize = BlockSize;
while (remainder >= blockSize) while (remainder >= blockSize)
{ {
remainder -= blockSize; remainder -= blockSize;
result++; result++;
} }
DoNothing(result, remainder); DoNothing(result, remainder);
} }
private void CalculateWithSubtractWithWhileWithSse2(long i) private void CalculateWithSubtractWithWhileWithSse2(long i)
{ {
var state = Vector128.Create(i, 0); var state = Vector128.Create(i, 0);
int blockSize = BlockSize; int blockSize = BlockSize;
var inc = _increment; var inc = _increment;
while (state.GetElement(0) >= blockSize) while (state.GetElement(0) >= blockSize)
{ {
state = Sse2.Add(state, inc); state = Sse2.Add(state, inc);
} }
DoNothing(state.GetElement(1), state.GetElement(0)); DoNothing(state.GetElement(1), state.GetElement(0));
} }
private void CalculateWithMixedApproachWithSubtractWithWhile(long i) private void CalculateWithMixedApproachWithSubtractWithWhile(long i)
{ {
long remainder; long remainder;
if (i >= _cutoff) if (i >= _cutoff)
{ {
DoNothing(Math.DivRem(i, BlockSize, out remainder), remainder); DoNothing(Math.DivRem(i, BlockSize, out remainder), remainder);
} }
else else
{ {
int result = 0; int result = 0;
int blockSize = BlockSize; int blockSize = BlockSize;
remainder = i; remainder = i;
while (remainder >= blockSize) while (remainder >= blockSize)
{ {
remainder -= blockSize; remainder -= blockSize;
result++; result++;
} }
DoNothing(result, remainder); DoNothing(result, remainder);
} }
} }
private void CalculateWithMixedApproachWithSubtractWithWhileAsVector(long i) private void CalculateWithMixedApproachWithSubtractWithWhileAsVector(long i)
{ {
if (i >= _cutoff) if (i >= _cutoff)
{ {
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder); DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
} }
else else
{ {
var state = new Vector<long>(new[] { 0L, i, 0, 0 }); var state = new Vector<long>(new[] { 0L, i, 0, 0 });
var increment = new Vector<long>(new[] { 1L, -BlockSize, 0, 0 }); var increment = new Vector<long>(new[] { 1L, -BlockSize, 0, 0 });
int blockSize = BlockSize; int blockSize = BlockSize;
while (state[1] >= blockSize) while (state[1] >= blockSize)
{ {
state += increment; state += increment;
} }
DoNothing(state[0], state[1]); DoNothing(state[0], state[1]);
} }
} }
private void CalculateWithMixedApproachWithSubtractWithWhileWithSse2(long i) private void CalculateWithMixedApproachWithSubtractWithWhileWithSse2(long i)
{ {
if (i >= _cutoff) if (i >= _cutoff)
{ {
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder); DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
} }
else else
{ {
var increment = _increment; var increment = _increment;
var state = Vector128.Create(i, 0); var state = Vector128.Create(i, 0);
int blockSize = BlockSize; int blockSize = BlockSize;
while (state.GetElement(0) >= blockSize) while (state.GetElement(0) >= blockSize)
{ {
state = Sse2.Add(state, increment); state = Sse2.Add(state, increment);
} }
DoNothing(state.GetElement(1), state.GetElement(0)); DoNothing(state.GetElement(1), state.GetElement(0));
} }
} }
private void CalculateWithMixedApproachWithSubtractWithStackalloc(long i) private void CalculateWithMixedApproachWithSubtractWithStackalloc(long i)
{ {
if (i >= _cutoff) if (i >= _cutoff)
{ {
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder); DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
} }
else else
{ {
Span<long> state = stackalloc long[2] { i, 0 };// Vector128.Create(BlockSize, 0); Span<long> state = stackalloc long[2] { i, 0 };// Vector128.Create(BlockSize, 0);
int blockSize = BlockSize; int blockSize = BlockSize;
while (state[0] >= blockSize) while (state[0] >= blockSize)
{ {
state[0] -= blockSize; state[0] -= blockSize;
state[1]++; state[1]++;
} }
DoNothing(state[1], state[0]); DoNothing(state[1], state[0]);
} }
} }
private void CalculateWithMixedApproachWithForLoopOptimized(long i) private void CalculateWithMixedApproachWithForLoopOptimized(long i)
{ {
long total; long total;
if (i >= _cutoff) if (i >= _cutoff)
{ {
DoNothing(Math.DivRem(i, BlockSize, out total), total); DoNothing(Math.DivRem(i, BlockSize, out total), total);
} }
else else
{ {
total = 0; total = 0;
int result = 0; int result = 0;
int blockSize = BlockSize; int blockSize = BlockSize;
while (total + blockSize <= i) while (total + blockSize <= i)
{ {
total += blockSize; total += blockSize;
result++; result++;
} }
DoNothing(result, i - total); DoNothing(result, i - total);
} }
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void DividerAndModulusWithOperator() public void DividerAndModulusWithOperator()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateDividerAndModulusWithOperator(i); CalculateDividerAndModulusWithOperator(i);
} }
} }
[Benchmark] [Benchmark]
public void MathDivRem() public void MathDivRem()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithMathDivRem(i); CalculateWithMathDivRem(i);
} }
} }
[Benchmark] [Benchmark]
public void AddWithFor() public void AddWithFor()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithAddWithFor(i); CalculateWithAddWithFor(i);
} }
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void AddWithForOptimized() public void AddWithForOptimized()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithAddWithForOptimized(i); CalculateWithAddWithForOptimized(i);
} }
} }
[Benchmark] [Benchmark]
public void XDivYMulX() public void XDivYMulX()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithXDivYMulX(i); CalculateWithXDivYMulX(i);
} }
} }
[Benchmark] [Benchmark]
public void XDivYMulXWithLocalVar() public void XDivYMulXWithLocalVar()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithXDivYMulXWithLocalVar(i); CalculateWithXDivYMulXWithLocalVar(i);
} }
} }
[Benchmark] [Benchmark]
public void SubtractWithWhile() public void SubtractWithWhile()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithSubtractWithWhile(i); CalculateWithSubtractWithWhile(i);
} }
} }
[Benchmark] [Benchmark]
public void SubtractWithWhileWithSse2() public void SubtractWithWhileWithSse2()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithSubtractWithWhileWithSse2(i); CalculateWithSubtractWithWhileWithSse2(i);
} }
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhile() public void MixedApproachWithSubtractWithWhile()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithMixedApproachWithSubtractWithWhile(i); CalculateWithMixedApproachWithSubtractWithWhile(i);
} }
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhileAsVector() public void MixedApproachWithSubtractWithWhileAsVector()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithMixedApproachWithSubtractWithWhileAsVector(i); CalculateWithMixedApproachWithSubtractWithWhileAsVector(i);
} }
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhileWithSse2() public void MixedApproachWithSubtractWithWhileWithSse2()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithMixedApproachWithSubtractWithWhileWithSse2(i); CalculateWithMixedApproachWithSubtractWithWhileWithSse2(i);
} }
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithStackalloc() public void MixedApproachWithSubtractWithStackalloc()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithMixedApproachWithSubtractWithStackalloc(i); CalculateWithMixedApproachWithSubtractWithStackalloc(i);
} }
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void MixedApproachWithForLoopOptimized() public void MixedApproachWithForLoopOptimized()
{ {
long testNumber = TestNumber; long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--) for (long i = testNumber; i >= _lastCaseIndex; i--)
{ {
CalculateWithMixedApproachWithForLoopOptimized(i); CalculateWithMixedApproachWithForLoopOptimized(i);
} }
} }
} }
} }
@@ -1,142 +1,142 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public abstract class PocBenchmarkBase<TBenchmarkType> public abstract class PocBenchmarkBase<TBenchmarkType>
{ {
public virtual TBenchmarkType? DataType { get; set; } public virtual TBenchmarkType? DataType { get; set; }
public virtual TBenchmarkType? BaseDataType { get; set; } public virtual TBenchmarkType? BaseDataType { get; set; }
private Action? _testMethod; private Action? _testMethod;
protected Action TestMethod protected Action TestMethod
{ {
get => _testMethod ?? throw new OperationCanceledException("Skip - No actual defined"); get => _testMethod ?? throw new OperationCanceledException("Skip - No actual defined");
set => _testMethod = value; set => _testMethod = value;
} }
private Action? _baselineMethod; private Action? _baselineMethod;
protected Action BaselineMethod protected Action BaselineMethod
{ {
get => _baselineMethod ?? throw new OperationCanceledException("Skip - No baseline defined"); get => _baselineMethod ?? throw new OperationCanceledException("Skip - No baseline defined");
set => _baselineMethod = value; set => _baselineMethod = value;
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void Baseline() => BaselineMethod.Invoke(); public void Baseline() => BaselineMethod.Invoke();
[Benchmark] [Benchmark]
public void Actual() => TestMethod.Invoke(); public void Actual() => TestMethod.Invoke();
// 0 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524_288 // 0 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524_288
// 1048576 2097152 4194304 8388608 16777216 33554432 67108864 134217728 268435456 536870912 // 1048576 2097152 4194304 8388608 16777216 33554432 67108864 134217728 268435456 536870912
// 1073741824 2147483648 // 1073741824 2147483648
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)] // [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)]
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)] // [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)]
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 100_000_000)] // [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 100_000_000)]
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 850_000 - 1, 1_000_000, RecyclableDefaults.MaxPooledBlockSize)] // [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 850_000 - 1, 1_000_000, RecyclableDefaults.MaxPooledBlockSize)]
// [Params(1_000_000, 10_000_000, 100_000_000, 1_000_000_000)] // [Params(1_000_000, 10_000_000, 100_000_000, 1_000_000_000)]
// [Params(1, 10, 64, 128, 192, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144)] // [Params(1, 10, 64, 128, 192, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144)]
// [Params(524_288, 786_432, 1_048_576)] // [Params(524_288, 786_432, 1_048_576)]
// [Params(262_144, 393_216, 524_288)] // [Params(262_144, 393_216, 524_288)]
// [Params(850_000_000)] // [Params(850_000_000)]
// [Params(100_000_000)] // [Params(100_000_000)]
// [Params(RecyclableDefaults.MinItemsCountForParallelization)] // [Params(RecyclableDefaults.MinItemsCountForParallelization)]
// [Params(127)] // [Params(127)]
// [Params(16)] // [Params(16)]
// [Params(10, 15, 128)] // [Params(10, 15, 128)]
// [Params(1)] // [Params(1)]
// [Params(RecyclableDefaults.MaxPooledBlockSize)] // [Params(RecyclableDefaults.MaxPooledBlockSize)]
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 10_000_000, 33_554_432, 50_000_000, 100_000_000)] // [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 10_000_000, 33_554_432, 50_000_000, 100_000_000)]
// *** [Params(RecyclableDefaults.MinPooledArrayLength - 3, RecyclableDefaults.MinPooledArrayLength - 2, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinPooledArrayLength + 1, RecyclableDefaults.MinPooledArrayLength + 2, RecyclableDefaults.MinPooledArrayLength + 3)] // *** [Params(RecyclableDefaults.MinPooledArrayLength - 3, RecyclableDefaults.MinPooledArrayLength - 2, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinPooledArrayLength + 1, RecyclableDefaults.MinPooledArrayLength + 2, RecyclableDefaults.MinPooledArrayLength + 3)]
[Params(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 70, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)] [Params(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 70, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)]
// *** [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 70, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)] // *** [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 70, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)]
// [Params(256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)] // [Params(256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)]
// [Params(RecyclableDefaults.MinItemsCountForParallelization, 1_048_576, 2_097_152, 4_194_304, 8_388_608, 16_777_216, 33_554_432, RecyclableDefaults.MaxPooledBlockSize)] // [Params(RecyclableDefaults.MinItemsCountForParallelization, 1_048_576, 2_097_152, 4_194_304, 8_388_608, 16_777_216, 33_554_432, RecyclableDefaults.MaxPooledBlockSize)]
// *** [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 64, 70, 80, 90, 100, 127, 128)] // *** [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 64, 70, 80, 90, 100, 127, 128)]
// [Params(1, 10, 1024, 10240, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinItemsCountForParallelization)] // [Params(1, 10, 1024, 10240, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinItemsCountForParallelization)]
// [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)] // [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)]
// [Params(1_048_576)] // [Params(1_048_576)]
public virtual int TestObjectCount { get; set; } = 0; //8_192;//131_072; //1; //33_554_432; //524_288; //131_072; public virtual int TestObjectCount { get; set; } = 0; //8_192;//131_072; //1; //33_554_432; //524_288; //131_072;
public virtual int TestObjectCountForSlowMethods { get; private set; } public virtual int TestObjectCountForSlowMethods { get; private set; }
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200)] // [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200)]
// [Params(10, 20, 50, 100)] // [Params(10, 20, 50, 100)]
// [Params(1, 10)] // [Params(1, 10)]
public int Divider = 10; public int Divider = 10;
//public int BlockSize = 1_048_576;//int.MaxValue - 1; //public int BlockSize = 1_048_576;//int.MaxValue - 1;
public int BlockSize { get; private set; } public int BlockSize { get; private set; }
public byte BlockSizePow2BitShift => checked((byte)(31 - BitOperations.LeadingZeroCount((uint)BlockSize))); public byte BlockSizePow2BitShift => checked((byte)(31 - BitOperations.LeadingZeroCount((uint)BlockSize)));
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
protected static void DoNothing<T>(in T item) protected static void DoNothing<T>(in T item)
{ {
_ = item; _ = item;
} }
protected static Exception CreateUnknownBenchmarkTypeException<T>(T benchmarkType) protected static Exception CreateUnknownBenchmarkTypeException<T>(T benchmarkType)
{ {
return new InvalidOperationException($"******* UNKNOWN BENCHMARK TYPE {{{benchmarkType}}} *******"); return new InvalidOperationException($"******* UNKNOWN BENCHMARK TYPE {{{benchmarkType}}} *******");
} }
protected static Exception CreateMethodNotFoundException(in string methodName, in string? className) protected static Exception CreateMethodNotFoundException(in string methodName, in string? className)
{ {
return new MethodAccessException($"******* METHOD {{{methodName}}} NOT FOUND IN CLASS {{{className}}} *******"); return new MethodAccessException($"******* METHOD {{{methodName}}} NOT FOUND IN CLASS {{{className}}} *******");
} }
protected virtual void PrepareData<T>(T benchmarkType) { } protected virtual void PrepareData<T>(T benchmarkType) { }
protected virtual Action? GetTestMethod(TBenchmarkType? benchmarkType) => null; protected virtual Action? GetTestMethod(TBenchmarkType? benchmarkType) => null;
[GlobalCleanup] [GlobalCleanup]
public virtual void Cleanup() public virtual void Cleanup()
{ {
_baselineMethod = null; _baselineMethod = null;
_testMethod = null; _testMethod = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
} }
[GlobalSetup] [GlobalSetup]
public virtual void Setup() public virtual void Setup()
{ {
Console.WriteLine($"******* SETTING UP TEST CASE FOR BENCHMARK {{{DataType}}} *******"); Console.WriteLine($"******* SETTING UP TEST CASE FOR BENCHMARK {{{DataType}}} *******");
TestObjectCountForSlowMethods = (TestObjectCount / 2) + 1; TestObjectCountForSlowMethods = (TestObjectCount / 2) + 1;
BlockSize = TestObjectCount switch BlockSize = TestObjectCount switch
{ {
0 => 1, 0 => 1,
> 0 and <= 10_240 => TestObjectCount, > 0 and <= 10_240 => TestObjectCount,
_ => TestObjectCount / Divider > 0 ? TestObjectCount / Divider : TestObjectCount _ => TestObjectCount / Divider > 0 ? TestObjectCount / Divider : TestObjectCount
}; };
if (BlockSize > 0) if (BlockSize > 0)
{ {
BlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)BlockSize)); BlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)BlockSize));
} }
Console.WriteLine($"******* BaseDataType = {BaseDataType}; default = {default(TBenchmarkType)} *******"); Console.WriteLine($"******* BaseDataType = {BaseDataType}; default = {default(TBenchmarkType)} *******");
if (!EqualityComparer<TBenchmarkType>.Default.Equals(BaseDataType, default)) if (!EqualityComparer<TBenchmarkType>.Default.Equals(BaseDataType, default))
{ {
Console.WriteLine("******* SETTING UP BASELINE DATA *******"); Console.WriteLine("******* SETTING UP BASELINE DATA *******");
PrepareData(BaseDataType); PrepareData(BaseDataType);
_baselineMethod = GetTestMethod(BaseDataType); _baselineMethod = GetTestMethod(BaseDataType);
} }
//~ We don't want to prepare data if baseline data is the same is benchmark data. //~ We don't want to prepare data if baseline data is the same is benchmark data.
//~ One would override another - waste of time & resources. //~ One would override another - waste of time & resources.
if (!EqualityComparer<TBenchmarkType>.Default.Equals(BaseDataType, DataType)) if (!EqualityComparer<TBenchmarkType>.Default.Equals(BaseDataType, DataType))
{ {
Console.WriteLine("******* SETTING UP TEST CASE DATA *******"); Console.WriteLine("******* SETTING UP TEST CASE DATA *******");
//~ If BaseDataType == null, then we'll come here only if DataType != null. //~ If BaseDataType == null, then we'll come here only if DataType != null.
PrepareData(DataType!); PrepareData(DataType!);
_testMethod = GetTestMethod(DataType); _testMethod = GetTestMethod(DataType);
} }
Console.WriteLine("******* DATA PREPARED *******"); Console.WriteLine("******* DATA PREPARED *******");
} }
} }
} }
@@ -1,47 +1,47 @@
using System.Reflection; using System.Reflection;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Collections.Pooled; using Collections.Pooled;
using Recyclable.Collections.Benchmarks.Core; using Recyclable.Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
public partial class RecyclableLongListPocBenchmarks<T> : PocBenchmarkBase<T> public partial class RecyclableLongListPocBenchmarks<T> : PocBenchmarkBase<T>
{ {
private long[]? _testArray; private long[]? _testArray;
public long[] TestObjects => _testArray ?? throw new NullReferenceException("Something is wrong and test objects are null"); public long[] TestObjects => _testArray ?? throw new NullReferenceException("Something is wrong and test objects are null");
public RecyclableLongList<long> TestObjectsAsRecyclableLongList => _testRecyclableLongList ?? throw new NullReferenceException($"Something is wrong and {nameof(RecyclableLongList<long>)} is null"); public RecyclableLongList<long> TestObjectsAsRecyclableLongList => _testRecyclableLongList ?? throw new NullReferenceException($"Something is wrong and {nameof(RecyclableLongList<long>)} is null");
private RecyclableLongList<long>? _testRecyclableLongList; private RecyclableLongList<long>? _testRecyclableLongList;
private PooledList<long>? _testPooledList; private PooledList<long>? _testPooledList;
internal PooledList<long> TestPooledList => _testPooledList ?? throw new NullReferenceException($"Something is wrong and {nameof(PooledList<long>)} is null"); internal PooledList<long> TestPooledList => _testPooledList ?? throw new NullReferenceException($"Something is wrong and {nameof(PooledList<long>)} is null");
private static readonly MethodInfo _ensureCapacityNewFunc; private static readonly MethodInfo _ensureCapacityNewFunc;
static RecyclableLongListPocBenchmarks() static RecyclableLongListPocBenchmarks()
{ {
_ensureCapacityNewFunc = typeof(RecyclableLongList<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic) _ensureCapacityNewFunc = typeof(RecyclableLongList<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic)
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongList<long>)}"); ?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongList<long>)}");
} }
public override void Setup() public override void Setup()
{ {
Console.WriteLine("******* SETTING UP EXPECTED RESULTS DATA *******"); Console.WriteLine("******* SETTING UP EXPECTED RESULTS DATA *******");
_testArray = DataGenerator.EnumerateTestObjects(TestObjectCount); _testArray = DataGenerator.EnumerateTestObjects(TestObjectCount);
base.Setup(); base.Setup();
} }
public override void Cleanup() public override void Cleanup()
{ {
_testArray = null; _testArray = null;
_testRecyclableLongList?.Dispose(); _testRecyclableLongList?.Dispose();
_testRecyclableLongList = null; _testRecyclableLongList = null;
_testPooledList?.Dispose(); _testPooledList?.Dispose();
_testPooledList = null; _testPooledList = null;
base.Cleanup(); base.Cleanup();
} }
} }
} }
@@ -1,26 +1,26 @@
BenchmarkDotNet=v0.13.3, OS=ubuntu 20.04 BenchmarkDotNet=v0.13.3, OS=ubuntu 20.04
Intel Xeon Platinum 8272CL CPU 2.60GHz, 1 CPU, 4 logical and 2 physical cores Intel Xeon Platinum 8272CL CPU 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK=7.0.203 .NET SDK=7.0.203
[Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
DefaultJob : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 DefaultJob : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
| Method | Mean | Error | StdDev | Ratio | RatioSD | | Method | Mean | Error | StdDev | Ratio | RatioSD |
|------------------------- |--------:|---------:|---------:|------:|--------:| |------------------------- |--------:|---------:|---------:|------:|--------:|
| RecyclableLongList_IndexOfV1 | 2.274 s | 0.0447 s | 0.0894 s | 1.00 | 0.04 | | RecyclableLongList_IndexOfV1 | 2.274 s | 0.0447 s | 0.0894 s | 1.00 | 0.04 |
| RecyclableLongList_IndexOfV2 | 2.302 s | 0.0456 s | 0.0750 s | 1.00 | 0.00 | | RecyclableLongList_IndexOfV2 | 2.302 s | 0.0456 s | 0.0750 s | 1.00 | 0.00 |
| RecyclableLongList_IndexOf | 2.234 s | 0.0446 s | 0.0681 s | 0.97 | 0.05 | | RecyclableLongList_IndexOf | 2.234 s | 0.0446 s | 0.0681 s | 0.97 | 0.05 |
// * Hints * // * Hints *
Outliers Outliers
RecyclableLongListPocBenchmarks.RecyclableLongList_IndexOfV2: Default -> 2 outliers were removed (2.54 s, 2.56 s) RecyclableLongListPocBenchmarks.RecyclableLongList_IndexOfV2: Default -> 2 outliers were removed (2.54 s, 2.56 s)
RecyclableLongListPocBenchmarks.RecyclableLongList_IndexOf: Default -> 1 outlier was detected (2.06 s) RecyclableLongListPocBenchmarks.RecyclableLongList_IndexOf: Default -> 1 outlier was detected (2.06 s)
// * Legends * // * Legends *
Mean : Arithmetic mean of all measurements Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements StdDev : Standard deviation of all measurements
Ratio : Mean of the ratio distribution ([Current]/[Baseline]) Ratio : Mean of the ratio distribution ([Current]/[Baseline])
RatioSD : Standard deviation of the ratio distribution ([Current]/[Baseline]) RatioSD : Standard deviation of the ratio distribution ([Current]/[Baseline])
1 s : 1 Second (1 sec) 1 s : 1 Second (1 sec)
@@ -1,364 +1,364 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
#pragma warning disable RCS1085 #pragma warning disable RCS1085
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public class RefVsInstanceMemberPocBenchmarks public class RefVsInstanceMemberPocBenchmarks
{ {
public static void Run() public static void Run()
{ {
var benchmark = new RefVsInstanceMemberPocBenchmarks(); var benchmark = new RefVsInstanceMemberPocBenchmarks();
benchmark.ArrayInstanceMember2(); benchmark.ArrayInstanceMember2();
benchmark.ArrayInstanceMember3(); benchmark.ArrayInstanceMember3();
benchmark.ArrayInstanceMember4(); benchmark.ArrayInstanceMember4();
benchmark.ArrayInstanceMember5(); benchmark.ArrayInstanceMember5();
benchmark.ArrayInstanceMember6(); benchmark.ArrayInstanceMember6();
benchmark.ArrayInstanceMemberLocalVar5(); benchmark.ArrayInstanceMemberLocalVar5();
benchmark.ArrayInstanceMemberLocalVar6(); benchmark.ArrayInstanceMemberLocalVar6();
benchmark.IntInstanceMember2(); benchmark.IntInstanceMember2();
benchmark.IntInstanceMember3(); benchmark.IntInstanceMember3();
benchmark.IntInstanceMember4(); benchmark.IntInstanceMember4();
benchmark.IntInstanceMember5(); benchmark.IntInstanceMember5();
benchmark.RefToArray2(); benchmark.RefToArray2();
benchmark.RefToArray3(); benchmark.RefToArray3();
benchmark.RefToArray4(); benchmark.RefToArray4();
benchmark.RefToArray5(); benchmark.RefToArray5();
benchmark.RefToArray6(); benchmark.RefToArray6();
benchmark.RefToInt2(); benchmark.RefToInt2();
benchmark.RefToInt3(); benchmark.RefToInt3();
} }
private readonly TestCase _testCase = new(); private readonly TestCase _testCase = new();
private static void DoNothing(int count) private static void DoNothing(int count)
{ {
_ = count; _ = count;
} }
private class TestCase private class TestCase
{ {
private int[] _items = new int[123]; private int[] _items = new int[123];
private int _count = 123; private int _count = 123;
private static int Add(int count) private static int Add(int count)
{ {
return count; return count;
} }
public int Count public int Count
{ {
get => _count; get => _count;
set => _count = value; set => _count = value;
} }
public ref int GetCountAsRefCount2() public ref int GetCountAsRefCount2()
{ {
ref int count = ref _count; ref int count = ref _count;
count = 234; count = 234;
return ref count; return ref count;
} }
public ref int GetCountAsRefCount3() public ref int GetCountAsRefCount3()
{ {
ref int count = ref _count; ref int count = ref _count;
count = 234; count = 234;
_ = TestCase.Add(count); _ = TestCase.Add(count);
return ref count; return ref count;
} }
public ref int GetCountAsRefCount4() public ref int GetCountAsRefCount4()
{ {
ref int count = ref _count; ref int count = ref _count;
count = 234; count = 234;
_ = TestCase.Add(count); _ = TestCase.Add(count);
_ = TestCase.Add(count); _ = TestCase.Add(count);
return ref count; return ref count;
} }
public ref int GetCountAsRefCount5() public ref int GetCountAsRefCount5()
{ {
ref int count = ref _count; ref int count = ref _count;
count = 234; count = 234;
_ = TestCase.Add(count); _ = TestCase.Add(count);
_ = TestCase.Add(count); _ = TestCase.Add(count);
_ = TestCase.Add(count); _ = TestCase.Add(count);
return ref count; return ref count;
} }
public int GetCountAsInstanceMember2() public int GetCountAsInstanceMember2()
{ {
_count = 234; _count = 234;
return _count; return _count;
} }
public int GetCountAsInstanceMember3() public int GetCountAsInstanceMember3()
{ {
_count = 234; _count = 234;
_ = TestCase.Add(_count); _ = TestCase.Add(_count);
return _count; return _count;
} }
public int GetCountAsInstanceMember4() public int GetCountAsInstanceMember4()
{ {
_count = 234; _count = 234;
_ = TestCase.Add(_count); _ = TestCase.Add(_count);
_ = TestCase.Add(_count); _ = TestCase.Add(_count);
return _count; return _count;
} }
public int GetCountAsInstanceMember5() public int GetCountAsInstanceMember5()
{ {
_count = 234; _count = 234;
_ = TestCase.Add(_count); _ = TestCase.Add(_count);
_ = TestCase.Add(_count); _ = TestCase.Add(_count);
_ = TestCase.Add(_count); _ = TestCase.Add(_count);
return _count; return _count;
} }
public int GetLengthAsArray2() public int GetLengthAsArray2()
{ {
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
return _items.Length; return _items.Length;
} }
public int GetLengthAsArray3() public int GetLengthAsArray3()
{ {
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
return _items.Length; return _items.Length;
} }
public int GetLengthAsArray4() public int GetLengthAsArray4()
{ {
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
return _items.Length; return _items.Length;
} }
public int GetLengthAsArray5() public int GetLengthAsArray5()
{ {
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
return _items.Length; return _items.Length;
} }
public int GetLengthAsArray6() public int GetLengthAsArray6()
{ {
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length); _ = TestCase.Add(_items.Length);
return _items.Length; return _items.Length;
} }
public int GetLengthAsArrayLocalVar5() public int GetLengthAsArrayLocalVar5()
{ {
var items = _items; var items = _items;
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
return items.Length; return items.Length;
} }
public int GetLengthAsArrayLocalVar6() public int GetLengthAsArrayLocalVar6()
{ {
var items = _items; var items = _items;
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
return items.Length; return items.Length;
} }
public int GetLengthAsRefArray2() public int GetLengthAsRefArray2()
{ {
ref int[] items = ref _items; ref int[] items = ref _items;
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
return items.Length; return items.Length;
} }
public int GetLengthAsRefArray3() public int GetLengthAsRefArray3()
{ {
ref int[] items = ref _items; ref int[] items = ref _items;
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
return items.Length; return items.Length;
} }
public int GetLengthAsRefArray4() public int GetLengthAsRefArray4()
{ {
ref int[] items = ref _items; ref int[] items = ref _items;
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
return items.Length; return items.Length;
} }
public int GetLengthAsRefArray5() public int GetLengthAsRefArray5()
{ {
ref int[] items = ref _items; ref int[] items = ref _items;
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
return items.Length; return items.Length;
} }
public int GetLengthAsRefArray6() public int GetLengthAsRefArray6()
{ {
ref int[] items = ref _items; ref int[] items = ref _items;
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length); _ = TestCase.Add(items.Length);
return items.Length; return items.Length;
} }
} }
[Benchmark] [Benchmark]
public void RefToInt2() public void RefToInt2()
{ {
var count = _testCase.GetCountAsRefCount2(); var count = _testCase.GetCountAsRefCount2();
DoNothing(count); DoNothing(count);
} }
[Benchmark] [Benchmark]
public void RefToInt3() public void RefToInt3()
{ {
var count = _testCase.GetCountAsRefCount3(); var count = _testCase.GetCountAsRefCount3();
DoNothing(count); DoNothing(count);
} }
[Benchmark] [Benchmark]
public void RefToInt4() public void RefToInt4()
{ {
var count = _testCase.GetCountAsRefCount4(); var count = _testCase.GetCountAsRefCount4();
DoNothing(count); DoNothing(count);
} }
[Benchmark] [Benchmark]
public void RefToInt5() public void RefToInt5()
{ {
var count = _testCase.GetCountAsRefCount5(); var count = _testCase.GetCountAsRefCount5();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void IntInstanceMember2() public void IntInstanceMember2()
{ {
var count = _testCase.GetCountAsInstanceMember2(); var count = _testCase.GetCountAsInstanceMember2();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void IntInstanceMember3() public void IntInstanceMember3()
{ {
var count = _testCase.GetCountAsInstanceMember3(); var count = _testCase.GetCountAsInstanceMember3();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void IntInstanceMember4() public void IntInstanceMember4()
{ {
var count = _testCase.GetCountAsInstanceMember4(); var count = _testCase.GetCountAsInstanceMember4();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void IntInstanceMember5() public void IntInstanceMember5()
{ {
var count = _testCase.GetCountAsInstanceMember5(); var count = _testCase.GetCountAsInstanceMember5();
DoNothing(count); DoNothing(count);
} }
[Benchmark] [Benchmark]
public void RefToArray2() public void RefToArray2()
{ {
var count = _testCase.GetLengthAsRefArray2(); var count = _testCase.GetLengthAsRefArray2();
DoNothing(count); DoNothing(count);
} }
[Benchmark] [Benchmark]
public void RefToArray3() public void RefToArray3()
{ {
var count = _testCase.GetLengthAsRefArray3(); var count = _testCase.GetLengthAsRefArray3();
DoNothing(count); DoNothing(count);
} }
[Benchmark] [Benchmark]
public void RefToArray4() public void RefToArray4()
{ {
var count = _testCase.GetLengthAsRefArray4(); var count = _testCase.GetLengthAsRefArray4();
DoNothing(count); DoNothing(count);
} }
[Benchmark] [Benchmark]
public void RefToArray5() public void RefToArray5()
{ {
var count = _testCase.GetLengthAsRefArray5(); var count = _testCase.GetLengthAsRefArray5();
DoNothing(count); DoNothing(count);
} }
[Benchmark] [Benchmark]
public void RefToArray6() public void RefToArray6()
{ {
var count = _testCase.GetLengthAsRefArray6(); var count = _testCase.GetLengthAsRefArray6();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void ArrayInstanceMember2() public void ArrayInstanceMember2()
{ {
var count = _testCase.GetLengthAsArray2(); var count = _testCase.GetLengthAsArray2();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void ArrayInstanceMember3() public void ArrayInstanceMember3()
{ {
var count = _testCase.GetLengthAsArray3(); var count = _testCase.GetLengthAsArray3();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void ArrayInstanceMember4() public void ArrayInstanceMember4()
{ {
var count = _testCase.GetLengthAsArray4(); var count = _testCase.GetLengthAsArray4();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void ArrayInstanceMember5() public void ArrayInstanceMember5()
{ {
var count = _testCase.GetLengthAsArray5(); var count = _testCase.GetLengthAsArray5();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void ArrayInstanceMember6() public void ArrayInstanceMember6()
{ {
var count = _testCase.GetLengthAsArray6(); var count = _testCase.GetLengthAsArray6();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void ArrayInstanceMemberLocalVar5() public void ArrayInstanceMemberLocalVar5()
{ {
var count = _testCase.GetLengthAsArrayLocalVar5(); var count = _testCase.GetLengthAsArrayLocalVar5();
DoNothing(count); DoNothing(count);
} }
[Benchmark(Baseline = false)] [Benchmark(Baseline = false)]
public void ArrayInstanceMemberLocalVar6() public void ArrayInstanceMemberLocalVar6()
{ {
var count = _testCase.GetLengthAsArrayLocalVar6(); var count = _testCase.GetLengthAsArrayLocalVar6();
DoNothing(count); DoNothing(count);
} }
} }
} }
@@ -1,45 +1,45 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public enum RoundBenchmarkType public enum RoundBenchmarkType
{ {
MathFloor, MathFloor,
MathRound, MathRound,
WithTypecasting, WithTypecasting,
} }
public class RoundBenchmarks : PocBenchmarkBase<RoundBenchmarkType> public class RoundBenchmarks : PocBenchmarkBase<RoundBenchmarkType>
{ {
[Params(RoundBenchmarkType.MathFloor)] [Params(RoundBenchmarkType.MathFloor)]
public override RoundBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; } public override RoundBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
[Params(RoundBenchmarkType.MathRound, RoundBenchmarkType.WithTypecasting)] [Params(RoundBenchmarkType.MathRound, RoundBenchmarkType.WithTypecasting)]
public override RoundBenchmarkType DataType { get => base.DataType; set => base.DataType = value; } public override RoundBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
private const double OptimalParallelSearchStep = 0.329; private const double OptimalParallelSearchStep = 0.329;
public void RoundWithMathFloor() public void RoundWithMathFloor()
{ {
DoNothing((long)Math.Floor(TestObjectCount * OptimalParallelSearchStep)); DoNothing((long)Math.Floor(TestObjectCount * OptimalParallelSearchStep));
} }
public void RoundWithMathRound() public void RoundWithMathRound()
{ {
DoNothing((long)Math.Round(TestObjectCount * OptimalParallelSearchStep, 0)); DoNothing((long)Math.Round(TestObjectCount * OptimalParallelSearchStep, 0));
} }
public void RoundWithTypeCasting() public void RoundWithTypeCasting()
{ {
DoNothing((long)(TestObjectCount * OptimalParallelSearchStep)); DoNothing((long)(TestObjectCount * OptimalParallelSearchStep));
} }
protected override Action GetTestMethod(RoundBenchmarkType benchmarkType) => benchmarkType switch protected override Action GetTestMethod(RoundBenchmarkType benchmarkType) => benchmarkType switch
{ {
RoundBenchmarkType.MathFloor => RoundWithMathFloor, RoundBenchmarkType.MathFloor => RoundWithMathFloor,
RoundBenchmarkType.MathRound => RoundWithMathRound, RoundBenchmarkType.MathRound => RoundWithMathRound,
RoundBenchmarkType.WithTypecasting => RoundWithTypeCasting, RoundBenchmarkType.WithTypecasting => RoundWithTypeCasting,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType), _ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
}; };
} }
} }
@@ -1,147 +1,147 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
static class RoundUpToPowerOf2Alternatives static class RoundUpToPowerOf2Alternatives
{ {
private static readonly int[] PowersOfTwo = private static readonly int[] PowersOfTwo =
{ {
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576,
2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824
}; };
public static int BinarySearch(int value) public static int BinarySearch(int value)
{ {
if (value <= 0) if (value <= 0)
{ {
throw new ArgumentException("Input must be a positive integer."); throw new ArgumentException("Input must be a positive integer.");
} }
// Find the smallest power of 2 greater than or equal to value using a lookup table // Find the smallest power of 2 greater than or equal to value using a lookup table
int index = Array.BinarySearch(PowersOfTwo, value); int index = Array.BinarySearch(PowersOfTwo, value);
if (index < 0) if (index < 0)
{ {
// If value is not a power of 2, use the next power of 2 from the table // If value is not a power of 2, use the next power of 2 from the table
index = ~index; index = ~index;
} }
return PowersOfTwo[index]; return PowersOfTwo[index];
} }
public static int RightBitShifting(int value) public static int RightBitShifting(int value)
{ {
// Decrease value by 1 to handle the case where the input is already a power of 2 // Decrease value by 1 to handle the case where the input is already a power of 2
value--; value--;
value |= value >> 1; value |= value >> 1;
value |= value >> 2; value |= value >> 2;
value |= value >> 4; value |= value >> 4;
value |= value >> 8; value |= value >> 8;
value |= value >> 16; value |= value >> 16;
return value + 1; return value + 1;
} }
public static int LeftBitShifting(int value) public static int LeftBitShifting(int value)
{ {
if (value == 0) if (value == 0)
{ {
return 1; return 1;
} }
// Ensure value is a power of 2 or the next power of 2 // Ensure value is a power of 2 or the next power of 2
int result = 1; int result = 1;
while (result < value) while (result < value)
{ {
result = checked(result << 1); result = checked(result << 1);
} }
return result; return result;
} }
} }
public enum RoundUpToPowerOf2BenchmarkType public enum RoundUpToPowerOf2BenchmarkType
{ {
Undefined, Undefined,
BitOperations, BitOperations,
RightBitShifting, RightBitShifting,
LeftBitShifting, LeftBitShifting,
BinarySearch, BinarySearch,
IsPowOf2, IsPowOf2,
IsPowOf2_GreaterThanFirst, IsPowOf2_GreaterThanFirst,
IsPowOf2_WithIf, IsPowOf2_WithIf,
IsPowOf2_GreaterEqual, IsPowOf2_GreaterEqual,
} }
public class RoundUpToPowerOf2vsOtherBenchmarks : PocBenchmarkBase<RoundUpToPowerOf2BenchmarkType> public class RoundUpToPowerOf2vsOtherBenchmarks : PocBenchmarkBase<RoundUpToPowerOf2BenchmarkType>
{ {
// [Params(-850_000, 850_000)] // [Params(-850_000, 850_000)]
[Params(-int.MaxValue, int.MaxValue)] [Params(-int.MaxValue, int.MaxValue)]
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; } public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
[Params(RoundUpToPowerOf2BenchmarkType.IsPowOf2)] [Params(RoundUpToPowerOf2BenchmarkType.IsPowOf2)]
public override RoundUpToPowerOf2BenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; } public override RoundUpToPowerOf2BenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
[Params(RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterThanFirst, RoundUpToPowerOf2BenchmarkType.IsPowOf2_WithIf, RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterEqual)] [Params(RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterThanFirst, RoundUpToPowerOf2BenchmarkType.IsPowOf2_WithIf, RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterEqual)]
// [Params(RoundUpToPowerOf2BenchmarkType.RightBitShifting, RoundUpToPowerOf2BenchmarkType.LeftBitShifting, RoundUpToPowerOf2BenchmarkType.BinarySearch)] // [Params(RoundUpToPowerOf2BenchmarkType.RightBitShifting, RoundUpToPowerOf2BenchmarkType.LeftBitShifting, RoundUpToPowerOf2BenchmarkType.BinarySearch)]
public override RoundUpToPowerOf2BenchmarkType DataType { get => base.DataType; set => base.DataType = value; } public override RoundUpToPowerOf2BenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
private void BitOperations() private void BitOperations()
{ {
DoNothing(System.Numerics.BitOperations.RoundUpToPowerOf2((uint)base.TestObjectCount)); DoNothing(System.Numerics.BitOperations.RoundUpToPowerOf2((uint)base.TestObjectCount));
} }
private void RightBitShifting() private void RightBitShifting()
{ {
DoNothing(RoundUpToPowerOf2Alternatives.RightBitShifting(TestObjectCount)); DoNothing(RoundUpToPowerOf2Alternatives.RightBitShifting(TestObjectCount));
} }
private void LeftBitShifting() private void LeftBitShifting()
{ {
DoNothing(RoundUpToPowerOf2Alternatives.LeftBitShifting(TestObjectCount)); DoNothing(RoundUpToPowerOf2Alternatives.LeftBitShifting(TestObjectCount));
} }
private void BinarySearch() private void BinarySearch()
{ {
DoNothing(RoundUpToPowerOf2Alternatives.BinarySearch(TestObjectCount)); DoNothing(RoundUpToPowerOf2Alternatives.BinarySearch(TestObjectCount));
} }
private void IsPowOf2() private void IsPowOf2()
{ {
DoNothing((TestObjectCount & (TestObjectCount - 1)) == 0 && TestObjectCount > 0); DoNothing((TestObjectCount & (TestObjectCount - 1)) == 0 && TestObjectCount > 0);
} }
private void IsPowOf2_GreaterThanFirst() private void IsPowOf2_GreaterThanFirst()
{ {
DoNothing(TestObjectCount > 0 && (TestObjectCount & (TestObjectCount - 1)) == 0); DoNothing(TestObjectCount > 0 && (TestObjectCount & (TestObjectCount - 1)) == 0);
} }
private void IsPowOf2_WithIf() private void IsPowOf2_WithIf()
{ {
DoNothing(TestObjectCount > 0 ? (TestObjectCount & (TestObjectCount - 1)) == 0 : false); DoNothing(TestObjectCount > 0 ? (TestObjectCount & (TestObjectCount - 1)) == 0 : false);
} }
private void IsPowOf2_GreaterEqual() private void IsPowOf2_GreaterEqual()
{ {
DoNothing(TestObjectCount >= 0 && TestObjectCount != 0 && (TestObjectCount & (TestObjectCount - 1)) == 0); DoNothing(TestObjectCount >= 0 && TestObjectCount != 0 && (TestObjectCount & (TestObjectCount - 1)) == 0);
} }
protected override Action? GetTestMethod(RoundUpToPowerOf2BenchmarkType benchmarkType) => benchmarkType switch protected override Action? GetTestMethod(RoundUpToPowerOf2BenchmarkType benchmarkType) => benchmarkType switch
{ {
RoundUpToPowerOf2BenchmarkType.BitOperations => BitOperations, RoundUpToPowerOf2BenchmarkType.BitOperations => BitOperations,
RoundUpToPowerOf2BenchmarkType.RightBitShifting => RightBitShifting, RoundUpToPowerOf2BenchmarkType.RightBitShifting => RightBitShifting,
RoundUpToPowerOf2BenchmarkType.LeftBitShifting => LeftBitShifting, RoundUpToPowerOf2BenchmarkType.LeftBitShifting => LeftBitShifting,
RoundUpToPowerOf2BenchmarkType.BinarySearch => BinarySearch, RoundUpToPowerOf2BenchmarkType.BinarySearch => BinarySearch,
RoundUpToPowerOf2BenchmarkType.IsPowOf2 => IsPowOf2, RoundUpToPowerOf2BenchmarkType.IsPowOf2 => IsPowOf2,
RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterThanFirst => IsPowOf2_GreaterThanFirst, RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterThanFirst => IsPowOf2_GreaterThanFirst,
RoundUpToPowerOf2BenchmarkType.IsPowOf2_WithIf => IsPowOf2_WithIf, RoundUpToPowerOf2BenchmarkType.IsPowOf2_WithIf => IsPowOf2_WithIf,
RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterEqual => IsPowOf2_GreaterEqual, RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterEqual => IsPowOf2_GreaterEqual,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType), _ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
}; };
protected override void PrepareData<T>(T benchmarkType) protected override void PrepareData<T>(T benchmarkType)
{ {
} }
} }
} }
@@ -1,64 +1,64 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public enum SpanConstructorVsTypecastingPocBenchmarkType public enum SpanConstructorVsTypecastingPocBenchmarkType
{ {
Unknown, Unknown,
Constructor, Constructor,
TypeCasting, TypeCasting,
} }
[MemoryDiagnoser] [MemoryDiagnoser]
public class SpanConstructorVsTypecastingPocBenchmarks : PocBenchmarkBase<SpanConstructorVsTypecastingPocBenchmarkType> public class SpanConstructorVsTypecastingPocBenchmarks : PocBenchmarkBase<SpanConstructorVsTypecastingPocBenchmarkType>
{ {
private int[]? TestData { get; set; } private int[]? TestData { get; set; }
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
private void DoNothing(ReadOnlySpan<int> span) { } private void DoNothing(ReadOnlySpan<int> span) { }
[Params(SpanConstructorVsTypecastingPocBenchmarkType.TypeCasting)] [Params(SpanConstructorVsTypecastingPocBenchmarkType.TypeCasting)]
public override SpanConstructorVsTypecastingPocBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; } public override SpanConstructorVsTypecastingPocBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
[Params(SpanConstructorVsTypecastingPocBenchmarkType.Constructor)] [Params(SpanConstructorVsTypecastingPocBenchmarkType.Constructor)]
public override SpanConstructorVsTypecastingPocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; } public override SpanConstructorVsTypecastingPocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
[Params(8)] [Params(8)]
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; } public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
public static void Run() public static void Run()
{ {
var benchmark = new SpanConstructorVsTypecastingPocBenchmarks(); var benchmark = new SpanConstructorVsTypecastingPocBenchmarks();
benchmark.Setup(); benchmark.Setup();
benchmark.Constructor(); benchmark.Constructor();
benchmark.TypeCasting(); benchmark.TypeCasting();
benchmark.Cleanup(); benchmark.Cleanup();
} }
public void Constructor() public void Constructor()
{ {
ReadOnlySpan<int> span = new(TestData); ReadOnlySpan<int> span = new(TestData);
DoNothing(span); DoNothing(span);
} }
public void TypeCasting() public void TypeCasting()
{ {
ReadOnlySpan<int> span = TestData; ReadOnlySpan<int> span = TestData;
DoNothing(span); DoNothing(span);
} }
public override void Setup() public override void Setup()
{ {
TestData = new int[TestObjectCount]; TestData = new int[TestObjectCount];
base.Setup(); base.Setup();
} }
protected override Action? GetTestMethod(SpanConstructorVsTypecastingPocBenchmarkType benchmarkType) => benchmarkType switch protected override Action? GetTestMethod(SpanConstructorVsTypecastingPocBenchmarkType benchmarkType) => benchmarkType switch
{ {
SpanConstructorVsTypecastingPocBenchmarkType.Constructor => Constructor, SpanConstructorVsTypecastingPocBenchmarkType.Constructor => Constructor,
SpanConstructorVsTypecastingPocBenchmarkType.TypeCasting => TypeCasting, SpanConstructorVsTypecastingPocBenchmarkType.TypeCasting => TypeCasting,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType), _ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
}; };
} }
} }
@@ -1,99 +1,99 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
public enum SpanVsArrayBenchmarkType public enum SpanVsArrayBenchmarkType
{ {
ArrayGetWithVar, ArrayGetWithVar,
ArrayGetWithField, ArrayGetWithField,
SpanGet, SpanGet,
ArraySetWithVar, ArraySetWithVar,
ArraySetWithField, ArraySetWithField,
SpanSet SpanSet
} }
public class SpanVsArrayBenchmarks : RecyclableBenchmarkBase<SpanVsArrayBenchmarkType> public class SpanVsArrayBenchmarks : RecyclableBenchmarkBase<SpanVsArrayBenchmarkType>
{ {
// [Params(SpanVsArrayBenchmarkType.ArrayGetWithVar, SpanVsArrayBenchmarkType.SpanGet)] // [Params(SpanVsArrayBenchmarkType.ArrayGetWithVar, SpanVsArrayBenchmarkType.SpanGet)]
// [Params(SpanVsArrayBenchmarkType.ArraySetWithVar, SpanVsArrayBenchmarkType.SpanSet)] // [Params(SpanVsArrayBenchmarkType.ArraySetWithVar, SpanVsArrayBenchmarkType.SpanSet)]
[Params [Params
( (
SpanVsArrayBenchmarkType.ArrayGetWithVar, SpanVsArrayBenchmarkType.SpanGet, SpanVsArrayBenchmarkType.ArrayGetWithVar, SpanVsArrayBenchmarkType.SpanGet,
SpanVsArrayBenchmarkType.ArraySetWithField, SpanVsArrayBenchmarkType.ArraySetWithVar, SpanVsArrayBenchmarkType.SpanSet SpanVsArrayBenchmarkType.ArraySetWithField, SpanVsArrayBenchmarkType.ArraySetWithVar, SpanVsArrayBenchmarkType.SpanSet
)] )]
public override SpanVsArrayBenchmarkType DataType { get => base.DataType; set => base.DataType = value; } public override SpanVsArrayBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
[Params(SpanVsArrayBenchmarkType.ArrayGetWithField)] [Params(SpanVsArrayBenchmarkType.ArrayGetWithField)]
// [Params(SpanVsArrayBenchmarkType.ArraySetWithField)] // [Params(SpanVsArrayBenchmarkType.ArraySetWithField)]
public override SpanVsArrayBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; } public override SpanVsArrayBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
protected override Action GetTestMethod(SpanVsArrayBenchmarkType benchmarkType) => benchmarkType switch protected override Action GetTestMethod(SpanVsArrayBenchmarkType benchmarkType) => benchmarkType switch
{ {
SpanVsArrayBenchmarkType.ArrayGetWithVar => ArrayGetWithVar, SpanVsArrayBenchmarkType.ArrayGetWithVar => ArrayGetWithVar,
SpanVsArrayBenchmarkType.ArrayGetWithField => ArrayGetWithField, SpanVsArrayBenchmarkType.ArrayGetWithField => ArrayGetWithField,
SpanVsArrayBenchmarkType.SpanGet => SpanGet, SpanVsArrayBenchmarkType.SpanGet => SpanGet,
SpanVsArrayBenchmarkType.ArraySetWithVar => ArraySetWithVar, SpanVsArrayBenchmarkType.ArraySetWithVar => ArraySetWithVar,
SpanVsArrayBenchmarkType.ArraySetWithField => ArraySetWithField, SpanVsArrayBenchmarkType.ArraySetWithField => ArraySetWithField,
SpanVsArrayBenchmarkType.SpanSet => SpanSet, SpanVsArrayBenchmarkType.SpanSet => SpanSet,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType), _ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
}; };
public void ArrayGetWithVar() public void ArrayGetWithVar()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (long i = 0; i < dataCount; i++) for (long i = 0; i < dataCount; i++)
{ {
DoNothing(data[i]); DoNothing(data[i]);
} }
} }
public void ArrayGetWithField() public void ArrayGetWithField()
{ {
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (long i = 0; i < dataCount; i++) for (long i = 0; i < dataCount; i++)
{ {
DoNothing(_testObjects![i]); DoNothing(_testObjects![i]);
} }
} }
public void SpanGet() public void SpanGet()
{ {
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
Span<long> data = new(_testObjects, 0, dataCount); Span<long> data = new(_testObjects, 0, dataCount);
for (int i = 0; i < dataCount; i++) for (int i = 0; i < dataCount; i++)
{ {
DoNothing(data[i]); DoNothing(data[i]);
} }
} }
public void ArraySetWithVar() public void ArraySetWithVar()
{ {
var data = TestObjects; var data = TestObjects;
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (long i = 0; i < dataCount; i++) for (long i = 0; i < dataCount; i++)
{ {
data[i] = i; data[i] = i;
} }
} }
public void ArraySetWithField() public void ArraySetWithField()
{ {
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
for (long i = 0; i < dataCount; i++) for (long i = 0; i < dataCount; i++)
{ {
_testObjects![i] = i; _testObjects![i] = i;
} }
} }
public void SpanSet() public void SpanSet()
{ {
var dataCount = TestObjectCount; var dataCount = TestObjectCount;
Span<long> data = new(_testObjects, 0, dataCount); Span<long> data = new(_testObjects, 0, dataCount);
for (int i = 0; i < dataCount; i++) for (int i = 0; i < dataCount; i++)
{ {
data[i] = i; data[i] = i;
} }
} }
} }
} }
File diff suppressed because it is too large. Load diff
@@ -1,63 +1,63 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading.Tasks.Sources; using System.Threading.Tasks.Sources;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Benchmarks.POC namespace Recyclable.Collections.Benchmarks.POC
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
public class TaskRunVsTaskFactoryStartNewBenchmarks public class TaskRunVsTaskFactoryStartNewBenchmarks
{ {
public int NumberOfTasks = 100; public int NumberOfTasks = 100;
private static readonly TaskFactory TaskPool = new(); private static readonly TaskFactory TaskPool = new();
[Benchmark] [Benchmark]
public void OwnTaskFactory() public void OwnTaskFactory()
{ {
for (var i = 0; i < NumberOfTasks; i++) for (var i = 0; i < NumberOfTasks; i++)
{ {
_ = TaskPool.StartNew(() => {}); _ = TaskPool.StartNew(() => {});
} }
} }
[Benchmark] [Benchmark]
public void TaskRun() public void TaskRun()
{ {
for (var i = 0; i < NumberOfTasks; i++) for (var i = 0; i < NumberOfTasks; i++)
{ {
_ = Task.Run(() => { }); _ = Task.Run(() => { });
} }
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void TaskFactoryStartNew() public void TaskFactoryStartNew()
{ {
for (var i = 0; i < NumberOfTasks; i++) for (var i = 0; i < NumberOfTasks; i++)
{ {
_ = Task.Factory.StartNew(() => { }); _ = Task.Factory.StartNew(() => { });
} }
} }
[Benchmark] [Benchmark]
public void TaskConstructor() public void TaskConstructor()
{ {
for (var i = 0; i < NumberOfTasks; i++) for (var i = 0; i < NumberOfTasks; i++)
{ {
new Task(() => { }).Start(); new Task(() => { }).Start();
} }
} }
[Benchmark] [Benchmark]
public void TaskConstructorWithKnownScheduler() public void TaskConstructorWithKnownScheduler()
{ {
TaskScheduler scheduler = TaskScheduler.Current; TaskScheduler scheduler = TaskScheduler.Current;
for (var i = 0; i < NumberOfTasks; i++) for (var i = 0; i < NumberOfTasks; i++)
{ {
new Task(() => { }).Start(scheduler); new Task(() => { }).Start(scheduler);
} }
} }
} }
} }
+65 -65
View File
@@ -1,65 +1,65 @@
using BenchmarkDotNet.Configs; using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running; using BenchmarkDotNet.Running;
using Recyclable.Collections.Benchmarks.POC; using Recyclable.Collections.Benchmarks.POC;
#pragma warning disable RCS1036, RCS1213, IDE0051 #pragma warning disable RCS1036, RCS1213, IDE0051
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public static class Program public static class Program
{ {
static IConfig BenchmarkConfig { get; } = ManualConfig.Create(DefaultConfig.Instance) static IConfig BenchmarkConfig { get; } = ManualConfig.Create(DefaultConfig.Instance)
.WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary); .WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary);
static void RunAssemblyBenchmarks() static void RunAssemblyBenchmarks()
{ {
_ = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(new[] { "-f*" }); _ = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(new[] { "-f*" });
} }
private static void RunRecyclableCollectionsBenchmarks() private static void RunRecyclableCollectionsBenchmarks()
{ {
_ = BenchmarkRunner.Run<RecyclableCollectionsBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<RecyclableCollectionsBenchmarks>(BenchmarkConfig);
} }
static void RunSelectedBenchmarks() static void RunSelectedBenchmarks()
{ {
// **************** // ****************
// *** Template *** // *** Template ***
// **************** // ****************
var benchmark = new RecyclableCollectionsBenchmarks(); var benchmark = new RecyclableCollectionsBenchmarks();
benchmark.Setup(); benchmark.Setup();
foreach (var _ in Enumerable.Range(1, 1000)) foreach (var _ in Enumerable.Range(1, 1000))
{ {
// benchmark.RecyclableList_Create_WithCapacity(); // benchmark.RecyclableList_Create_WithCapacity();
} }
benchmark.Cleanup(); benchmark.Cleanup();
} }
static void RunPocBenchmarks() static void RunPocBenchmarks()
{ {
_ = BenchmarkRunner.Run<BoolOrComparePocBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<BoolOrComparePocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<DelegateVsComparerPocBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<DelegateVsComparerPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<ModuloPocBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<ModuloPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<TaskRunVsTaskFactoryStartNewBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<TaskRunVsTaskFactoryStartNewBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<RefVsInstanceMemberPocBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<RefVsInstanceMemberPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<RoundBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<RoundBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<SpanVsArrayBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<SpanVsArrayBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<LessOperatorVsAndOperatorBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<LessOperatorVsAndOperatorBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<EqualVsContainsInConsecutiveOrderBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<EqualVsContainsInConsecutiveOrderBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<RoundUpToPowerOf2vsOtherBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<RoundUpToPowerOf2vsOtherBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<GreaterOperatorVsDoesntEqualOperatorBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<GreaterOperatorVsDoesntEqualOperatorBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<SpanConstructorVsTypecastingPocBenchmarks>(BenchmarkConfig); _ = BenchmarkRunner.Run<SpanConstructorVsTypecastingPocBenchmarks>(BenchmarkConfig);
// ArraySizeLimitPocBenchmarks>(BenchmarkConfig); // ArraySizeLimitPocBenchmarks>(BenchmarkConfig);
} }
static void Main() static void Main()
{ {
RunRecyclableCollectionsBenchmarks(); RunRecyclableCollectionsBenchmarks();
// RunPocBenchmarks(); // RunPocBenchmarks();
// RunSelectedBenchmarks(); // RunSelectedBenchmarks();
// RunAssemblyBenchmarks(); // RunAssemblyBenchmarks();
} }
} }
} }
@@ -1,12 +1,12 @@
{ {
"profiles": { "profiles": {
"WSL": { "WSL": {
"commandName": "WSL2", "commandName": "WSL2",
"distributionName": "" "distributionName": ""
}, },
".Net": { ".Net": {
"commandName": "Project", "commandName": "Project",
"nativeDebugging": true "nativeDebugging": true
} }
} }
} }
@@ -1,22 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<StartupObject>Recyclable.Collections.Benchmarks.Program</StartupObject> <StartupObject>Recyclable.Collections.Benchmarks.Program</StartupObject>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" /> <PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
<PackageReference Include="Collections.Pooled" Version="1.0.82" /> <PackageReference Include="Collections.Pooled" Version="1.0.82" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Recyclable.CollectionsTests\Recyclable.CollectionsTests.csproj" /> <ProjectReference Include="..\Recyclable.CollectionsTests\Recyclable.CollectionsTests.csproj" />
<ProjectReference Include="..\Recyclable.Collections\Recyclable.Collections.csproj" /> <ProjectReference Include="..\Recyclable.Collections\Recyclable.Collections.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -1,340 +1,340 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Collections.Pooled; using Collections.Pooled;
using Recyclable.Collections.Benchmarks.Core; using Recyclable.Collections.Benchmarks.Core;
using Recyclable.Collections.Benchmarks.POC; using Recyclable.Collections.Benchmarks.POC;
namespace Recyclable.Collections.Benchmarks namespace Recyclable.Collections.Benchmarks
{ {
public enum RecyclableCollectionsBenchmarkSource public enum RecyclableCollectionsBenchmarkSource
{ {
Unknown, Unknown,
Array, Array,
List, List,
PooledList, PooledList,
RecyclableList, RecyclableList,
RecyclableLongList, RecyclableLongList,
} }
[MemoryDiagnoser] [MemoryDiagnoser]
public class RecyclableBenchmarkBase<TBenchmarkType> : PocBenchmarkBase<TBenchmarkType> public class RecyclableBenchmarkBase<TBenchmarkType> : PocBenchmarkBase<TBenchmarkType>
{ {
protected long[]? _testObjects; protected long[]? _testObjects;
protected long[] TestObjects => _testObjects ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); protected long[] TestObjects => _testObjects ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
protected List<long>? _testObjectsAsList; protected List<long>? _testObjectsAsList;
protected List<long> TestObjectsAsList => _testObjectsAsList ?? throw new NullReferenceException("Something went wrong and the field is not initialized"); protected List<long> TestObjectsAsList => _testObjectsAsList ?? throw new NullReferenceException("Something went wrong and the field is not initialized");
protected PooledList<long>? _testObjectsAsPooledList; protected PooledList<long>? _testObjectsAsPooledList;
public PooledList<long> TestObjectsAsPooledList => _testObjectsAsPooledList ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); public PooledList<long> TestObjectsAsPooledList => _testObjectsAsPooledList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
protected RecyclableList<long>? _testObjectsAsRecyclableList; protected RecyclableList<long>? _testObjectsAsRecyclableList;
protected RecyclableList<long> TestObjectsAsRecyclableList => _testObjectsAsRecyclableList ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); protected RecyclableList<long> TestObjectsAsRecyclableList => _testObjectsAsRecyclableList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
protected RecyclableLongList<long>? _testObjectsAsRecyclableLongList; protected RecyclableLongList<long>? _testObjectsAsRecyclableLongList;
protected RecyclableLongList<long> TestObjectsAsRecyclableLongList => _testObjectsAsRecyclableLongList ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); protected RecyclableLongList<long> TestObjectsAsRecyclableLongList => _testObjectsAsRecyclableLongList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
protected IEnumerable<long> TestObjectsAsIEnumerable protected IEnumerable<long> TestObjectsAsIEnumerable
{ {
get get
{ {
for (long i = 0; i < TestObjectCount; i++) for (long i = 0; i < TestObjectCount; i++)
{ {
yield return i; yield return i;
} }
} }
} }
public override void Setup() public override void Setup()
{ {
PrepareData(RecyclableCollectionsBenchmarkSource.Array); PrepareData(RecyclableCollectionsBenchmarkSource.Array);
base.Setup(); base.Setup();
} }
protected override void PrepareData<T>(T benchmarkType) protected override void PrepareData<T>(T benchmarkType)
{ {
Console.WriteLine($"******* PREPARING DATA FOR {benchmarkType} *******"); Console.WriteLine($"******* PREPARING DATA FOR {benchmarkType} *******");
switch (benchmarkType) switch (benchmarkType)
{ {
case RecyclableCollectionsBenchmarkSource.Unknown: case RecyclableCollectionsBenchmarkSource.Unknown:
break; break;
case RecyclableCollectionsBenchmarkSource.Array: case RecyclableCollectionsBenchmarkSource.Array:
_testObjects ??= DataGenerator.EnumerateTestObjects(TestObjectCount); _testObjects ??= DataGenerator.EnumerateTestObjects(TestObjectCount);
break; break;
case RecyclableCollectionsBenchmarkSource.List: case RecyclableCollectionsBenchmarkSource.List:
_testObjectsAsList ??= TestObjects.ToList(); _testObjectsAsList ??= TestObjects.ToList();
break; break;
case RecyclableCollectionsBenchmarkSource.PooledList: case RecyclableCollectionsBenchmarkSource.PooledList:
_testObjectsAsPooledList ??= new PooledList<long>(TestObjects, ClearMode.Auto); _testObjectsAsPooledList ??= new PooledList<long>(TestObjects, ClearMode.Auto);
break; break;
case RecyclableCollectionsBenchmarkSource.RecyclableList: case RecyclableCollectionsBenchmarkSource.RecyclableList:
_testObjectsAsRecyclableList ??= new(TestObjects, initialCapacity: TestObjectCount); _testObjectsAsRecyclableList ??= new(TestObjects, initialCapacity: TestObjectCount);
break; break;
case RecyclableCollectionsBenchmarkSource.RecyclableLongList: case RecyclableCollectionsBenchmarkSource.RecyclableLongList:
_testObjectsAsRecyclableLongList ??= new(TestObjects, BlockSize, initialCapacity: base.TestObjectCount); _testObjectsAsRecyclableLongList ??= new(TestObjects, BlockSize, initialCapacity: base.TestObjectCount);
break; break;
default: default:
throw CreateUnknownBenchmarkTypeException(benchmarkType); throw CreateUnknownBenchmarkTypeException(benchmarkType);
} }
base.PrepareData(benchmarkType); base.PrepareData(benchmarkType);
} }
public override void Cleanup() public override void Cleanup()
{ {
Console.WriteLine("******* GLOBAL CLEANUP *******"); Console.WriteLine("******* GLOBAL CLEANUP *******");
// Enable this if item type is changed to reference type // Enable this if item type is changed to reference type
//if (_testObjects != null) //if (_testObjects != null)
//{ //{
// Array.Fill(_testObjects, 0); // Array.Fill(_testObjects, 0);
//} //}
_testObjects = default; _testObjects = default;
_testObjectsAsList = default; _testObjectsAsList = default;
_testObjectsAsPooledList?.Dispose(); _testObjectsAsPooledList?.Dispose();
_testObjectsAsPooledList = default; _testObjectsAsPooledList = default;
_testObjectsAsRecyclableList?.Dispose(); _testObjectsAsRecyclableList?.Dispose();
_testObjectsAsRecyclableList = default; _testObjectsAsRecyclableList = default;
_testObjectsAsRecyclableLongList?.Dispose(); _testObjectsAsRecyclableLongList?.Dispose();
_testObjectsAsRecyclableLongList = default; _testObjectsAsRecyclableLongList = default;
base.Cleanup(); base.Cleanup();
} }
} }
//[MemoryDiagnoser] //[MemoryDiagnoser]
//public class ListVsLinkedListBenchmarks //public class ListVsLinkedListBenchmarks
//{ //{
// [Benchmark] // [Benchmark]
// public void RecyclableList() // public void RecyclableList()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// using var list = new RecyclableList<object>(initialCapacity: dataCount); // using var list = new RecyclableList<object>(initialCapacity: dataCount);
// for (long i = 0; i < dataCount; i++) // for (long i = 0; i < dataCount; i++)
// { // {
// list.Add(data[i]); // list.Add(data[i]);
// } // }
// for (long i = 0; i < list.LongCount; i++) // for (long i = 0; i < list.LongCount; i++)
// { // {
// DoNothing(list[i]); // DoNothing(list[i]);
// } // }
// list.Clear(); // list.Clear();
// } // }
// [Benchmark(Baseline = true)] // [Benchmark(Baseline = true)]
// public void RecyclableLongList() // public void RecyclableLongList()
// { // {
// var data = TestObjects; // var data = TestObjects;
// long dataCount = TestObjectCount; // long dataCount = TestObjectCount;
// using var list = new RecyclableLongList<object>(BlockSize, totalItemsCount: dataCount); // using var list = new RecyclableLongList<object>(BlockSize, totalItemsCount: dataCount);
// for (long i = 0; i < dataCount; i++) // for (long i = 0; i < dataCount; i++)
// { // {
// list.Add(data[i]); // list.Add(data[i]);
// } // }
// for (long i = 0; i < list.LongCount; i++) // for (long i = 0; i < list.LongCount; i++)
// { // {
// DoNothing(list[i]); // DoNothing(list[i]);
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void List() // public void List()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new List<object>(capacity: dataCount); // var list = new List<object>(capacity: dataCount);
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// list.Add(data[i]); // list.Add(data[i]);
// } // }
// for (var i = 0; i < list.Count; i++) // for (var i = 0; i < list.Count; i++)
// { // {
// DoNothing(list[i]); // DoNothing(list[i]);
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void LinkedList() // public void LinkedList()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new LinkedList<object>(); // var list = new LinkedList<object>();
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// _ = list.AddLast(data[i]); // _ = list.AddLast(data[i]);
// } // }
// foreach (var node in list) // foreach (var node in list)
// { // {
// DoNothing(node); // DoNothing(node);
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void RecyclableQueue() // public void RecyclableQueue()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new RecyclableQueue<object>(BlockSize); // var list = new RecyclableQueue<object>(BlockSize);
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// list.Enqueue(data[i]); // list.Enqueue(data[i]);
// } // }
// while (list.LongCount > 0) // while (list.LongCount > 0)
// { // {
// DoNothing(list.Dequeue()); // DoNothing(list.Dequeue());
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void Queue() // public void Queue()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new Queue<object>(capacity: dataCount); // var list = new Queue<object>(capacity: dataCount);
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// list.Enqueue(data[i]); // list.Enqueue(data[i]);
// } // }
// while (list.Count > 0) // while (list.Count > 0)
// { // {
// DoNothing(list.Dequeue()); // DoNothing(list.Dequeue());
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void PriorityQueue() // public void PriorityQueue()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new PriorityQueue<object, long>(initialCapacity: dataCount); // var list = new PriorityQueue<object, long>(initialCapacity: dataCount);
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// list.Enqueue(data[i], i); // list.Enqueue(data[i], i);
// } // }
// while (list.Count > 0) // while (list.Count > 0)
// { // {
// DoNothing(list.Dequeue()); // DoNothing(list.Dequeue());
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void RecyclableSortedList_InUpdateMode() // public void RecyclableSortedList_InUpdateMode()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new RecyclableSortedList<long, object>(BlockSize); // var list = new RecyclableSortedList<long, object>(BlockSize);
// list.BeginUpdate(); // list.BeginUpdate();
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// list.Add(i, data[i]); // list.Add(i, data[i]);
// } // }
// list.EndUpdate(); // list.EndUpdate();
// for (var i = 0; i < list.LongCount; i++) // for (var i = 0; i < list.LongCount; i++)
// { // {
// DoNothing(list[i]); // DoNothing(list[i]);
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void RecyclableSortedList() // public void RecyclableSortedList()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new RecyclableSortedList<long, object>(BlockSize); // var list = new RecyclableSortedList<long, object>(BlockSize);
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// list.Add(i, data[i]); // list.Add(i, data[i]);
// } // }
// for (var i = 0; i < list.LongCount; i++) // for (var i = 0; i < list.LongCount; i++)
// { // {
// DoNothing(list[i]); // DoNothing(list[i]);
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void SortedList() // public void SortedList()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new SortedList<long, object>(); // var list = new SortedList<long, object>();
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// list.Add(i, data[i]); // list.Add(i, data[i]);
// } // }
// for (var i = 0; i < list.Count; i++) // for (var i = 0; i < list.Count; i++)
// { // {
// DoNothing(list[i]); // DoNothing(list[i]);
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void RecyclableStack() // public void RecyclableStack()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new RecyclableStack<object>(BlockSize); // var list = new RecyclableStack<object>(BlockSize);
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// list.Push(data[i]); // list.Push(data[i]);
// } // }
// while (list.LongCount > 0) // while (list.LongCount > 0)
// { // {
// DoNothing(list.Pop()); // DoNothing(list.Pop());
// } // }
// list.Clear(); // list.Clear();
// } // }
// //[Benchmark] // //[Benchmark]
// public void Stack() // public void Stack()
// { // {
// var data = TestObjects; // var data = TestObjects;
// var dataCount = TestObjectCount; // var dataCount = TestObjectCount;
// var list = new Stack<object>(); // var list = new Stack<object>();
// for (var i = 0; i < dataCount; i++) // for (var i = 0; i < dataCount; i++)
// { // {
// list.Push(data[i]); // list.Push(data[i]);
// } // }
// while (list.Count > 0) // while (list.Count > 0)
// { // {
// DoNothing(list.Pop()); // DoNothing(list.Pop());
// } // }
// list.Clear(); // list.Clear();
// } // }
//} //}
} }
File diff suppressed because it is too large. Load diff
+51 -51
View File
@@ -1,51 +1,51 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214 VisualStudioVersion = 17.4.33205.214
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections", "Recyclable.Collections\Recyclable.Collections.csproj", "{7EC0F96D-FB4C-418A-9172-612CC5BCE705}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections", "Recyclable.Collections\Recyclable.Collections.csproj", "{7EC0F96D-FB4C-418A-9172-612CC5BCE705}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.CollectionsTests", "Recyclable.CollectionsTests\Recyclable.CollectionsTests.csproj", "{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.CollectionsTests", "Recyclable.CollectionsTests\Recyclable.CollectionsTests.csproj", "{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Benchmarks", "Recyclable.Collections.Benchmarks\Recyclable.Collections.Benchmarks.csproj", "{CDC0CF78-FC11-43E3-9059-3261DA3639D1}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Benchmarks", "Recyclable.Collections.Benchmarks\Recyclable.Collections.Benchmarks.csproj", "{CDC0CF78-FC11-43E3-9059-3261DA3639D1}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64 Release|x64 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|Any CPU.Build.0 = Debug|Any CPU {7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|x64.ActiveCfg = Debug|x64 {7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|x64.ActiveCfg = Debug|x64
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|x64.Build.0 = Debug|x64 {7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|x64.Build.0 = Debug|x64
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|Any CPU.ActiveCfg = Release|Any CPU {7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|Any CPU.Build.0 = Release|Any CPU {7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|Any CPU.Build.0 = Release|Any CPU
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|x64.ActiveCfg = Release|x64 {7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|x64.ActiveCfg = Release|x64
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|x64.Build.0 = Release|x64 {7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|x64.Build.0 = Release|x64
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|Any CPU.Build.0 = Debug|Any CPU {2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|x64.ActiveCfg = Debug|x64 {2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|x64.ActiveCfg = Debug|x64
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|x64.Build.0 = Debug|x64 {2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|x64.Build.0 = Debug|x64
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|Any CPU.Build.0 = Release|Any CPU {2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|Any CPU.Build.0 = Release|Any CPU
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|x64.ActiveCfg = Release|x64 {2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|x64.ActiveCfg = Release|x64
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|x64.Build.0 = Release|x64 {2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|x64.Build.0 = Release|x64
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|Any CPU.Build.0 = Debug|Any CPU {CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|x64.ActiveCfg = Debug|x64 {CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|x64.ActiveCfg = Debug|x64
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|x64.Build.0 = Debug|x64 {CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|x64.Build.0 = Debug|x64
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|Any CPU.ActiveCfg = Release|Any CPU {CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|Any CPU.Build.0 = Release|Any CPU {CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|Any CPU.Build.0 = Release|Any CPU
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|x64.ActiveCfg = Release|x64 {CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|x64.ActiveCfg = Release|x64
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|x64.Build.0 = Release|x64 {CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|x64.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2C612762-D1AF-4E88-9427-B4750FBECB1A} SolutionGuid = {2C612762-D1AF-4E88-9427-B4750FBECB1A}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal
+9 -9
View File
@@ -1,9 +1,9 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
public static class CompareResult public static class CompareResult
{ {
public const int XLessThanY = -1; public const int XLessThanY = -1;
public const int XEqualY = 0; public const int XEqualY = 0;
public const int XGreaterThenY = 1; public const int XGreaterThenY = 1;
} }
} }
@@ -1,32 +1,32 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
public readonly struct ComparisonToComparerAdapter<T> : IComparer<T> public readonly struct ComparisonToComparerAdapter<T> : IComparer<T>
{ {
private readonly Comparison<T> _comparison; private readonly Comparison<T> _comparison;
public ComparisonToComparerAdapter(Comparison<T> comparison) public ComparisonToComparerAdapter(Comparison<T> comparison)
{ {
_comparison = comparison; _comparison = comparison;
} }
public int Compare(T? x, T? y) public int Compare(T? x, T? y)
{ {
if (x == null && y == null) if (x == null && y == null)
{ {
return CompareResult.XEqualY; return CompareResult.XEqualY;
} }
else if (x != null && y == null) else if (x != null && y == null)
{ {
return CompareResult.XGreaterThenY; return CompareResult.XGreaterThenY;
} }
else if (y != null && x == null) else if (y != null && x == null)
{ {
return CompareResult.XLessThanY; return CompareResult.XLessThanY;
} }
else else
{ {
return _comparison(x!, y!); return _comparison(x!, y!);
} }
} }
} }
} }
+12 -12
View File
@@ -1,12 +1,12 @@
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
internal static class ExceptionExtensions internal static class ExceptionExtensions
{ {
public static void CaptureAndRethrow(this Exception ex) public static void CaptureAndRethrow(this Exception ex)
{ {
ExceptionDispatchInfo.Capture(ex).Throw(); ExceptionDispatchInfo.Capture(ex).Throw();
} }
} }
} }
@@ -1,10 +1,10 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
internal class FrequencyTupleComparer<TKey, TValue> : IComparer<(TKey Key, TValue Value)> internal class FrequencyTupleComparer<TKey, TValue> : IComparer<(TKey Key, TValue Value)>
{ {
private static readonly IComparer<TKey> _baseComparer = Comparer<TKey>.Default; private static readonly IComparer<TKey> _baseComparer = Comparer<TKey>.Default;
public int Compare((TKey Key, TValue Value) x, (TKey Key, TValue Value) y) public int Compare((TKey Key, TValue Value) x, (TKey Key, TValue Value) y)
=> _baseComparer.Compare(x.Key, y.Key); => _baseComparer.Compare(x.Key, y.Key);
} }
} }
+8 -8
View File
@@ -1,8 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage // This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project. // attributes that are applied to this project.
// Project-level suppressions either have no target or are given // Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc. // a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE0032:Use auto property", Justification = "<Pending>", Scope = "member", Target = "~F:Recyclable.Collections.RecyclableList`1._count")] [assembly: SuppressMessage("Style", "IDE0032:Use auto property", Justification = "<Pending>", Scope = "member", Target = "~F:Recyclable.Collections.RecyclableList`1._count")]
@@ -1,7 +1,7 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
internal interface IRandomNumberGenerator internal interface IRandomNumberGenerator
{ {
int NextInt32(int startIndex, int endIndex); int NextInt32(int startIndex, int endIndex);
} }
} }
+10 -10
View File
@@ -1,10 +1,10 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
internal interface IRecyclableOwner<T> : IList<T> internal interface IRecyclableOwner<T> : IList<T>
{ {
T this[long index] { get; set; } T this[long index] { get; set; }
int BlockSize { get; } int BlockSize { get; }
long Capacity { get; set; } long Capacity { get; set; }
long LongCount { get; set; } long LongCount { get; set; }
} }
} }
@@ -1,7 +1,7 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
public interface IRecyclableVersionedList<T> : IEnumerable<T> public interface IRecyclableVersionedList<T> : IEnumerable<T>
{ {
new RecyclableList<T>.VersionedEnumerator GetEnumerator(); new RecyclableList<T>.VersionedEnumerator GetEnumerator();
} }
} }
@@ -1,7 +1,7 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
public interface IRecyclableVersionedLongList<T> : IEnumerable<T> public interface IRecyclableVersionedLongList<T> : IEnumerable<T>
{ {
new RecyclableLongList<T>.VersionedEnumerator GetEnumerator(); new RecyclableLongList<T>.VersionedEnumerator GetEnumerator();
} }
} }
+132 -132
View File
@@ -1,132 +1,132 @@
// Ignore Spelling: Linq // Ignore Spelling: Linq
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
// The source code was taken from // The source code was taken from
// https://codereview.stackexchange.com/questions/205407/quicksort-without-recursion. // https://codereview.stackexchange.com/questions/205407/quicksort-without-recursion.
// //
// It was only beautified for .Net 6.0+. // It was only beautified for .Net 6.0+.
namespace Recyclable.Collections.Linq namespace Recyclable.Collections.Linq
{ {
internal static class QuickSortExtensions<T> internal static class QuickSortExtensions<T>
{ {
public static void InsertionSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer) public static void InsertionSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer)
{ {
var left = startIndex; var left = startIndex;
while (left < endIndex) while (left < endIndex)
{ {
int right = left; int right = left;
T? temp = values[++left]; T? temp = values[++left];
while (right >= startIndex && 0 < comparer.Compare(values[right], temp)) while (right >= startIndex && 0 < comparer.Compare(values[right], temp))
{ {
values[right + 1] = values[right--]; values[right + 1] = values[right--];
} }
values[right + 1] = temp; values[right + 1] = temp;
} }
} }
public static void InsertionSort(IList<T> values, IComparer<T> comparer) public static void InsertionSort(IList<T> values, IComparer<T> comparer)
{ {
InsertionSort(values, 0, values.Count - 1, comparer); InsertionSort(values, 0, values.Count - 1, comparer);
} }
public static void InsertionSort(IList<T> values) public static void InsertionSort(IList<T> values)
{ {
InsertionSort(values, Comparer<T>.Default); InsertionSort(values, Comparer<T>.Default);
} }
public static void QuickSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator) public static void QuickSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
{ {
var range = (startIndex, endIndex); var range = (startIndex, endIndex);
var stack = new Stack<(int, int)>(); var stack = new Stack<(int, int)>();
do do
{ {
startIndex = range.startIndex; startIndex = range.startIndex;
endIndex = range.endIndex; endIndex = range.endIndex;
if (endIndex - startIndex + 1 < 31) if (endIndex - startIndex + 1 < 31)
{ {
InsertionSort(values, startIndex, endIndex, comparer); InsertionSort(values, startIndex, endIndex, comparer);
continue; continue;
} }
var pivot = SampleMedian(values, startIndex, endIndex, comparer, randomNumberGenerator); var pivot = SampleMedian(values, startIndex, endIndex, comparer, randomNumberGenerator);
var left = startIndex; var left = startIndex;
var right = endIndex; var right = endIndex;
while (left <= right) while (left <= right)
{ {
while (0 > comparer.Compare(values[left], pivot)) while (0 > comparer.Compare(values[left], pivot))
{ {
left++; left++;
} }
while (0 > comparer.Compare(pivot, values[right])) while (0 > comparer.Compare(pivot, values[right]))
{ {
right--; right--;
} }
if (left <= right) if (left <= right)
{ {
Swap(values, left++, right--); Swap(values, left++, right--);
} }
} }
if (startIndex < right) if (startIndex < right)
{ {
stack.Push((startIndex, right)); stack.Push((startIndex, right));
} }
if (left < endIndex) if (left < endIndex)
{ {
stack.Push((left, endIndex)); stack.Push((left, endIndex));
} }
} }
while (stack.TryPop(out range)); while (stack.TryPop(out range));
} }
public static void QuickSort(IList<T> values, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator) public static void QuickSort(IList<T> values, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
=> QuickSort(values, 0, values.Count - 1, comparer, randomNumberGenerator); => QuickSort(values, 0, values.Count - 1, comparer, randomNumberGenerator);
public static void QuickSort(IList<T> values) public static void QuickSort(IList<T> values)
=> QuickSort(values, Comparer<T>.Default, new SystemRandomNumberGenerator()); => QuickSort(values, Comparer<T>.Default, new SystemRandomNumberGenerator());
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static T SampleMedian(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator) private static T SampleMedian(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
{ {
var left = randomNumberGenerator.NextInt32(startIndex, endIndex); var left = randomNumberGenerator.NextInt32(startIndex, endIndex);
var middle = randomNumberGenerator.NextInt32(startIndex, endIndex); var middle = randomNumberGenerator.NextInt32(startIndex, endIndex);
var right = randomNumberGenerator.NextInt32(startIndex, endIndex); var right = randomNumberGenerator.NextInt32(startIndex, endIndex);
if (0 > comparer.Compare(values[right], values[left])) if (0 > comparer.Compare(values[right], values[left]))
{ {
Swap(values, right, left); Swap(values, right, left);
} }
if (0 > comparer.Compare(values[middle], values[left])) if (0 > comparer.Compare(values[middle], values[left]))
{ {
Swap(values, middle, left); Swap(values, middle, left);
} }
if (0 > comparer.Compare(values[right], values[middle])) if (0 > comparer.Compare(values[right], values[middle]))
{ {
Swap(values, right, middle); Swap(values, right, middle);
} }
return values[middle]; return values[middle];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(IList<T> values, int xIndex, int yIndex) private static void Swap(IList<T> values, int xIndex, int yIndex)
=> (values[yIndex], values[xIndex]) = (values[xIndex], values[yIndex]); => (values[yIndex], values[xIndex]) = (values[xIndex], values[yIndex]);
} }
} }
+35 -35
View File
@@ -1,35 +1,35 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
internal static class MathUtils internal static class MathUtils
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPow2(long value) => (value != 0) && ((value & (value - 1)) == 0); public static bool IsPow2(long value) => (value != 0) && ((value & (value - 1)) == 0);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetPow2Shift(long value) => BitOperations.IsPow2(value) ? 63 - BitOperations.LeadingZeroCount((ulong)value) : -1; public static int GetPow2Shift(long value) => BitOperations.IsPow2(value) ? 63 - BitOperations.LeadingZeroCount((ulong)value) : -1;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long DivMod(long number, int divider, out long remainder) public static long DivMod(long number, int divider, out long remainder)
{ {
if (number >= checked((long)divider << 4)) if (number >= checked((long)divider << 4))
{ {
return Math.DivRem(number, divider, out remainder); return Math.DivRem(number, divider, out remainder);
} }
else else
{ {
int result = 0; int result = 0;
remainder = number; remainder = number;
while (remainder >= divider) while (remainder >= divider)
{ {
remainder -= divider; remainder -= divider;
result++; result++;
} }
return result; return result;
} }
} }
} }
} }
+24 -24
View File
@@ -1,24 +1,24 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
internal sealed class MemoryBucket<T> internal sealed class MemoryBucket<T>
{ {
public MemoryBucket(T[] memory, MemoryBucket<T> nextBucket) public MemoryBucket(T[] memory, MemoryBucket<T> nextBucket)
{ {
_memory = memory; _memory = memory;
_nextBucket = nextBucket; _nextBucket = nextBucket;
} }
public MemoryBucket(T[] memory) public MemoryBucket(T[] memory)
{ {
_memory = memory; _memory = memory;
} }
#pragma warning disable RCS1085 #pragma warning disable RCS1085
private T[] _memory; private T[] _memory;
public T[] Memory { get => _memory; set => _memory = value; } public T[] Memory { get => _memory; set => _memory = value; }
private MemoryBucket<T>? _nextBucket; private MemoryBucket<T>? _nextBucket;
public MemoryBucket<T>? NextBucket { get => _nextBucket; set => _nextBucket = value; } public MemoryBucket<T>? NextBucket { get => _nextBucket; set => _nextBucket = value; }
#pragma warning restore RCS1085 #pragma warning restore RCS1085
} }
} }
+117 -117
View File
@@ -1,117 +1,117 @@
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Recyclable.Collections.Parallel namespace Recyclable.Collections.Parallel
{ {
public sealed class BarrierSlimmer : IDisposable public sealed class BarrierSlimmer : IDisposable
{ {
private static readonly ObjectPool<BarrierSlimmer> _defaultPool = new DefaultObjectPool<BarrierSlimmer>(new DefaultPooledObjectPolicy<BarrierSlimmer>()); private static readonly ObjectPool<BarrierSlimmer> _defaultPool = new DefaultObjectPool<BarrierSlimmer>(new DefaultPooledObjectPolicy<BarrierSlimmer>());
private ObjectPool<BarrierSlimmer>? _assignedPool; private ObjectPool<BarrierSlimmer>? _assignedPool;
private readonly SpinLock _lock = new(false); private readonly SpinLock _lock = new(false);
public bool HasParticipants; public bool HasParticipants;
public int Participants; public int Participants;
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public void AddParticipant() public void AddParticipant()
{ {
bool isLocked = false; bool isLocked = false;
_lock.Enter(ref isLocked); _lock.Enter(ref isLocked);
Participants++; Participants++;
if (Participants == 1) if (Participants == 1)
{ {
HasParticipants = true; HasParticipants = true;
} }
if (isLocked) if (isLocked)
{ {
_lock.Exit(false); _lock.Exit(false);
} }
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public void RemoveParticipant() public void RemoveParticipant()
{ {
if (!HasParticipants) if (!HasParticipants)
{ {
ThrowNoMoreParticipants(); ThrowNoMoreParticipants();
} }
bool isLocked = false; bool isLocked = false;
_lock.Enter(ref isLocked); _lock.Enter(ref isLocked);
Participants--; Participants--;
if (Participants == 0) if (Participants == 0)
{ {
HasParticipants = false; HasParticipants = false;
} }
if (isLocked) if (isLocked)
{ {
_lock.Exit(false); _lock.Exit(false);
} }
} }
private static void ThrowNoMoreParticipants() private static void ThrowNoMoreParticipants()
{ {
throw new InvalidOperationException("There are no more participants. The operation is not allowed"); throw new InvalidOperationException("There are no more participants. The operation is not allowed");
} }
public BarrierSlimmer() public BarrierSlimmer()
{ {
_lock = new(false); _lock = new(false);
} }
public BarrierSlimmer(int participants) public BarrierSlimmer(int participants)
{ {
_lock = new(false); _lock = new(false);
Participants = participants; Participants = participants;
HasParticipants = participants > 0; HasParticipants = participants > 0;
} }
public static BarrierSlimmer Create(int participants) public static BarrierSlimmer Create(int participants)
{ {
var result = _defaultPool.Get(); var result = _defaultPool.Get();
result.Participants = participants; result.Participants = participants;
result.HasParticipants = participants > 0; result.HasParticipants = participants > 0;
return result; return result;
} }
public void Dispose() public void Dispose()
{ {
var assignedPool = _assignedPool; var assignedPool = _assignedPool;
_assignedPool = null; _assignedPool = null;
assignedPool?.Return(this); assignedPool?.Return(this);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public void SignalAndWait() public void SignalAndWait()
{ {
RemoveParticipant(); RemoveParticipant();
//if (!HasParticipants) //if (!HasParticipants)
//{ //{
// return; // return;
//} //}
//if (!HasParticipants) //if (!HasParticipants)
//{ //{
// return; // return;
//} //}
//if (!HasParticipants) //if (!HasParticipants)
//{ //{
// return; // return;
//} //}
// SpinWait.SpinUntil(() => HasParticipants); // SpinWait.SpinUntil(() => HasParticipants);
var waitSpinner = new SpinWait(); var waitSpinner = new SpinWait();
while (HasParticipants) while (HasParticipants)
{ {
waitSpinner.SpinOnce(0); waitSpinner.SpinOnce(0);
} }
} }
} }
} }
@@ -1,111 +1,111 @@
namespace Recyclable.Collections.Parallel namespace Recyclable.Collections.Parallel
{ {
public sealed class IndexOfSynchronizationContext public sealed class IndexOfSynchronizationContext
{ {
private SpinWait _participantsSpinWait = new(); private SpinWait _participantsSpinWait = new();
private SpinLockSlimmer _participantsSpinLock = new(); private SpinLockSlimmer _participantsSpinLock = new();
private SpinLockSlimmer _indexOfSpinLock = new(); private SpinLockSlimmer _indexOfSpinLock = new();
internal bool _isItemFound; internal bool _isItemFound;
internal int _participants; internal int _participants;
internal bool _returned; internal bool _returned;
public void AddParticipant() public void AddParticipant()
{ {
_participantsSpinLock.Enter(); _participantsSpinLock.Enter();
_participants++; _participants++;
_participantsSpinLock.Exit(); _participantsSpinLock.Exit();
} }
public void AddParticipants(int newParticipants) public void AddParticipants(int newParticipants)
{ {
_participantsSpinLock.Enter(); _participantsSpinLock.Enter();
_participants += newParticipants; _participants += newParticipants;
_participantsSpinLock.Exit(); _participantsSpinLock.Exit();
} }
public void Lock() public void Lock()
{ {
_indexOfSpinLock.Enter(); _indexOfSpinLock.Enter();
} }
public void RemoveParticipants(int participantsToRemove) public void RemoveParticipants(int participantsToRemove)
{ {
if (_participants < participantsToRemove) if (_participants < participantsToRemove)
{ {
ThrowNotEnoughParticipants(); ThrowNotEnoughParticipants();
} }
_participantsSpinLock.Enter(); _participantsSpinLock.Enter();
_participants -= participantsToRemove; _participants -= participantsToRemove;
_participantsSpinLock.Exit(); _participantsSpinLock.Exit();
} }
public void RemoveParticipant() public void RemoveParticipant()
{ {
if (_participants == 0) if (_participants == 0)
{ {
ThrowNoMoreParticipants(); ThrowNoMoreParticipants();
} }
_participantsSpinLock.Enter(); _participantsSpinLock.Enter();
_participants--; _participants--;
_participantsSpinLock.Exit(); _participantsSpinLock.Exit();
} }
public void SignalAndWait() public void SignalAndWait()
{ {
if (_participants == 0) if (_participants == 0)
{ {
ThrowNoMoreParticipants(); ThrowNoMoreParticipants();
} }
_participantsSpinLock.Enter(); _participantsSpinLock.Enter();
_participants--; _participants--;
_participantsSpinLock.Exit(); _participantsSpinLock.Exit();
while (_participants > 0) while (_participants > 0)
{ {
_participantsSpinWait.SpinOnce(); _participantsSpinWait.SpinOnce();
} }
} }
public void Unlock() public void Unlock()
{ {
_indexOfSpinLock.Exit(); _indexOfSpinLock.Exit();
} }
private static void ThrowNotEnoughParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are not enough participants."); private static void ThrowNotEnoughParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are not enough participants.");
private static void ThrowNoMoreParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are no more participants."); private static void ThrowNoMoreParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are no more participants.");
public long FoundBlockIndex; public long FoundBlockIndex;
public long FoundItemIndex; public long FoundItemIndex;
public Exception? Exception; public Exception? Exception;
public int Participants => _participants; public int Participants => _participants;
public IndexOfSynchronizationContext() public IndexOfSynchronizationContext()
{ {
} }
public IndexOfSynchronizationContext(int participantCount) public IndexOfSynchronizationContext(int participantCount)
{ {
_participants = participantCount; _participants = participantCount;
} }
public void SetItemFound() public void SetItemFound()
{ {
_isItemFound = true; _isItemFound = true;
} }
public bool IsItemFound => _isItemFound; public bool IsItemFound => _isItemFound;
public void Dispose() public void Dispose()
{ {
if (!_returned) if (!_returned)
{ {
_returned = true; _returned = true;
IndexOfSynchronizationContextPool.Shared.Return(this); IndexOfSynchronizationContextPool.Shared.Return(this);
} }
} }
} }
} }
@@ -1,50 +1,50 @@
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Parallel namespace Recyclable.Collections.Parallel
{ {
public static class IndexOfSynchronizationContextPool public static class IndexOfSynchronizationContextPool
{ {
private static readonly SpinLock _updateLock = new(false); private static readonly SpinLock _updateLock = new(false);
public static readonly ObjectPool<IndexOfSynchronizationContext> Shared = new DefaultObjectPool<IndexOfSynchronizationContext>(new DefaultPooledObjectPolicy<IndexOfSynchronizationContext>()); public static readonly ObjectPool<IndexOfSynchronizationContext> Shared = new DefaultObjectPool<IndexOfSynchronizationContext>(new DefaultPooledObjectPolicy<IndexOfSynchronizationContext>());
public static IndexOfSynchronizationContext GetWithOneParticipant() public static IndexOfSynchronizationContext GetWithOneParticipant()
{ {
bool lockTaken = false; bool lockTaken = false;
_updateLock.Enter(ref lockTaken); _updateLock.Enter(ref lockTaken);
var context = Shared.Get(); var context = Shared.Get();
if (lockTaken) if (lockTaken)
{ {
_updateLock.Exit(false); _updateLock.Exit(false);
} }
if (context._isItemFound) if (context._isItemFound)
{ {
context._isItemFound = false; context._isItemFound = false;
} }
switch (context._participants) switch (context._participants)
{ {
case > 1: case > 1:
context.RemoveParticipants(context._participants - 1); context.RemoveParticipants(context._participants - 1);
break; break;
case 0: case 0:
context.AddParticipant(); context.AddParticipant();
break; break;
} }
if (context.Exception != null) if (context.Exception != null)
{ {
context.Exception = null; context.Exception = null;
} }
if (context.FoundItemIndex >= 0) if (context.FoundItemIndex >= 0)
{ {
context.FoundItemIndex = RecyclableDefaults.ItemNotFoundIndexLong; context.FoundItemIndex = RecyclableDefaults.ItemNotFoundIndexLong;
} }
context._returned = false; context._returned = false;
return context; return context;
} }
} }
} }
@@ -1,38 +1,38 @@
namespace Recyclable.Collections.Parallel namespace Recyclable.Collections.Parallel
{ {
public sealed class ManualResetEventSlimmer public sealed class ManualResetEventSlimmer
{ {
internal bool _isSet; internal bool _isSet;
public bool IsSet => _isSet; public bool IsSet => _isSet;
internal bool _returned; internal bool _returned;
public ManualResetEventSlimmer() public ManualResetEventSlimmer()
{ {
} }
public ManualResetEventSlimmer(bool initialState) public ManualResetEventSlimmer(bool initialState)
{ {
_isSet = initialState; _isSet = initialState;
} }
public void Set() public void Set()
{ {
_isSet = true; _isSet = true;
} }
public void Reset() public void Reset()
{ {
_isSet = false; _isSet = false;
} }
public void Dispose() public void Dispose()
{ {
if (!_returned) if (!_returned)
{ {
_returned = true; _returned = true;
ManualResetEventSlimmerPool.Shared.Return(this); ManualResetEventSlimmerPool.Shared.Return(this);
} }
} }
} }
} }
@@ -1,29 +1,29 @@
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Parallel namespace Recyclable.Collections.Parallel
{ {
public static class ManualResetEventSlimmerPool public static class ManualResetEventSlimmerPool
{ {
public static readonly SpinLock _updateLock = new(false); public static readonly SpinLock _updateLock = new(false);
public static readonly ObjectPool<ManualResetEventSlimmer> Shared = new DefaultObjectPool<ManualResetEventSlimmer>(new DefaultPooledObjectPolicy<ManualResetEventSlimmer>()); public static readonly ObjectPool<ManualResetEventSlimmer> Shared = new DefaultObjectPool<ManualResetEventSlimmer>(new DefaultPooledObjectPolicy<ManualResetEventSlimmer>());
public static ManualResetEventSlimmer Create(bool initialState) public static ManualResetEventSlimmer Create(bool initialState)
{ {
bool lockTaken = false; bool lockTaken = false;
_updateLock.Enter(ref lockTaken); _updateLock.Enter(ref lockTaken);
ManualResetEventSlimmer result = Shared.Get(); ManualResetEventSlimmer result = Shared.Get();
if (lockTaken) if (lockTaken)
{ {
_updateLock.Exit(false); _updateLock.Exit(false);
} }
result._returned = false; result._returned = false;
if (result._isSet != initialState) if (result._isSet != initialState)
{ {
result._isSet = initialState; result._isSet = initialState;
} }
return result; return result;
} }
} }
} }
@@ -1,26 +1,26 @@
namespace Recyclable.Collections.Parallel namespace Recyclable.Collections.Parallel
{ {
public struct SpinLockSlimmer public struct SpinLockSlimmer
{ {
private volatile int _locked; private volatile int _locked;
public void Enter() public void Enter()
{ {
if (Interlocked.CompareExchange(ref _locked, 1, 0) == 0) if (Interlocked.CompareExchange(ref _locked, 1, 0) == 0)
{ {
return; return;
} }
SpinWait waiter = new(); SpinWait waiter = new();
while (Interlocked.CompareExchange(ref _locked, 1, 0) == 1) while (Interlocked.CompareExchange(ref _locked, 1, 0) == 1)
{ {
waiter.SpinOnce(); waiter.SpinOnce();
} }
} }
public void Exit() public void Exit()
{ {
_locked = 0; _locked = 0;
} }
} }
} }
@@ -0,0 +1,20 @@
namespace Recyclable.Collections.Pools
{
public sealed class BiDirectionalRecyclableArrayPoolChunk<T>
{
private static readonly T[] _empty = Array.Empty<T>();
internal static T[] Empty => _empty;
internal T[] Value;
internal int Top;
internal int Bottom;
internal BiDirectionalRecyclableArrayPoolChunk<T>? Previous;
internal BiDirectionalRecyclableArrayPoolChunk<T>? Next;
public BiDirectionalRecyclableArrayPoolChunk()
{
Value = _empty;
}
}
}
@@ -0,0 +1,37 @@
using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Pools
{
public static class BiDirectionalRecyclableArrayPoolChunkPool<T>
{
private static readonly ObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>> _pool =
new DefaultObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<BiDirectionalRecyclableArrayPoolChunk<T>>());
public static BiDirectionalRecyclableArrayPoolChunk<T> Rent() => _pool.Get();
public static void Return(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{
chunk.Top = 0;
chunk.Bottom = 0;
chunk.Previous = null;
chunk.Next = null;
_pool.Return(chunk);
}
public static void Dispose(BiDirectionalRecyclableArrayPoolChunk<T> chunk, bool needsClearing)
{
var buffer = chunk.Value;
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
}
chunk.Value = BiDirectionalRecyclableArrayPoolChunk<T>.Empty;
chunk.Top = 0;
chunk.Bottom = 0;
chunk.Previous = null;
chunk.Next = null;
_pool.Return(chunk);
}
}
}
+31 -31
View File
@@ -1,32 +1,32 @@
namespace Recyclable.Collections.Pools namespace Recyclable.Collections.Pools
{ {
internal enum MemoryPressure internal enum MemoryPressure
{ {
High, High,
Medium, Medium,
Low Low
} }
internal static class MemoryUtils internal static class MemoryUtils
{ {
public static MemoryPressure GetMemoryPressure() public static MemoryPressure GetMemoryPressure()
{ {
const double HighPressureThreshold = .90; // Percent of GC memory pressure threshold we consider "high" const double HighPressureThreshold = .90; // Percent of GC memory pressure threshold we consider "high"
const double MediumPressureThreshold = .70; // Percent of GC memory pressure threshold we consider "medium" const double MediumPressureThreshold = .70; // Percent of GC memory pressure threshold we consider "medium"
GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo(); GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo();
if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold) if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold)
{ {
return MemoryPressure.High; return MemoryPressure.High;
} }
if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold) if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold)
{ {
return MemoryPressure.Medium; return MemoryPressure.Medium;
} }
return MemoryPressure.Low; return MemoryPressure.Low;
} }
} }
} }
+144 -144
View File
@@ -1,144 +1,144 @@
namespace Recyclable.Collections.Pools namespace Recyclable.Collections.Pools
{ {
public struct OneSizeArrayPool<T> public struct OneSizeArrayPool<T>
{ {
private volatile MemoryBucket<T>? _currentBucket; private volatile MemoryBucket<T>? _currentBucket;
private volatile int _blockCount; private volatile int _blockCount;
private volatile int _locked; private volatile int _locked;
private readonly int _blockSize; private readonly int _blockSize;
private readonly int _minBlockCount; private readonly int _minBlockCount;
internal volatile bool _memoryExceeded; internal volatile bool _memoryExceeded;
private void Enter() private void Enter()
{ {
if (Interlocked.CompareExchange(ref _locked, 1, 0) == 0) if (Interlocked.CompareExchange(ref _locked, 1, 0) == 0)
{ {
return; return;
} }
SpinWait waiter = new(); SpinWait waiter = new();
while (Interlocked.CompareExchange(ref _locked, 1, 0) == 1) while (Interlocked.CompareExchange(ref _locked, 1, 0) == 1)
{ {
waiter.SpinOnce(); waiter.SpinOnce();
} }
} }
private void Exit() private void Exit()
{ {
_locked = 0; _locked = 0;
} }
public OneSizeArrayPool(int blockSize, int minBlockCount) public OneSizeArrayPool(int blockSize, int minBlockCount)
{ {
_blockCount = 0; _blockCount = 0;
_currentBucket = null; _currentBucket = null;
_locked = 0; _locked = 0;
_memoryExceeded = false; _memoryExceeded = false;
_blockSize = blockSize; _blockSize = blockSize;
_minBlockCount = minBlockCount; _minBlockCount = minBlockCount;
_ = Task.Run(ReleaseMemoryOnExcess); _ = Task.Run(ReleaseMemoryOnExcess);
} }
public T[] Rent() public T[] Rent()
{ {
MemoryBucket<T>? bucket; MemoryBucket<T>? bucket;
Enter(); Enter();
if (_currentBucket != null) if (_currentBucket != null)
{ {
_blockCount--; _blockCount--;
bucket = _currentBucket; bucket = _currentBucket;
_currentBucket = _currentBucket.NextBucket; _currentBucket = _currentBucket.NextBucket;
} }
else else
{ {
bucket = null; bucket = null;
} }
Exit(); Exit();
return bucket != null ? bucket.Memory : new T[_blockSize]; return bucket != null ? bucket.Memory : new T[_blockSize];
} }
public void ReturnUnchecked(T[] array, bool clearArray) public void ReturnUnchecked(T[] array, bool clearArray)
{ {
MemoryBucket<T> bucket = new(array); MemoryBucket<T> bucket = new(array);
if (clearArray) if (clearArray)
{ {
// TODO: Measure performance // TODO: Measure performance
// new Span<T>(array).Clear(); // new Span<T>(array).Clear();
Array.Clear(array, 0, array.Length); Array.Clear(array, 0, array.Length);
} }
Enter(); Enter();
bucket.NextBucket = _currentBucket; bucket.NextBucket = _currentBucket;
_currentBucket = bucket; _currentBucket = bucket;
_blockCount++; _blockCount++;
Exit(); Exit();
} }
public void Return(T[] array, bool clearArray = false) public void Return(T[] array, bool clearArray = false)
{ {
if (array.Length != _blockSize) if (array.Length != _blockSize)
{ {
ThrowHelper.ThrowWrongReturnedArraySizeException(array.Length, _blockSize); ThrowHelper.ThrowWrongReturnedArraySizeException(array.Length, _blockSize);
} }
if (clearArray) if (clearArray)
{ {
// TODO: Measure performance // TODO: Measure performance
// new Span<T>(array).Clear(); // new Span<T>(array).Clear();
Array.Clear(array, 0, array.Length); Array.Clear(array, 0, array.Length);
} }
MemoryBucket<T> bucket = new(array); MemoryBucket<T> bucket = new(array);
Enter(); Enter();
if (_blockCount < _minBlockCount) if (_blockCount < _minBlockCount)
{ {
bucket.NextBucket = _currentBucket; bucket.NextBucket = _currentBucket;
_currentBucket = bucket; _currentBucket = bucket;
_blockCount++; _blockCount++;
} }
Exit(); Exit();
} }
private const int MemoryReleaseTaskSleepMilliseconds = 500; private const int MemoryReleaseTaskSleepMilliseconds = 500;
private async Task ReleaseMemoryOnExcess() private async Task ReleaseMemoryOnExcess()
{ {
while (true) while (true)
{ {
if (_memoryExceeded) if (_memoryExceeded)
{ {
Enter(); Enter();
if (_currentBucket != null) if (_currentBucket != null)
{ {
_blockCount--; _blockCount--;
_currentBucket = _currentBucket.NextBucket; _currentBucket = _currentBucket.NextBucket;
} }
if (_currentBucket != null) if (_currentBucket != null)
{ {
_blockCount--; _blockCount--;
_currentBucket = _currentBucket.NextBucket; _currentBucket = _currentBucket.NextBucket;
} }
if (_currentBucket != null) if (_currentBucket != null)
{ {
_blockCount--; _blockCount--;
_currentBucket = _currentBucket.NextBucket; _currentBucket = _currentBucket.NextBucket;
} }
if (_currentBucket != null) if (_currentBucket != null)
{ {
_blockCount--; _blockCount--;
_currentBucket = _currentBucket.NextBucket; _currentBucket = _currentBucket.NextBucket;
} }
Exit(); Exit();
} }
await Task.Delay(MemoryReleaseTaskSleepMilliseconds); await Task.Delay(MemoryReleaseTaskSleepMilliseconds);
} }
} }
} }
} }
@@ -1,53 +1,53 @@
using System.Numerics; using System.Numerics;
namespace Recyclable.Collections.Pools namespace Recyclable.Collections.Pools
{ {
public readonly struct RecyclableArrayPool<T> public readonly struct RecyclableArrayPool<T>
{ {
internal static readonly OneSizeArrayPool<T>[] _pools = new OneSizeArrayPool<T>[32]; internal static readonly OneSizeArrayPool<T>[] _pools = new OneSizeArrayPool<T>[32];
public static T[] RentShared(int blockSize) => _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)].Rent(); public static T[] RentShared(int blockSize) => _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)].Rent();
public static void ReturnShared(in T[] memoryBlock, bool needsClearing) public static void ReturnShared(in T[] memoryBlock, bool needsClearing)
{ {
if (BitOperations.IsPow2(memoryBlock.Length)) if (BitOperations.IsPow2(memoryBlock.Length))
{ {
_pools[31 - BitOperations.LeadingZeroCount((uint)memoryBlock.Length)].ReturnUnchecked(memoryBlock, needsClearing); _pools[31 - BitOperations.LeadingZeroCount((uint)memoryBlock.Length)].ReturnUnchecked(memoryBlock, needsClearing);
} }
} }
public static ref OneSizeArrayPool<T> Shared(int blockSize) public static ref OneSizeArrayPool<T> Shared(int blockSize)
=> ref _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)]; => ref _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)];
static RecyclableArrayPool() static RecyclableArrayPool()
{ {
var pools = _pools; var pools = _pools;
for (var arraySize = 0; arraySize < 32; arraySize++) for (var arraySize = 0; arraySize < 32; arraySize++)
{ {
int blockSize = 1 << arraySize; int blockSize = 1 << arraySize;
pools[arraySize] = new OneSizeArrayPool<T>(blockSize, 1000); pools[arraySize] = new OneSizeArrayPool<T>(blockSize, 1000);
} }
_ = Task.Run(CheckAndReportMemoryPressure); _ = Task.Run(CheckAndReportMemoryPressure);
} }
public static readonly OneSizeArrayPool<T> Null = new(0, 0); public static readonly OneSizeArrayPool<T> Null = new(0, 0);
private static async Task CheckAndReportMemoryPressure() private static async Task CheckAndReportMemoryPressure()
{ {
var previousValue = false; var previousValue = false;
while (true) while (true)
{ {
bool memoryExceeded = MemoryUtils.GetMemoryPressure() == MemoryPressure.High; bool memoryExceeded = MemoryUtils.GetMemoryPressure() == MemoryPressure.High;
if (memoryExceeded != previousValue) if (memoryExceeded != previousValue)
{ {
previousValue = memoryExceeded; previousValue = memoryExceeded;
for (var i = 0; i < _pools!.Length; i++) for (var i = 0; i < _pools!.Length; i++)
{ {
_pools[i]._memoryExceeded = memoryExceeded; _pools[i]._memoryExceeded = memoryExceeded;
} }
} }
await Task.Delay(500); await Task.Delay(500);
} }
} }
} }
} }
@@ -0,0 +1,19 @@
namespace Recyclable.Collections.Pools
{
public sealed class RecyclableArrayPoolChunk<T>
{
private static readonly T[] _empty = Array.Empty<T>();
internal static T[] Empty => _empty;
internal T[] Value;
internal int Index;
internal RecyclableArrayPoolChunk<T>? Previous;
internal RecyclableArrayPoolChunk<T>? Next;
public RecyclableArrayPoolChunk()
{
Value = _empty;
}
}
}
@@ -0,0 +1,34 @@
using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Pools
{
public static class RecyclableArrayPoolChunkPool<T>
{
private static readonly ObjectPool<RecyclableArrayPoolChunk<T>> _pool =
new DefaultObjectPool<RecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<RecyclableArrayPoolChunk<T>>());
public static RecyclableArrayPoolChunk<T> Rent() => _pool.Get();
public static void Return(RecyclableArrayPoolChunk<T> chunk)
{
chunk.Index = 0;
chunk.Previous = null;
chunk.Next = null;
_pool.Return(chunk);
}
public static void Dispose(RecyclableArrayPoolChunk<T> chunk, bool needsClearing)
{
var buffer = chunk.Value;
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
}
chunk.Value = RecyclableArrayPoolChunk<T>.Empty;
chunk.Index = 0;
chunk.Previous = null;
chunk.Next = null;
_pool.Return(chunk);
}
}
}
@@ -1,37 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <DefineConstants>$(DefineConstants);WITH_VERSIONING</DefineConstants>
<PackageId>Recyclable.Collections</PackageId> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageVersion>0.0.6</PackageVersion> <PackageId>Recyclable.Collections</PackageId>
<Authors>Marek Lemańczyk</Authors> <PackageVersion>0.0.6</PackageVersion>
<Company>N/A</Company> <Authors>Marek Lemańczyk</Authors>
<Product>Recyclable.Collections</Product> <Company>N/A</Company>
<PackageTags>Recyclable ArrayPool Array Pool List Sortable Sort</PackageTags> <Product>Recyclable.Collections</Product>
<Description>Recyclable.Collections project is an open source framework for operating dynamic lists at performance close to raw arrays. It aims at providing minimal memory footprint. It implements IList&lt;T&gt;'s interface and is targeted as direct replacements of List&lt;T&gt;, SortableList&lt;T&gt;, PriorityQueue&lt;T&gt; and similar.</Description> <PackageTags>Recyclable ArrayPool Array Pool List Sortable Sort</PackageTags>
<PackageProjectUrl>https://github.com/mlemanczyk/Recyclable.Collections</PackageProjectUrl> <Description>Recyclable.Collections project is an open source framework for operating dynamic lists at performance close to raw arrays. It aims at providing minimal memory footprint. It implements IList&lt;T&gt;'s interface and is targeted as direct replacements of List&lt;T&gt;, SortableList&lt;T&gt;, PriorityQueue&lt;T&gt; and similar.</Description>
<RepositoryUrl>https://github.com/mlemanczyk/Recyclable.Collections</RepositoryUrl> <PackageProjectUrl>https://github.com/mlemanczyk/Recyclable.Collections</PackageProjectUrl>
<Copyright>Marek Lemańczyk 2023</Copyright> <RepositoryUrl>https://github.com/mlemanczyk/Recyclable.Collections</RepositoryUrl>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile> <Copyright>Marek Lemańczyk 2023</Copyright>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<RepositoryType>git</RepositoryType> <PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>List&lt;T&gt; compatibility pack added for RecyclableList&lt;T&gt;. Various performance improvements and fixes.</PackageReleaseNotes> <RepositoryType>git</RepositoryType>
<Platforms>AnyCPU;x64</Platforms> <PackageReleaseNotes>List&lt;T&gt; compatibility pack added for RecyclableList&lt;T&gt;. Various performance improvements and fixes.</PackageReleaseNotes>
</PropertyGroup> <Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md"> <ItemGroup>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md">
</None> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
<None Include="..\README.md" Pack="true" PackagePath="" Link="README.md"> </None>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <None Include="..\README.md" Pack="true" PackagePath="" Link="README.md">
</None> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</ItemGroup> </None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" /> <ItemGroup>
<PackageReference Include="morelinq" Version="3.4.2" /> <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
</ItemGroup> <PackageReference Include="morelinq" Version="3.4.2" />
</Project> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VersionedCollectionsGenerator\VersionedCollectionsGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
+14 -14
View File
@@ -1,14 +1,14 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
public static class RecyclableDefaults public static class RecyclableDefaults
{ {
public const int BlockIndexLimit = int.MaxValue; public const int BlockIndexLimit = int.MaxValue;
public const int BlockSize = 16_384; public const int BlockSize = 16_384;
public const int InitialCapacity = 4; public const int InitialCapacity = 4;
public const long MinItemsCountForParallelization = 850_000; public const long MinItemsCountForParallelization = 850_000;
public const int MinPooledArrayLength = 37; public const int MinPooledArrayLength = 37;
public const int MaxPooledBlockSize = 2_147_483_591; public const int MaxPooledBlockSize = 2_147_483_591;
public const long ItemNotFoundIndexLong = -1L; public const long ItemNotFoundIndexLong = -1L;
public const int ItemNotFoundIndex = -1; public const int ItemNotFoundIndex = -1;
} }
} }
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,406 @@
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal sealed class RecyclableDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IDisposable
where TKey : notnull
{
internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType;
internal readonly int _blockSize;
internal readonly int _blockSizeMinus1;
internal readonly byte _blockShift;
#nullable disable
internal Entry[][] _entries;
#nullable restore
internal int[] _buckets;
internal int _count;
internal bool _disposed;
internal struct Entry
{
public int HashCode;
public int Next;
public TKey Key;
public TValue Value;
}
public RecyclableDictionary(int blockSize = RecyclableDefaults.BlockSize)
{
if (!BitOperations.IsPow2((uint)blockSize))
{
blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize);
}
_blockSize = blockSize;
_blockSizeMinus1 = blockSize - 1;
_blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize));
_entries = 4 >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(4)
: new Entry[4][];
_entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(blockSize)
: new Entry[blockSize];
_buckets = new int[4];
Array.Fill(_buckets, -1);
}
public int Count => _count;
public TValue this[TKey key]
{
get
{
var index = FindIndex(key, out _);
if (index >= 0)
{
return GetEntry(index).Value;
}
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "Key not found");
return default!;
}
set
{
var index = FindIndex(key, out _);
if (index >= 0)
{
ref var entry = ref GetEntry(index);
entry.Value = value;
return;
}
Add(key, value);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(TKey key, TValue value)
{
var hash = key.GetHashCode() & int.MaxValue;
if (_count >= (_buckets.Length * 3) / 4)
{
ResizeBuckets(_buckets.Length * 2);
}
EnsureCapacity(_count + 1);
int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket];
while (index >= 0)
{
ref var check = ref GetEntry(index);
if (check.HashCode == hash && EqualityComparer<TKey>.Default.Equals(check.Key, key))
{
throw new ArgumentException("An element with the same key already exists.", nameof(key));
}
index = check.Next;
}
int newIndex = _count++;
ref var entry = ref GetEntry(newIndex);
entry.HashCode = hash;
entry.Key = key;
entry.Value = value;
entry.Next = _buckets[bucket];
_buckets[bucket] = newIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(TKey key) => FindIndex(key, out _) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TKey key, out TValue value)
{
var index = FindIndex(key, out _);
if (index >= 0)
{
value = GetEntry(index).Value;
return true;
}
value = default!;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(TKey key)
{
if (_count == 0)
{
return false;
}
var hash = key.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1);
int prev = -1;
int index = _buckets[bucket];
while (index >= 0)
{
ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key))
{
if (prev < 0)
{
_buckets[bucket] = entry.Next;
}
else
{
GetEntry(prev).Next = entry.Next;
}
int lastIndex = _count - 1;
if (index != lastIndex)
{
MoveEntry(lastIndex, index);
}
ClearEntry(lastIndex);
_count--;
return true;
}
prev = index;
index = entry.Next;
}
return false;
}
public void Clear()
{
if (_count == 0)
{
Array.Fill(_buckets, -1);
return;
}
int blockIndex = 0;
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing);
_entries[blockIndex++] = null!;
}
}
else
{
if (_needsClearing)
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length);
_entries[blockIndex++] = null!;
}
}
else
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
_entries[blockIndex++] = null!;
}
}
}
_count = 0;
Array.Fill(_buckets, -1);
}
public TKey GetKey(int index) => GetEntry(index).Key;
public TValue GetValue(int index) => GetEntry(index).Value;
public Enumerator GetEnumerator() => new(this);
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
public void Dispose()
{
if (_disposed)
{
return;
}
Clear();
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
}
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<int>.ReturnShared(_buckets, false);
}
_entries = Array.Empty<Entry[]>();
_buckets = Array.Empty<int>();
_disposed = true;
GC.SuppressFinalize(this);
}
private ref Entry GetEntry(int index)
{
return ref _entries[index >> _blockShift][index & _blockSizeMinus1];
}
private void ClearEntry(int index)
{
ref var entry = ref GetEntry(index);
entry.HashCode = 0;
entry.Next = -1;
if (_needsClearing)
{
entry.Key = default!;
entry.Value = default!;
}
}
private void MoveEntry(int sourceIndex, int targetIndex)
{
ref var source = ref GetEntry(sourceIndex);
ref var target = ref GetEntry(targetIndex);
target = source;
int bucket = source.HashCode & (_buckets.Length - 1);
int cur = _buckets[bucket];
int prev = -1;
while (cur != sourceIndex)
{
prev = cur;
cur = GetEntry(cur).Next;
}
if (prev < 0)
{
_buckets[bucket] = targetIndex;
}
else
{
GetEntry(prev).Next = targetIndex;
}
}
private void EnsureCapacity(int min)
{
int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift;
if (_entries.Length < requiredBlocks)
{
var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks)
: new Entry[requiredBlocks][];
Array.Copy(_entries, newBlocks, _entries.Length);
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
}
_entries = newBlocks;
}
for (int i = 0; i < requiredBlocks; i++)
{
if (_entries[i] == null)
{
_entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(_blockSize)
: new Entry[_blockSize];
}
}
}
private void ResizeBuckets(int newSize)
{
var newBuckets = new int[newSize];
Array.Fill(newBuckets, -1);
for (int i = 0; i < _count; i++)
{
ref var entry = ref GetEntry(i);
int bucket = entry.HashCode & (newSize - 1);
entry.Next = newBuckets[bucket];
newBuckets[bucket] = i;
}
_buckets = newBuckets;
}
private int FindIndex(TKey key, out int hash)
{
if (_count == 0)
{
hash = 0;
return -1;
}
hash = key.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket];
while (index >= 0)
{
ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key))
{
return index;
}
index = entry.Next;
}
return -1;
}
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
{
private readonly RecyclableDictionary<TKey, TValue> _dictionary;
private int _index;
private KeyValuePair<TKey, TValue> _current;
internal Enumerator(RecyclableDictionary<TKey, TValue> dictionary)
{
_dictionary = dictionary;
_index = 0;
_current = default;
}
public KeyValuePair<TKey, TValue> Current => _current;
object IEnumerator.Current => _current;
public bool MoveNext()
{
if (_index >= _dictionary._count)
{
return false;
}
var entry = _dictionary.GetEntry(_index++);
_current = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
return true;
}
public void Reset()
{
_index = 0;
_current = default;
}
public void Dispose()
{
}
}
}
}
File diff suppressed because it is too large. Load diff
+362
View File
@@ -0,0 +1,362 @@
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal sealed class RecyclableHashSet<T> : IEnumerable<T>, IDisposable
where T : notnull
{
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
internal readonly int _blockSize;
internal readonly int _blockSizeMinus1;
internal readonly byte _blockShift;
#nullable disable
internal Entry[][] _entries;
#nullable restore
internal int[] _buckets;
internal int _count;
internal bool _disposed;
internal struct Entry
{
public int HashCode;
public int Next;
public T Value;
}
public RecyclableHashSet(int blockSize = RecyclableDefaults.BlockSize)
{
if (!BitOperations.IsPow2((uint)blockSize))
{
blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize);
}
_blockSize = blockSize;
_blockSizeMinus1 = blockSize - 1;
_blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize));
_entries = 4 >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(4)
: new Entry[4][];
_entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(blockSize)
: new Entry[blockSize];
_buckets = new int[4];
Array.Fill(_buckets, -1);
}
public int Count => _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add(T item)
{
var hash = item.GetHashCode() & int.MaxValue;
if (_count >= (_buckets.Length * 3) / 4)
{
ResizeBuckets(_buckets.Length * 2);
}
EnsureCapacity(_count + 1);
int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket];
while (index >= 0)
{
ref var check = ref GetEntry(index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, item))
{
return false;
}
index = check.Next;
}
int newIndex = _count++;
ref var entry = ref GetEntry(newIndex);
entry.HashCode = hash;
entry.Value = item;
entry.Next = _buckets[bucket];
_buckets[bucket] = newIndex;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(T item) => FindIndex(item, out _) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(T item)
{
if (_count == 0)
{
return false;
}
var hash = item.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1);
int prev = -1;
int index = _buckets[bucket];
while (index >= 0)
{
ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item))
{
if (prev < 0)
{
_buckets[bucket] = entry.Next;
}
else
{
GetEntry(prev).Next = entry.Next;
}
int lastIndex = _count - 1;
if (index != lastIndex)
{
MoveEntry(lastIndex, index);
}
ClearEntry(lastIndex);
_count--;
return true;
}
prev = index;
index = entry.Next;
}
return false;
}
public void Clear()
{
if (_count == 0)
{
Array.Fill(_buckets, -1);
return;
}
int blockIndex = 0;
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing);
_entries[blockIndex++] = null!;
}
}
else
{
if (_needsClearing)
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length);
_entries[blockIndex++] = null!;
}
}
else
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
_entries[blockIndex++] = null!;
}
}
}
_count = 0;
Array.Fill(_buckets, -1);
}
public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
public void Dispose()
{
if (_disposed)
{
return;
}
Clear();
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
}
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<int>.ReturnShared(_buckets, false);
}
_entries = Array.Empty<Entry[]>();
_buckets = Array.Empty<int>();
_disposed = true;
GC.SuppressFinalize(this);
}
private ref Entry GetEntry(int index)
{
return ref _entries[index >> _blockShift][index & _blockSizeMinus1];
}
private void ClearEntry(int index)
{
ref var entry = ref GetEntry(index);
entry.HashCode = 0;
entry.Next = -1;
if (_needsClearing)
{
entry.Value = default!;
}
}
private void MoveEntry(int sourceIndex, int targetIndex)
{
ref var source = ref GetEntry(sourceIndex);
ref var target = ref GetEntry(targetIndex);
target = source;
int bucket = source.HashCode & (_buckets.Length - 1);
int cur = _buckets[bucket];
int prev = -1;
while (cur != sourceIndex)
{
prev = cur;
cur = GetEntry(cur).Next;
}
if (prev < 0)
{
_buckets[bucket] = targetIndex;
}
else
{
GetEntry(prev).Next = targetIndex;
}
}
private void EnsureCapacity(int min)
{
int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift;
if (_entries.Length < requiredBlocks)
{
var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks)
: new Entry[requiredBlocks][];
Array.Copy(_entries, newBlocks, _entries.Length);
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
}
_entries = newBlocks;
}
for (int i = 0; i < requiredBlocks; i++)
{
if (_entries[i] == null)
{
_entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(_blockSize)
: new Entry[_blockSize];
}
}
}
private void ResizeBuckets(int newSize)
{
int[] newBuckets = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<int>.RentShared(newSize)
: new int[newSize];
Array.Fill(newBuckets, -1);
for (int i = 0; i < _count; i++)
{
ref var entry = ref GetEntry(i);
int bucket = entry.HashCode & (newSize - 1);
entry.Next = newBuckets[bucket];
newBuckets[bucket] = i;
}
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<int>.ReturnShared(_buckets, false);
}
_buckets = newBuckets;
}
private int FindIndex(T item, out int hash)
{
if (_count == 0)
{
hash = 0;
return -1;
}
hash = item.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket];
while (index >= 0)
{
ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item))
{
return index;
}
index = entry.Next;
}
return -1;
}
public struct Enumerator : IEnumerator<T>
{
private readonly RecyclableHashSet<T> _set;
private int _index;
private T? _current;
internal Enumerator(RecyclableHashSet<T> set)
{
_set = set;
_index = 0;
_current = default;
}
public T Current => _current!;
object IEnumerator.Current => _current!;
public bool MoveNext()
{
if (_index >= _set._count)
{
return false;
}
_current = _set.GetEntry(_index++).Value;
return true;
}
public void Reset()
{
_index = 0;
_current = default;
}
public void Dispose()
{
}
}
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,323 @@
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal sealed class RecyclableLinkedList<T> : IEnumerable<T>, IDisposable
{
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous, bool forHead)
{
var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent();
if (chunk.Value.Length < size)
{
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
}
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(size)
: new T[size];
}
if (forHead)
{
chunk.Top = size;
chunk.Bottom = size;
}
else
{
chunk.Top = 0;
chunk.Bottom = 0;
}
chunk.Previous = previous;
chunk.Next = null;
return chunk;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReturnChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{
BiDirectionalRecyclableArrayPoolChunkPool<T>.Return(chunk);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DisposeChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{
BiDirectionalRecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing);
}
internal BiDirectionalRecyclableArrayPoolChunk<T> _head;
internal BiDirectionalRecyclableArrayPoolChunk<T> _tail;
internal bool _disposed;
internal long _capacity;
internal long _count;
public RecyclableLinkedList(int initialCapacity = RecyclableDefaults.InitialCapacity)
{
if (initialCapacity < 1)
{
initialCapacity = 1;
}
if (!BitOperations.IsPow2((uint)initialCapacity))
{
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
}
_head = RentChunk(initialCapacity, null, false);
_tail = _head;
_capacity = initialCapacity;
_count = 0;
}
public RecyclableLinkedList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity)
{
foreach (var item in source)
{
AddLast(item);
}
}
public int Count => checked((int)_count);
public long LongCount => _count;
public void AddLast(T item)
{
if (_tail.Top == _tail.Value.Length)
{
GrowTail();
}
_tail.Value[_tail.Top++] = item;
_count++;
}
public void AddFirst(T item)
{
if (_head.Bottom == 0)
{
GrowHead();
}
_head.Value[--_head.Bottom] = item;
_count++;
}
public T RemoveFirst()
{
if (_count == 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
}
_count--;
var value = _head.Value[_head.Bottom];
if (_needsClearing)
{
_head.Value[_head.Bottom] = default!;
}
_head.Bottom++;
if (_head.Bottom == _head.Top && _head.Next != null)
{
ReleaseHeadChunk();
}
else if (_head.Bottom == _head.Top)
{
_head.Bottom = _head.Top;
}
return value;
}
public T RemoveLast()
{
if (_count == 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
}
_count--;
_tail.Top--;
var value = _tail.Value[_tail.Top];
if (_needsClearing)
{
_tail.Value[_tail.Top] = default!;
}
if (_tail.Top == _tail.Bottom && _tail.Previous != null)
{
ReleaseTailChunk();
}
else if (_tail.Top == _tail.Bottom)
{
_tail.Top = _tail.Bottom;
}
return value;
}
public void Clear()
{
while (_head.Next != null)
{
ReleaseHeadChunk();
}
while (_tail.Previous != null)
{
ReleaseTailChunk();
}
if (_needsClearing && _head.Top > _head.Bottom)
{
Array.Clear(_head.Value, _head.Bottom, _head.Top - _head.Bottom);
}
_capacity = _head.Value.Length;
_head.Bottom = _head.Top;
_tail = _head;
_count = 0;
}
public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
private void GrowTail()
{
int newSize;
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
{
newSize = RecyclableDefaults.MaxPooledBlockSize;
}
else
{
var doubled = _capacity << 1;
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
}
var newChunk = RentChunk(newSize, _tail, false);
_tail.Next = newChunk;
_tail = newChunk;
_capacity += newSize;
}
private void GrowHead()
{
int newSize;
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
{
newSize = RecyclableDefaults.MaxPooledBlockSize;
}
else
{
var doubled = _capacity << 1;
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
}
var newChunk = RentChunk(newSize, null, true);
newChunk.Next = _head;
_head.Previous = newChunk;
_head = newChunk;
_capacity += newSize;
}
private void ReleaseHeadChunk()
{
var toReturn = _head;
_head = toReturn.Next!;
_head.Previous = null;
_capacity -= toReturn.Value.Length;
ReturnChunk(toReturn);
}
private void ReleaseTailChunk()
{
var toReturn = _tail;
_tail = toReturn.Previous!;
_tail.Next = null;
_capacity -= toReturn.Value.Length;
ReturnChunk(toReturn);
}
public void Dispose()
{
if (_disposed)
{
return;
}
var chunk = _head;
while (chunk != null)
{
var next = chunk.Next;
DisposeChunk(chunk);
chunk = next;
}
_disposed = true;
GC.SuppressFinalize(this);
}
public struct Enumerator : IEnumerator<T>
{
private readonly BiDirectionalRecyclableArrayPoolChunk<T>? _start;
private BiDirectionalRecyclableArrayPoolChunk<T>? _chunk;
private int _index;
private T? _current;
internal Enumerator(RecyclableLinkedList<T> list)
{
_start = list._head;
_chunk = list._head;
_index = _chunk != null ? _chunk.Bottom : 0;
_current = default;
}
public T Current => _current!;
object IEnumerator.Current => _current!;
public bool MoveNext()
{
if (_chunk == null)
{
return false;
}
while (_index >= _chunk.Top)
{
_chunk = _chunk.Next;
if (_chunk == null)
{
return false;
}
_index = _chunk.Bottom;
}
_current = _chunk.Value[_index++];
return true;
}
public void Reset()
{
_chunk = _start;
_index = _chunk != null ? _chunk.Bottom : 0;
}
public void Dispose()
{
_chunk = null;
_current = default;
}
}
}
}
File diff suppressed because it is too large. Load diff
@@ -1,272 +1,272 @@
using System.Numerics; using System.Numerics;
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")] [Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
public static class zRecyclableListCompatibilityListFind public static class zRecyclableListCompatibilityListFind
{ {
public static bool Exists<T>(this RecyclableList<T> list, Predicate<T> match) public static bool Exists<T>(this RecyclableList<T> list, Predicate<T> match)
{ {
int sourceItemsCount = list._count; int sourceItemsCount = list._count;
if (sourceItemsCount == 0) if (sourceItemsCount == 0)
{ {
return false; return false;
} }
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
return true; return true;
} }
} }
return false; return false;
} }
public static T? Find<T>(this RecyclableList<T> list, Predicate<T> match) public static T? Find<T>(this RecyclableList<T> list, Predicate<T> match)
{ {
int sourceItemsCount = list._count; int sourceItemsCount = list._count;
if (sourceItemsCount == 0) if (sourceItemsCount == 0)
{ {
return default; return default;
} }
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
return sourceSpan[itemIndex]; return sourceSpan[itemIndex];
} }
} }
return default; return default;
} }
public static RecyclableList<T> FindAll<T>(this RecyclableList<T> list, Predicate<T> match) public static RecyclableList<T> FindAll<T>(this RecyclableList<T> list, Predicate<T> match)
{ {
int sourceItemsCount = list._count; int sourceItemsCount = list._count;
if (sourceItemsCount == 0) if (sourceItemsCount == 0)
{ {
return new(); return new();
} }
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
RecyclableList<T> result = new(sourceItemsCount >> 3); RecyclableList<T> result = new(sourceItemsCount >> 3);
Span<T> resultSpan = result._memoryBlock; Span<T> resultSpan = result._memoryBlock;
int capacity = result._capacity, int capacity = result._capacity,
resultItemsCount = 0; resultItemsCount = 0;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
if (resultItemsCount + 1 > capacity) if (resultItemsCount + 1 > capacity)
{ {
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1)); capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1));
_ = RecyclableListHelpers<T>.EnsureCapacity(result, resultItemsCount, capacity); _ = RecyclableListHelpers<T>.EnsureCapacity(result, resultItemsCount, capacity);
resultSpan = result._memoryBlock; resultSpan = result._memoryBlock;
} }
resultSpan[resultItemsCount++] = sourceSpan[itemIndex]; resultSpan[resultItemsCount++] = sourceSpan[itemIndex];
} }
} }
result._capacity = capacity; result._capacity = capacity;
result._count = resultItemsCount; result._count = resultItemsCount;
return result; return result;
} }
public static RecyclableList<int> FindAllIndexes<T>(this RecyclableList<T> list, Predicate<T> match) public static RecyclableList<int> FindAllIndexes<T>(this RecyclableList<T> list, Predicate<T> match)
{ {
int sourceItemsCount = list._count; int sourceItemsCount = list._count;
if (sourceItemsCount == 0) if (sourceItemsCount == 0)
{ {
return new(); return new();
} }
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
RecyclableList<int> result = new(sourceItemsCount >> 3); RecyclableList<int> result = new(sourceItemsCount >> 3);
Span<int> resultSpan = result._memoryBlock; Span<int> resultSpan = result._memoryBlock;
int capacity = result._capacity, int capacity = result._capacity,
resultItemsCount = 0; resultItemsCount = 0;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
if (resultItemsCount + 1 > capacity) if (resultItemsCount + 1 > capacity)
{ {
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1)); capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1));
_ = RecyclableListHelpers<int>.EnsureCapacity(result, resultItemsCount, capacity); _ = RecyclableListHelpers<int>.EnsureCapacity(result, resultItemsCount, capacity);
resultSpan = result._memoryBlock; resultSpan = result._memoryBlock;
} }
resultSpan[resultItemsCount++] = itemIndex; resultSpan[resultItemsCount++] = itemIndex;
} }
} }
result._capacity = capacity; result._capacity = capacity;
result._count = resultItemsCount; result._count = resultItemsCount;
return result; return result;
} }
public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match) public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match)
{ {
int sourceItemsCount = list._count; int sourceItemsCount = list._count;
if (sourceItemsCount == 0 || startIndex >= sourceItemsCount) if (sourceItemsCount == 0 || startIndex >= sourceItemsCount)
{ {
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount); sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount);
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++) for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
return itemIndex; return itemIndex;
} }
} }
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match) public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match)
{ {
int sourceItemsCount = list._count; int sourceItemsCount = list._count;
if (sourceItemsCount == 0 || startIndex >= sourceItemsCount) if (sourceItemsCount == 0 || startIndex >= sourceItemsCount)
{ {
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount); sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount);
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++) for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
return itemIndex; return itemIndex;
} }
} }
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
public static int FindIndex<T>(this RecyclableList<T> list, Predicate<T> match) public static int FindIndex<T>(this RecyclableList<T> list, Predicate<T> match)
{ {
int sourceItemsCount = list._count; int sourceItemsCount = list._count;
if (sourceItemsCount == 0) if (sourceItemsCount == 0)
{ {
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
return itemIndex; return itemIndex;
} }
} }
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
public static T? FindLast<T>(this RecyclableList<T> list, Predicate<T> match) public static T? FindLast<T>(this RecyclableList<T> list, Predicate<T> match)
{ {
if (list._count == 0) if (list._count == 0)
{ {
return default; return default;
} }
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--) for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
return sourceSpan[itemIndex]; return sourceSpan[itemIndex];
} }
} }
return default; return default;
} }
public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match) public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match)
{ {
if (count == 0) if (count == 0)
{ {
if (startIndex != -1) if (startIndex != -1)
{ {
ThrowHelper.ThrowArgumentOutOfRangeException_Index(); ThrowHelper.ThrowArgumentOutOfRangeException_Index();
} }
} }
else if (count < 0 || startIndex - count + 1 < 0) else if (count < 0 || startIndex - count + 1 < 0)
{ {
ThrowHelper.ThrowArgumentOutOfRangeException_Count(); ThrowHelper.ThrowArgumentOutOfRangeException_Count();
} }
if (startIndex < 0) if (startIndex < 0)
{ {
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
int sourceItemsCount = count; // startIndex - count + 1 >= 0 ? count : startIndex + 1; int sourceItemsCount = count; // startIndex - count + 1 >= 0 ? count : startIndex + 1;
if (sourceItemsCount == 0) if (sourceItemsCount == 0)
{ {
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
int lastItemIndex = startIndex - sourceItemsCount; int lastItemIndex = startIndex - sourceItemsCount;
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex > lastItemIndex; itemIndex--) for (var itemIndex = startIndex; itemIndex > lastItemIndex; itemIndex--)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
return itemIndex; return itemIndex;
} }
} }
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match) public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match)
{ {
if (list._count == 0 || startIndex < 0) if (list._count == 0 || startIndex < 0)
{ {
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex >= 0; itemIndex--) for (var itemIndex = startIndex; itemIndex >= 0; itemIndex--)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
return itemIndex; return itemIndex;
} }
} }
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
public static int FindLastIndex<T>(this RecyclableList<T> list, Predicate<T> match) public static int FindLastIndex<T>(this RecyclableList<T> list, Predicate<T> match)
{ {
if (list._count == 0) if (list._count == 0)
{ {
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
ReadOnlySpan<T> sourceSpan = list._memoryBlock; ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--) for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--)
{ {
if (match(sourceSpan[itemIndex])) if (match(sourceSpan[itemIndex]))
{ {
return itemIndex; return itemIndex;
} }
} }
return RecyclableDefaults.ItemNotFoundIndex; return RecyclableDefaults.ItemNotFoundIndex;
} }
} }
} }
@@ -1,26 +1,26 @@
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")] [Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
public static class zRecyclableListCompatibilityListIndexOf public static class zRecyclableListCompatibilityListIndexOf
{ {
public static int IndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0 public static int IndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0
? Array.IndexOf(list._memoryBlock, item, index, list._count - index) ? Array.IndexOf(list._memoryBlock, item, index, list._count - index)
: RecyclableDefaults.ItemNotFoundIndex; : RecyclableDefaults.ItemNotFoundIndex;
public static int IndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0 public static int IndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0
? Array.IndexOf(list._memoryBlock, item, index, count) ? Array.IndexOf(list._memoryBlock, item, index, count)
: RecyclableDefaults.ItemNotFoundIndex; : RecyclableDefaults.ItemNotFoundIndex;
public static int LastIndexOf<T>(this RecyclableList<T> list, T item) => list._count != 0 public static int LastIndexOf<T>(this RecyclableList<T> list, T item) => list._count != 0
? Array.LastIndexOf(list._memoryBlock, item, list._count - 1) ? Array.LastIndexOf(list._memoryBlock, item, list._count - 1)
: RecyclableDefaults.ItemNotFoundIndex; : RecyclableDefaults.ItemNotFoundIndex;
public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0 public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0
? Array.LastIndexOf(list._memoryBlock, item, index) ? Array.LastIndexOf(list._memoryBlock, item, index)
: RecyclableDefaults.ItemNotFoundIndex; : RecyclableDefaults.ItemNotFoundIndex;
public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0 public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0
? Array.LastIndexOf(list._memoryBlock, item, index, count) ? Array.LastIndexOf(list._memoryBlock, item, index, count)
: RecyclableDefaults.ItemNotFoundIndex; : RecyclableDefaults.ItemNotFoundIndex;
} }
} }
Loaded 100 of 151 files, more files were not shown because too many files have changed in this diff. Show more