Migrated to git.privacyguard.company

This commit is contained in:
Marek Lemanczyk committed 2026-09-08 22:49:08 +02:00
1 parent 4b17a4c827
commit b5a574355e
151 files changed
+38551 -38550

No files matched your search

+28 -28
View File
@@ -1,28 +1,28 @@
# This workflow will build a .NET project # 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 # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
name: .NET name: .NET
on: on:
push: push:
branches: [ "main" ] branches: [ "main" ]
pull_request: pull_request:
branches: [ "main" ] branches: [ "main" ]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.x
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore
- name: Build - name: Build
run: dotnet build --no-restore run: dotnet build --no-restore
- name: Test - name: Test
run: dotnet test --framework net8.0 --verbosity normal 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/net8.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 -29
View File
@@ -1,29 +1,29 @@
# AGENTS # AGENTS
This repository hosts various libraries distributed as NuGet packages. Keep these links up to date so contributors and tools can easily locate them. 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. Only the .NET SDK **8.0** is available in the execution environment. Use this version when building or running tests.
## Implementation Guidelines ## Implementation Guidelines
1. All `*.csproj` files should enable nullable reference types with `<Nullable>enable</Nullable>`. 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. 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. 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 ## 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`, 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. 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. 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**. 1. All tests must use **xUnit** together with **FluentAssertions**.
## Source Code Formatting Guidelines ## Source Code Formatting Guidelines
1. Indent code using four spaces or a single tab per level. 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. 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. 1. Always use braces for `if` and `else` bodies.
## NuGet packages ## NuGet packages
- [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections) - [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections)
- [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List) - [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List)
- [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent) - [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent)
- [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq) - [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq)
- [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching) - [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching)
- [Recyclable.Collections.TestData](https://github.com/mlemanczyk/Recyclable.Collections.TestData) - [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 -433
View File
@@ -1,433 +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>`
## NuGet packages ## NuGet packages
- [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections) - [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections)
- [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List) - [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List)
- [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent) - [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent)
- [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq) - [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq)
- [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching) - [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching)
- [Recyclable.Collections.TestData](https://github.com/mlemanczyk/Recyclable.Collections.TestData) - [Recyclable.Collections.TestData](https://github.com/mlemanczyk/Recyclable.Collections.TestData)
# Milestones # Milestones
1. ✅ Create basic classes 1. ✅ Create basic classes
1. ✅ `RecyclableList<T>` 1. ✅ `RecyclableList<T>`
1. ✅ `RecyclableLongList<T>` 1. ✅ `RecyclableLongList<T>`
1. 🅿️ `RecyclableQueue<T>` 1. 🅿️ `RecyclableQueue<T>`
1. 🅿️ `RecyclableSortedList<T>` 1. 🅿️ `RecyclableSortedList<T>`
1. 🅿️ `RecyclableStack<T>` 1. 🅿️ `RecyclableStack<T>`
1. 🅿️ `RecyclableUnorderedList<T>` 1. 🅿️ `RecyclableUnorderedList<T>`
1. ✅ Create basic unit tests 1. ✅ Create basic unit tests
1. ✅ `RecyclableList<T>` 1. ✅ `RecyclableList<T>`
1. ✅ `RecyclableLongList<T>` 1. ✅ `RecyclableLongList<T>`
1. 🅿️ `RecyclableQueue<T>` 1. 🅿️ `RecyclableQueue<T>`
1. 🅿️ `RecyclableSortedList<T>` 1. 🅿️ `RecyclableSortedList<T>`
1. 🅿️ `RecyclableStack<T>` 1. 🅿️ `RecyclableStack<T>`
1. 🅿️ `RecyclableUnorderedList<T>` 1. 🅿️ `RecyclableUnorderedList<T>`
1. ✅ Optimize `RecyclableList<T>` 1. ✅ Optimize `RecyclableList<T>`
1. ✅ `Add` 1. ✅ `Add`
1. ✅ `AddRange` 1. ✅ `AddRange`
1. ✅ when source is `array<T>` 1. ✅ when source is `array<T>`
1. ✅ when source is `List<T>` 1. ✅ when source is `List<T>`
1. ✅ when source is `IList<T>` 1. ✅ when source is `IList<T>`
1. ✅ when source is `RecyclableList<T>` 1. ✅ when source is `RecyclableList<T>`
1. ✅ when source is `RecyclableLongList<T>` 1. ✅ when source is `RecyclableLongList<T>`
1. ✅ when source is `IEnumerable<T>` 1. ✅ when source is `IEnumerable<T>`
1. ✅ when source has non-enumerated count 1. ✅ when source has non-enumerated count
1. ✅ `Clear` 1. ✅ `Clear`
1. ✅ `Contains` 1. ✅ `Contains`
1. ✅ `CopyTo` 1. ✅ `CopyTo`
1. ✅ `Dispose` 1. ✅ `Dispose`
1. ✅ `EnsureCapacity` 1. ✅ `EnsureCapacity`
1. ✅ `GetEnumerator` 1. ✅ `GetEnumerator`
1. ✅ `IndexOf` 1. ✅ `IndexOf`
1. ✅ `Insert` 1. ✅ `Insert`
1. ✅ `Remove` 1. ✅ `Remove`
1. ✅ `RemoveAt` 1. ✅ `RemoveAt`
1. ✅ `Resize` 1. ✅ `Resize`
1. ✅ `this[int index]` 1. ✅ `this[int index]`
1. ✅ Port `RecyclableList<T>` implementation to `RecyclableLongList<T>` 1. ✅ Port `RecyclableList<T>` implementation to `RecyclableLongList<T>`
1. 👉 Optimize `RecyclableLongList<T>` 1. 👉 Optimize `RecyclableLongList<T>`
1. ✅ `Add` 1. ✅ `Add`
1. ✅ `AddRange` 1. ✅ `AddRange`
1. ✅ when source is `array<T>` 1. ✅ when source is `array<T>`
1. ✅ when source is `List<T>` 1. ✅ when source is `List<T>`
1. ✅ when source is `IList<T>` 1. ✅ when source is `IList<T>`
1. ✅ when source is `RecyclableList<T>` 1. ✅ when source is `RecyclableList<T>`
1. ✅ when source is `RecyclableLongList<T>` 1. ✅ when source is `RecyclableLongList<T>`
1. ✅ when source is `IEnumerable<T>` 1. ✅ when source is `IEnumerable<T>`
1. ✅ when source has non-enumerated count 1. ✅ when source has non-enumerated count
1. ✅ `Clear` 1. ✅ `Clear`
1. ✅ `Contains` 1. ✅ `Contains`
1. 🅿️ `CopyTo` 1. 🅿️ `CopyTo`
1. ✅ `Dispose` 1. ✅ `Dispose`
1. ✅ `EnsureCapacity` 1. ✅ `EnsureCapacity`
1. ✅ `GetEnumerator` 1. ✅ `GetEnumerator`
1. ✅ `IndexOf` 1. ✅ `IndexOf`
1. ✅ `LongIndexOf` 1. ✅ `LongIndexOf`
1. ✅ `Insert` 1. ✅ `Insert`
1. ✅ `Remove` 1. ✅ `Remove`
1. ✅ `RemoveAt(int index)` 1. ✅ `RemoveAt(int index)`
1. ✅ `RemoveAt(long index)` 1. ✅ `RemoveAt(long index)`
1. ✅ `RemoveAt` 1. ✅ `RemoveAt`
1. ✅ `Resize` 1. ✅ `Resize`
1. ✅ `this[int index]` 1. ✅ `this[int index]`
1. ✅ `this[long index]` 1. ✅ `this[long index]`
1. ✅ Rename `RecyclableList<T>` to `RecyclableLongList<T>` 1. ✅ Rename `RecyclableList<T>` to `RecyclableLongList<T>`
1. ✅ Rename `RecyclableArrayList<T>` to `RecyclableList<T>` 1. ✅ Rename `RecyclableArrayList<T>` to `RecyclableList<T>`
1. ✅ Fix failing tests 1. ✅ Fix failing tests
1. ✅ Hide not ready classes 1. ✅ Hide not ready classes
1. ✅ `RecyclableQueue<T>` 1. ✅ `RecyclableQueue<T>`
1. ✅ `RecyclableSortedList<T>` 1. ✅ `RecyclableSortedList<T>`
1. ✅ `RecyclableStack<T>` 1. ✅ `RecyclableStack<T>`
1. ✅ `RecyclableUnorderedList<T>` 1. ✅ `RecyclableUnorderedList<T>`
1. ✅ Add support for `ReadOnlySpan<T>` 1. ✅ Add support for `ReadOnlySpan<T>`
1. ✅ Release 0.0.3-alpha 1. ✅ Release 0.0.3-alpha
1. ✅ Implement `List<T>` interfaces 1. ✅ Implement `List<T>` interfaces
1. ✅ `IList<T>` 1. ✅ `IList<T>`
1. ✅ `ICollection<T>` 1. ✅ `ICollection<T>`
1. ✅ `IEnumerable<T>` 1. ✅ `IEnumerable<T>`
1. ✅ `IEnumerable` 1. ✅ `IEnumerable`
1. ✅ `IReadOnlyList<T>` 1. ✅ `IReadOnlyList<T>`
1. ✅ `IReadOnlyCollection<T>` 1. ✅ `IReadOnlyCollection<T>`
1. ✅ `IList` 1. ✅ `IList`
1. ✅ `ICollection` 1. ✅ `ICollection`
1. ✅ Implement list versioning to allow data change identification 1. ✅ Implement list versioning to allow data change identification
1. ✅ Make sure that `NeedsClearing` is used & items are cleared in 1. ✅ Make sure that `NeedsClearing` is used & items are cleared in
1. ✅ `RecyclableList<T>` 1. ✅ `RecyclableList<T>`
1. ✅ `Clear` 1. ✅ `Clear`
1. ✅ `Dispose` 1. ✅ `Dispose`
1. ✅ `Remove` 1. ✅ `Remove`
1. ✅ `RemoveAt` 1. ✅ `RemoveAt`
1. ✅ Properties 1. ✅ Properties
1. ✅ Non-versioned & Versioned Enumerators 1. ✅ Non-versioned & Versioned Enumerators
1. ✅ `RecyclableListHelpers` 1. ✅ `RecyclableListHelpers`
1. ✅ `EnsureCapacity` 1. ✅ `EnsureCapacity`
1. ✅ `Resize` 1. ✅ `Resize`
1. ✅ `IList<T>` implementation 1. ✅ `IList<T>` implementation
1. ✅ `RecyclableLongList<T>` 1. ✅ `RecyclableLongList<T>`
1. ✅ `Clear` 1. ✅ `Clear`
1. ✅ `Dispose` 1. ✅ `Dispose`
1. ✅ `Remove` 1. ✅ `Remove`
1. ✅ `RemoveAt` 1. ✅ `RemoveAt`
1. ✅ `RemoveBlock` 1. ✅ `RemoveBlock`
1. ✅ Properties 1. ✅ Properties
1. ✅ Non-versioned & Versioned Enumerators 1. ✅ Non-versioned & Versioned Enumerators
1. ✅ `RecyclableLongListHelpers` 1. ✅ `RecyclableLongListHelpers`
1. ✅ `CopyFollowingItems` 1. ✅ `CopyFollowingItems`
1. ✅ `CopyTo` 1. ✅ `CopyTo`
1. ✅ `EnsureCapacity` 1. ✅ `EnsureCapacity`
1. ✅ `Resize` 1. ✅ `Resize`
1. ✅ `RecyclableLongList<T>.IndexOfHelpers` 1. ✅ `RecyclableLongList<T>.IndexOfHelpers`
1. ✅ Complete the implementation of `RecyclableLongList<T>` 1. ✅ Complete the implementation of `RecyclableLongList<T>`
1. ✅ `InsertAt` to allow inserting items anywhere 1. ✅ `InsertAt` to allow inserting items anywhere
1. ✅ `RemoveAt` to allow removing item at any index 1. ✅ `RemoveAt` to allow removing item at any index
1. ✅ Release 0.0.4 1. ✅ Release 0.0.4
1. ✅ Extend unit tests 1. ✅ Extend unit tests
1. ✅ `.Add` / `.AddRange` must accept `null` values 1. ✅ `.Add` / `.AddRange` must accept `null` values
1. ✅ `.AddRange` must accept empty collections 1. ✅ `.AddRange` must accept empty collections
1. ✅ `.Remove` / `.RemoveAt` / `.Clear` / `.Dispose` must clear reference when reference type 1. ✅ `.Remove` / `.RemoveAt` / `.Clear` / `.Dispose` must clear reference when reference type
1. ✅ `.Clear` / `.Dispose` must return arrays to pools 1. ✅ `.Clear` / `.Dispose` must return arrays to pools
1. ✅ `int` / `long` variants of `.Count` / `.LongCount`, `.IndexOf` / `.LongIndexOf`, `.Insert`, `.RemoveAt`, `this[]` 1. ✅ `int` / `long` variants of `.Count` / `.LongCount`, `.IndexOf` / `.LongIndexOf`, `.Insert`, `.RemoveAt`, `this[]`
1. ✅ Add `.ToRecyclableList` / `.ToRecyclableLongList` variants for all supported collection types 1. ✅ Add `.ToRecyclableList` / `.ToRecyclableLongList` variants for all supported collection types
1. ✅ `Array` 1. ✅ `Array`
1. ✅ `ICollection` 1. ✅ `ICollection`
1. ✅ `ICollection<T>` 1. ✅ `ICollection<T>`
1. ✅ `IEnumerable` 1. ✅ `IEnumerable`
1. ✅ `IEnumerable<T>` 1. ✅ `IEnumerable<T>`
1. ✅ `IReadOnlyList<T>` 1. ✅ `IReadOnlyList<T>`
1. ✅ `ReadOnlySpan<T>` 1. ✅ `ReadOnlySpan<T>`
1. ✅ `RecyclableList` 1. ✅ `RecyclableList`
1. ✅ `RecyclableLongList` 1. ✅ `RecyclableLongList`
1. ✅ `Span<T>` 1. ✅ `Span<T>`
1. ✅ `T[]` 1. ✅ `T[]`
1. ✅ Release 0.0.5 1. ✅ Release 0.0.5
1. ✅ Implement `List<T>` compatibility pack for `RecyclableList<T>` 1. ✅ Implement `List<T>` compatibility pack for `RecyclableList<T>`
1. ✅ Overflow review 1. ✅ Overflow review
1. ✅ Add type casting to `long` for `<<` & `>>` operations, where required 1. ✅ Add type casting to `long` for `<<` & `>>` operations, where required
1. ✅ Make type castings `checked`, where required 1. ✅ Make type castings `checked`, where required
1. ✅ Release 0.0.6 1. ✅ Release 0.0.6
1. 👉 Implement `List<T>` compatibility pack for `RecyclableLongList<T>` 1. 👉 Implement `List<T>` compatibility pack for `RecyclableLongList<T>`
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableList<T>` 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableList<T>`
1. `IndexOf` parallelization 1. `IndexOf` parallelization
1. 🅿️ Release 0.0.7 1. 🅿️ Release 0.0.7
1. 🅿️ Implement `ILongList<T>` interface 1. 🅿️ Implement `ILongList<T>` interface
1. 🅿️ `RecyclableList<T>` 1. 🅿️ `RecyclableList<T>`
1. 🅿️ `RecyclableLongList<T>` 1. 🅿️ `RecyclableLongList<T>`
1. 🅿️ Implement `RecyclableQueue<T>` 1. 🅿️ Implement `RecyclableQueue<T>`
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableQueue<T>` 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableQueue<T>`
1. 🅿️ Release 0.0.8 1. 🅿️ Release 0.0.8
1. 🅿️ Implement `RecyclableStack<T>` 1. 🅿️ Implement `RecyclableStack<T>`
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableStack<T>` 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableStack<T>`
1. 🅿️ Release 0.0.9 1. 🅿️ Release 0.0.9
1. 🅿️ Implement `RecyclableSortedList<T>` 1. 🅿️ Implement `RecyclableSortedList<T>`
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableSortedList<T>` 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableSortedList<T>`
1. 🅿️ Release 0.0.10 1. 🅿️ Release 0.0.10
1. 🅿️ Implement `RecyclableUnorderedList<T>` 1. 🅿️ Implement `RecyclableUnorderedList<T>`
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableUnorderedList<T>` 1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableUnorderedList<T>`
1. 🅿️ Release 0.0.11 1. 🅿️ Release 0.0.11
1. 🅿️ Implement `RecyclableVersionedList<T>` 1. 🅿️ Implement `RecyclableVersionedList<T>`
1. 🅿️ Port `RecyclableList<T>` to `RecyclableVersionedList<T>` 1. 🅿️ Port `RecyclableList<T>` to `RecyclableVersionedList<T>`
1. 🅿️ Port `RecyclableLongList<T>` to `RecyclableVersionedLongList<T>` 1. 🅿️ Port `RecyclableLongList<T>` to `RecyclableVersionedLongList<T>`
1. 🅿️ Release 0.0.12 1. 🅿️ Release 0.0.12
1. 🅿️ Optimize `OneSizeArrayPool` 1. 🅿️ Optimize `OneSizeArrayPool`
1. 🅿️ Review locks 1. 🅿️ Review locks
1. 🅿️ Measure multi-threading performance 1. 🅿️ Measure multi-threading performance
1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario 1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario
1. 🅿️ Optimize `RecyclableCollectionVersionObjectPool` 1. 🅿️ Optimize `RecyclableCollectionVersionObjectPool`
1. 🅿️ Implement custom `ObjectPool<T>` 1. 🅿️ Implement custom `ObjectPool<T>`
1. 🅿️ Measure multi-threading performance 1. 🅿️ Measure multi-threading performance
1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario 1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario
1. 🅿️ Review `RecyclableArrayPool` 1. 🅿️ Review `RecyclableArrayPool`
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 array disposal in high RAM pressure scenario
1. 🅿️ Optimize `MemoryBucket<T>` 1. 🅿️ Optimize `MemoryBucket<T>`
1. 🅿️ Convert to `struct`, if possible 1. 🅿️ Convert to `struct`, if possible
1. 🅿️ Find out if there are better replacements 1. 🅿️ Find out if there are better replacements
1. 🅿️ Release 0.0.13 1. 🅿️ Release 0.0.13
1. 🅿️ Optimize 1. 🅿️ Optimize
1. 🅿️ `IndexOfSynchronizationContext` 1. 🅿️ `IndexOfSynchronizationContext`
1. 🅿️ `IndexOfSynchronizationContextPool` 1. 🅿️ `IndexOfSynchronizationContextPool`
1. 🅿️ `ManualResetEventSlimmer` 1. 🅿️ `ManualResetEventSlimmer`
1. 🅿️ Multi-threading benchmarks 1. 🅿️ Multi-threading benchmarks
1. 🅿️ `ManualResetEventSlimmerPool` 1. 🅿️ `ManualResetEventSlimmerPool`
1. 🅿️ Multi-threading benchmarks 1. 🅿️ Multi-threading benchmarks
1. 🅿️ `SpinLockSlimmer` 1. 🅿️ `SpinLockSlimmer`
1. 🅿️ Multi-threading benchmarks 1. 🅿️ Multi-threading benchmarks
1. 🅿️ Remove range operators, where possible 1. 🅿️ Remove range operators, where possible
1. 🅿️ Release 0.0.14 1. 🅿️ Release 0.0.14
1. 🅿️ Extend unit tests 1. 🅿️ Extend unit tests
1. 🅿️ `RecyclableLongListExtensions.CopyTo` 1. 🅿️ `RecyclableLongListExtensions.CopyTo`
1. 🅿️ `RecyclableLongList` cases for large items count (&gt;`int.MaxValue`) 1. 🅿️ `RecyclableLongList` cases for large items count (&gt;`int.MaxValue`)
1. 🅿️ Pools 1. 🅿️ Pools
1. 🅿️ Multi-threading 1. 🅿️ Multi-threading
1. 🅿️ Add comparison tests vs `T[]` / `List<T>` equivalents 1. 🅿️ Add comparison tests vs `T[]` / `List<T>` equivalents
1. 🅿️ Add test cases for indexes beyond items count 1. 🅿️ Add test cases for indexes beyond items count
1. 🅿️ Cover border cases 1. 🅿️ Cover border cases
1. 🅿️ Non-existing items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()` 1. 🅿️ Non-existing items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()`
1. 🅿️ Duplicated items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()` 1. 🅿️ Duplicated items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()`
1. 🅿️ Append test data in reversed order in `.AddRange()`, `.InsertRange()` and similar tests 1. 🅿️ Append test data in reversed order in `.AddRange()`, `.InsertRange()` and similar tests
1. 🅿️ Switch tests to use Source / Target data variants in `.AddRange()`, `.InsertRange()` and similar tests 1. 🅿️ Switch tests to use Source / Target data variants in `.AddRange()`, `.InsertRange()` and similar tests
1. 🅿️ Cleanup 1. 🅿️ Cleanup
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field 1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
1. 🅿️ Replace all internal properties refs with fields, where possible 1. 🅿️ Replace all internal properties refs with fields, where possible
1. ✅ `RecyclableLongListExtensions` 1. ✅ `RecyclableLongListExtensions`
1. ✅ `ListExtensions` 1. ✅ `ListExtensions`
1. 🅿️ `MathUtils` 1. 🅿️ `MathUtils`
1. 🅿️ Resolve TODOs 1. 🅿️ Resolve TODOs
1. 🅿️ Remove obsolete methods 1. 🅿️ Remove obsolete methods
1. 🅿️ Add`RecyclableLongListHelpers.Enumerate` to benchmarks 1. 🅿️ Add`RecyclableLongListHelpers.Enumerate` to benchmarks
1. 🅿️ Review all classes & add `volatile`, where needed 1. 🅿️ Review all classes & add `volatile`, where needed
1. 🅿️ Remove `expectedItemsCount` parameter from `RecyclableLongList`'s constructor 1. 🅿️ Remove `expectedItemsCount` parameter from `RecyclableLongList`'s constructor
1. 🅿️ Remove `array` slicing in favor for indexing, like in `RecyclableCompatibilityListExtensions` 1. 🅿️ Remove `array` slicing in favor for indexing, like in `RecyclableCompatibilityListExtensions`
1. 🅿️ Release 0.0.15 1. 🅿️ Release 0.0.15
1. 🅿️ Optimize 1. 🅿️ Optimize
1. 🅿️ `ListExtensions` 1. 🅿️ `ListExtensions`
1. 🅿️ `MathUtils` 1. 🅿️ `MathUtils`
1. 🅿️ `SystemRandomNumberGenerator` 1. 🅿️ `SystemRandomNumberGenerator`
1. 🅿️ `RecyclableLongListExtensions` 1. 🅿️ `RecyclableLongListExtensions`
1. 🅿️ Memory release in pools to utilize GC events in case of memory pressure 1. 🅿️ Memory release in pools to utilize GC events in case of memory pressure
1. 🅿️ Release 0.0.16 1. 🅿️ Release 0.0.16
1. ✅ Review and remove warnings & hints 1. ✅ Review and remove warnings & hints
1. ✅ Warnings 1. ✅ Warnings
1. ✅ Hints 1. ✅ Hints
1. Documentation 1. Documentation
1. 🅿️ Document the most efficient iteration over array collections 1. 🅿️ Document the most efficient iteration over array collections
1. 🅿️ Document the most efficient iteration over blocked collections 1. 🅿️ Document the most efficient iteration over blocked collections
1. 🅿️ Document differences in behavior 1. 🅿️ Document differences in behavior
1. 🅿️ Document other specifics 1. 🅿️ Document other specifics
1. 🅿️ Release 1.0.0 1. 🅿️ Release 1.0.0
1. 🅿️ Implement source code generators 1. 🅿️ Implement source code generators
1. 🅿️ Add attributes 1. 🅿️ Add attributes
1. 🅿️ `GeneratorBaseClassAttribute` for marking base class used for generation 1. 🅿️ `GeneratorBaseClassAttribute` for marking base class used for generation
1. 🅿️ `VersionedAttribute` for marking classes as versioned 1. 🅿️ `VersionedAttribute` for marking classes as versioned
1. 🅿️ `IncVersionAttribute` for marking methods & setters as resulting in version increase 1. 🅿️ `IncVersionAttribute` for marking methods & setters as resulting in version increase
1. 🅿️ Implement source code generator 1. 🅿️ Implement source code generator
1. 🅿️ Generate partial class for classes marked with `GeneratorBaseClassAttribute` 1. 🅿️ Generate partial class for classes marked with `GeneratorBaseClassAttribute`
1. 🅿️ Add support for fields 1. 🅿️ Add support for fields
1. 🅿️ Add support for properties 1. 🅿️ Add support for properties
1. 🅿️ Add support for methods 1. 🅿️ Add support for methods
1. 🅿️ Add support for constructor 1. 🅿️ Add support for constructor
1. 🅿️ Skip base fields, methods, properties etc. when they're overridden in the child class 1. 🅿️ Skip base fields, methods, properties etc. when they're overridden in the child class
1. 🅿️ Add support for `ulong` indexing 1. 🅿️ Add support for `ulong` indexing
1. 🅿️ Convert `_memoryBlocks` to `Array` to allow `ulong` lengths 1. 🅿️ Convert `_memoryBlocks` to `Array` to allow `ulong` lengths
1. 🅿️ Convert block indexes from `int` to `ulong` or `long` 1. 🅿️ Convert block indexes from `int` to `ulong` or `long`
1. 🅿️ Optimize 1. 🅿️ Optimize
1. 🅿️ `RecyclableLongList<T>.Resize` 1. 🅿️ `RecyclableLongList<T>.Resize`
1. 🅿️ `RecyclableLongList<T>.CopyTo` 1. 🅿️ `RecyclableLongList<T>.CopyTo`
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. 🅿️ 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. 🅿️ Memory pools 1. 🅿️ Memory pools
1. 🅿️ `OneSizeArrayPool<T>` 1. 🅿️ `OneSizeArrayPool<T>`
1. 🅿️ `RecyclableArrayPool<T>` 1. 🅿️ `RecyclableArrayPool<T>`
1. 🅿️ Add support for `ICollection<T>` interface in `.AddRange` & `constructor` 1. 🅿️ Add support for `ICollection<T>` interface in `.AddRange` & `constructor`
1. 🅿️ Final optimizations 1. 🅿️ Final optimizations
1. 🅿️ Replace `Math` class usages with `if` statements 1. 🅿️ Replace `Math` class usages with `if` statements
1. 🅿️ Replace `a - b > 0` & `a - b < 0` comparisons with `a > b` & `a < b` 1. 🅿️ Replace `a - b > 0` & `a - b < 0` comparisons with `a > b` & `a < b`
1. 🅿️ Replace `a + b > 0` & `a + b < 0` comparisons with `a > b` & `a < b` 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 1. 🅿️ Replace `a / b` & `a * b` calculations with equivalents, where possible
1. 🅿️ Replace virtual calls with static calls 1. 🅿️ Replace virtual calls with static calls
1. 🅿️ Separate `++number`, `number++`, `--number` & `number--` operations to their own lines (they add extra variable) 1. 🅿️ Separate `++number`, `number++`, `--number` & `number--` operations to their own lines (they add extra variable)
1. 🅿️ Replace `blockSize` sums by powers of 2, minus 1 1. 🅿️ Replace `blockSize` sums by powers of 2, minus 1
1. 🅿️ Remove type castings, if possible 1. 🅿️ Remove type castings, if possible
1. 🅿️ Convert generic methods to non-generic 1. 🅿️ Convert generic methods to non-generic
1. 🅿️ Replace integer comparisons with comparisons to 0 / 1, if possible 1. 🅿️ Replace integer comparisons with comparisons to 0 / 1, if possible
1. 🅿️ Release 2.0.0 1. 🅿️ Release 2.0.0
# Characteristics of the classes # Characteristics of the classes
## Common ## Common
* 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`. * 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`.
* Memory blocks are created in 2 ways, depending on `blockSize` value * Memory blocks are created in 2 ways, depending on `blockSize` value
* when `blockSize < 128` ⇨ blocks are created as regular `arrays`. * 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. * 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. * 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. * `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. * 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. * 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. * ⚠️ 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.
```csharp ```csharp
foreach (var item in (IRecyclableVersionedList<long>)recyclableList) foreach (var item in (IRecyclableVersionedList<long>)recyclableList)
{ {
... ...
... ...
... ...
} }
foreach (var item in (IRecyclableVersionedLongList<long>)recyclableLongList) foreach (var item in (IRecyclableVersionedLongList<long>)recyclableLongList)
{ {
... ...
... ...
... ...
} }
``` ```
* ⚠️ 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 * ⚠️ 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 * when the collection is empty, or
* when `.MoveNext()` returned `false`, after reaching the end of the collection. * when `.MoveNext()` returned `false`, after reaching the end of the collection.
* ⚠️ If you decide to use `.GetEnumerator()` / `.GetVersionedEnumerator()`, instead of relying on `foreach` loops * ⚠️ 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 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. * you MUST NOT access `.Current` property before calling `.MoveNext()` or after reaching the end of the collection.
\ \
**Failing to do so WILL result in unpredictable behavior.** **Failing to do so WILL result in unpredictable behavior.**
### [Benchmark results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md) ### [Benchmark results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md)
## `RecyclableList<T>` ## `RecyclableList<T>`
* Range: `int` * Range: `int`
* Interfaces: `IList<T>`, `IEnumerable<T>`, `IDisposable`\ * 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. 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.
## `RecyclableLongList<T>`, `RecyclableSortableList<T>`, `RecyclableQueue<T>`, `RecyclableStack<T>` ## `RecyclableLongList<T>`, `RecyclableSortableList<T>`, `RecyclableQueue<T>`, `RecyclableStack<T>`
* Range: `long` * Range: `long`
* Interfaces: `IList<T>`, `ILongList<T>`, `IEnumerable<T>`, `IDisposable` * 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. * All classes provide large storage capabilities, in practice limited only by the memory available in your system.
* 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. * 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. * 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. * ⚠️ `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 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. 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.
As `RecyclableLongList<T>` is foreseen for use-cases with big data, you should automatically benefit from it in many use-cases. As `RecyclableLongList<T>` is foreseen for use-cases with big data, you should automatically benefit from it in many use-cases.
### [Benchmark Results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md) ### [Benchmark Results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md)
# Architecture Decisions # Architecture Decisions
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 architecture of the classes included in this project is driven primarily by performance & memory footprints. The following decisions were made to support it.
## Code duplication ## 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. 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.
## Common base - `ILongList<T>` ## 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. 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.
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. 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.
## Protection against incorrect use & state ## 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. 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 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. 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.
## `this[int index]`, `Count`, `IndexOf(T item)` etc. may overflow, but... ## `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. 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.
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. 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.
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 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.
If you foresee the overflowing as an issue, you're welcomed to propose an improvement & file a PR with proposed solution. If you foresee the overflowing as an issue, you're welcomed to propose an improvement & file a PR with proposed solution.
# Auto-properties, new language features & code style # Auto-properties, new language features & code style
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. 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.
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. 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.
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. 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.
Finally, you'll find places with code commented out. That's to easily track what has been tried & what proved to perform worse. Finally, you'll find places with code commented out. That's to easily track what has been tried & what proved to perform worse.
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. 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.
# Thread Safety # Thread Safety
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. 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.
# Use # Use
All the classes included in this package are meant to be direct replacements of their corresponding system classes. All the classes included in this package are meant to be direct replacements of their corresponding system classes.
* `IList<T>` ⇨ `ILongList<T>` * `IList<T>` ⇨ `ILongList<T>`
* `List<T>` ⇨ `RecyclableLongList<T>` * `List<T>` ⇨ `RecyclableLongList<T>`
* `Queue<T>` ⇨ `RecyclableQueue<T>` * `Queue<T>` ⇨ `RecyclableQueue<T>`
* `PriorityQueue<T>` ⇨ `RecyclableSortedList<T>` * `PriorityQueue<T>` ⇨ `RecyclableSortedList<T>`
* `SortedList<T>` ⇨ `RecyclableSortedList<T>` * `SortedList<T>` ⇨ `RecyclableSortedList<T>`
* `Stack<T>` ⇨ `RecyclableStack<T>` * `Stack<T>` ⇨ `RecyclableStack<T>`
Examples of each of the above classes are provided below. Please refer automated tests for more. Examples of each of the above classes are provided below. Please refer automated tests for more.
## `RecyclableList<T>` ## `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>`. 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>`.
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. 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.
## `RecyclableLongList<T>` ## `RecyclableLongList<T>`
```CSharp ```CSharp
public void RecyclableListExample() public void RecyclableListExample()
{ {
int[] testNumbers = new[] { 1, 2, 2, 3 }; int[] testNumbers = new[] { 1, 2, 2, 3 };
// Create // Create
using var list = new RecyclableLongList<int>(); using var list = new RecyclableLongList<int>();
// Add items // Add items
foreach (var index in testNumbers) foreach (var index in testNumbers)
{ {
list.Add(index); list.Add(index);
} }
foreach (var item in list) foreach (var item in list)
{ {
Console.WriteLine(item); Console.WriteLine(item);
} }
// This will iterate over this[long index] // This will iterate over this[long index]
for (var itemIdx = 0; itemIdx <= list.LongCount; itemIdx++) for (var itemIdx = 0; itemIdx <= list.LongCount; itemIdx++)
{ {
Console.WriteLine(list[itemIdx]); Console.WriteLine(list[itemIdx]);
} }
// This will iterate over this[int index] and may overflow if there are > int.MaxValue no. of items on the list. See: Architecture Decisions // 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++) for (var itemIdx = 0; itemIdx <= list.LongCount; itemIdx++)
{ {
Console.WriteLine(list[itemIdx]); Console.WriteLine(list[itemIdx]);
} }
list.Clear(); list.Clear();
} }
``` ```
### [Benchmark results](https://github.com/mlemanczyk/Recyclable.Collections/blob/main/Recyclable.Collections.Benchmarks/Results/Benchmarks.md) ### [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>net8.0;net7.0;net6.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;
} }
} }
} }
@@ -1,20 +1,20 @@
namespace Recyclable.Collections.Pools namespace Recyclable.Collections.Pools
{ {
public sealed class BiDirectionalRecyclableArrayPoolChunk<T> public sealed class BiDirectionalRecyclableArrayPoolChunk<T>
{ {
private static readonly T[] _empty = Array.Empty<T>(); private static readonly T[] _empty = Array.Empty<T>();
internal static T[] Empty => _empty; internal static T[] Empty => _empty;
internal T[] Value; internal T[] Value;
internal int Top; internal int Top;
internal int Bottom; internal int Bottom;
internal BiDirectionalRecyclableArrayPoolChunk<T>? Previous; internal BiDirectionalRecyclableArrayPoolChunk<T>? Previous;
internal BiDirectionalRecyclableArrayPoolChunk<T>? Next; internal BiDirectionalRecyclableArrayPoolChunk<T>? Next;
public BiDirectionalRecyclableArrayPoolChunk() public BiDirectionalRecyclableArrayPoolChunk()
{ {
Value = _empty; Value = _empty;
} }
} }
} }
@@ -1,37 +1,37 @@
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Pools namespace Recyclable.Collections.Pools
{ {
public static class BiDirectionalRecyclableArrayPoolChunkPool<T> public static class BiDirectionalRecyclableArrayPoolChunkPool<T>
{ {
private static readonly ObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>> _pool = private static readonly ObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>> _pool =
new DefaultObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<BiDirectionalRecyclableArrayPoolChunk<T>>()); new DefaultObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<BiDirectionalRecyclableArrayPoolChunk<T>>());
public static BiDirectionalRecyclableArrayPoolChunk<T> Rent() => _pool.Get(); public static BiDirectionalRecyclableArrayPoolChunk<T> Rent() => _pool.Get();
public static void Return(BiDirectionalRecyclableArrayPoolChunk<T> chunk) public static void Return(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{ {
chunk.Top = 0; chunk.Top = 0;
chunk.Bottom = 0; chunk.Bottom = 0;
chunk.Previous = null; chunk.Previous = null;
chunk.Next = null; chunk.Next = null;
_pool.Return(chunk); _pool.Return(chunk);
} }
public static void Dispose(BiDirectionalRecyclableArrayPoolChunk<T> chunk, bool needsClearing) public static void Dispose(BiDirectionalRecyclableArrayPoolChunk<T> chunk, bool needsClearing)
{ {
var buffer = chunk.Value; var buffer = chunk.Value;
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength) if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing); RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
} }
chunk.Value = BiDirectionalRecyclableArrayPoolChunk<T>.Empty; chunk.Value = BiDirectionalRecyclableArrayPoolChunk<T>.Empty;
chunk.Top = 0; chunk.Top = 0;
chunk.Bottom = 0; chunk.Bottom = 0;
chunk.Previous = null; chunk.Previous = null;
chunk.Next = null; chunk.Next = null;
_pool.Return(chunk); _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);
} }
} }
} }
} }
@@ -1,19 +1,19 @@
namespace Recyclable.Collections.Pools namespace Recyclable.Collections.Pools
{ {
public sealed class RecyclableArrayPoolChunk<T> public sealed class RecyclableArrayPoolChunk<T>
{ {
private static readonly T[] _empty = Array.Empty<T>(); private static readonly T[] _empty = Array.Empty<T>();
internal static T[] Empty => _empty; internal static T[] Empty => _empty;
internal T[] Value; internal T[] Value;
internal int Index; internal int Index;
internal RecyclableArrayPoolChunk<T>? Previous; internal RecyclableArrayPoolChunk<T>? Previous;
internal RecyclableArrayPoolChunk<T>? Next; internal RecyclableArrayPoolChunk<T>? Next;
public RecyclableArrayPoolChunk() public RecyclableArrayPoolChunk()
{ {
Value = _empty; Value = _empty;
} }
} }
} }
@@ -1,34 +1,34 @@
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Pools namespace Recyclable.Collections.Pools
{ {
public static class RecyclableArrayPoolChunkPool<T> public static class RecyclableArrayPoolChunkPool<T>
{ {
private static readonly ObjectPool<RecyclableArrayPoolChunk<T>> _pool = private static readonly ObjectPool<RecyclableArrayPoolChunk<T>> _pool =
new DefaultObjectPool<RecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<RecyclableArrayPoolChunk<T>>()); new DefaultObjectPool<RecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<RecyclableArrayPoolChunk<T>>());
public static RecyclableArrayPoolChunk<T> Rent() => _pool.Get(); public static RecyclableArrayPoolChunk<T> Rent() => _pool.Get();
public static void Return(RecyclableArrayPoolChunk<T> chunk) public static void Return(RecyclableArrayPoolChunk<T> chunk)
{ {
chunk.Index = 0; chunk.Index = 0;
chunk.Previous = null; chunk.Previous = null;
chunk.Next = null; chunk.Next = null;
_pool.Return(chunk); _pool.Return(chunk);
} }
public static void Dispose(RecyclableArrayPoolChunk<T> chunk, bool needsClearing) public static void Dispose(RecyclableArrayPoolChunk<T> chunk, bool needsClearing)
{ {
var buffer = chunk.Value; var buffer = chunk.Value;
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength) if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing); RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
} }
chunk.Value = RecyclableArrayPoolChunk<T>.Empty; chunk.Value = RecyclableArrayPoolChunk<T>.Empty;
chunk.Index = 0; chunk.Index = 0;
chunk.Previous = null; chunk.Previous = null;
chunk.Next = null; chunk.Next = null;
_pool.Return(chunk); _pool.Return(chunk);
} }
} }
} }
@@ -1,42 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks> <TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<DefineConstants>$(DefineConstants);WITH_VERSIONING</DefineConstants> <DefineConstants>$(DefineConstants);WITH_VERSIONING</DefineConstants>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>Recyclable.Collections</PackageId> <PackageId>Recyclable.Collections</PackageId>
<PackageVersion>0.0.6</PackageVersion> <PackageVersion>0.0.6</PackageVersion>
<Authors>Marek Lemańczyk</Authors> <Authors>Marek Lemańczyk</Authors>
<Company>N/A</Company> <Company>N/A</Company>
<Product>Recyclable.Collections</Product> <Product>Recyclable.Collections</Product>
<PackageTags>Recyclable ArrayPool Array Pool List Sortable Sort</PackageTags> <PackageTags>Recyclable ArrayPool Array Pool List Sortable Sort</PackageTags>
<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> <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>
<PackageProjectUrl>https://github.com/mlemanczyk/Recyclable.Collections</PackageProjectUrl> <PackageProjectUrl>https://github.com/mlemanczyk/Recyclable.Collections</PackageProjectUrl>
<RepositoryUrl>https://github.com/mlemanczyk/Recyclable.Collections</RepositoryUrl> <RepositoryUrl>https://github.com/mlemanczyk/Recyclable.Collections</RepositoryUrl>
<Copyright>Marek Lemańczyk 2023</Copyright> <Copyright>Marek Lemańczyk 2023</Copyright>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile> <PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageReleaseNotes>List&lt;T&gt; compatibility pack added for RecyclableList&lt;T&gt;. Various performance improvements and fixes.</PackageReleaseNotes> <PackageReleaseNotes>List&lt;T&gt; compatibility pack added for RecyclableList&lt;T&gt;. Various performance improvements and fixes.</PackageReleaseNotes>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md"> <None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Include="..\README.md" Pack="true" PackagePath="" Link="README.md"> <None Include="..\README.md" Pack="true" PackagePath="" Link="README.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" /> <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
<PackageReference Include="morelinq" Version="3.4.2" /> <PackageReference Include="morelinq" Version="3.4.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\VersionedCollectionsGenerator\VersionedCollectionsGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> <ProjectReference Include="..\VersionedCollectionsGenerator\VersionedCollectionsGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup> </ItemGroup>
</Project> </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
+406 -406
View File
@@ -1,406 +1,406 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools; using Recyclable.Collections.Pools;
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
internal sealed class RecyclableDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IDisposable internal sealed class RecyclableDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IDisposable
where TKey : notnull where TKey : notnull
{ {
internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType; internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType;
internal readonly int _blockSize; internal readonly int _blockSize;
internal readonly int _blockSizeMinus1; internal readonly int _blockSizeMinus1;
internal readonly byte _blockShift; internal readonly byte _blockShift;
#nullable disable #nullable disable
internal Entry[][] _entries; internal Entry[][] _entries;
#nullable restore #nullable restore
internal int[] _buckets; internal int[] _buckets;
internal int _count; internal int _count;
internal bool _disposed; internal bool _disposed;
internal struct Entry internal struct Entry
{ {
public int HashCode; public int HashCode;
public int Next; public int Next;
public TKey Key; public TKey Key;
public TValue Value; public TValue Value;
} }
public RecyclableDictionary(int blockSize = RecyclableDefaults.BlockSize) public RecyclableDictionary(int blockSize = RecyclableDefaults.BlockSize)
{ {
if (!BitOperations.IsPow2((uint)blockSize)) if (!BitOperations.IsPow2((uint)blockSize))
{ {
blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize); blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize);
} }
_blockSize = blockSize; _blockSize = blockSize;
_blockSizeMinus1 = blockSize - 1; _blockSizeMinus1 = blockSize - 1;
_blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize)); _blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize));
_entries = 4 >= RecyclableDefaults.MinPooledArrayLength _entries = 4 >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(4) ? RecyclableArrayPool<Entry[]>.RentShared(4)
: new Entry[4][]; : new Entry[4][];
_entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength _entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(blockSize) ? RecyclableArrayPool<Entry>.RentShared(blockSize)
: new Entry[blockSize]; : new Entry[blockSize];
_buckets = new int[4]; _buckets = new int[4];
Array.Fill(_buckets, -1); Array.Fill(_buckets, -1);
} }
public int Count => _count; public int Count => _count;
public TValue this[TKey key] public TValue this[TKey key]
{ {
get get
{ {
var index = FindIndex(key, out _); var index = FindIndex(key, out _);
if (index >= 0) if (index >= 0)
{ {
return GetEntry(index).Value; return GetEntry(index).Value;
} }
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "Key not found"); ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "Key not found");
return default!; return default!;
} }
set set
{ {
var index = FindIndex(key, out _); var index = FindIndex(key, out _);
if (index >= 0) if (index >= 0)
{ {
ref var entry = ref GetEntry(index); ref var entry = ref GetEntry(index);
entry.Value = value; entry.Value = value;
return; return;
} }
Add(key, value); Add(key, value);
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(TKey key, TValue value) public void Add(TKey key, TValue value)
{ {
var hash = key.GetHashCode() & int.MaxValue; var hash = key.GetHashCode() & int.MaxValue;
if (_count >= (_buckets.Length * 3) / 4) if (_count >= (_buckets.Length * 3) / 4)
{ {
ResizeBuckets(_buckets.Length * 2); ResizeBuckets(_buckets.Length * 2);
} }
EnsureCapacity(_count + 1); EnsureCapacity(_count + 1);
int bucket = hash & (_buckets.Length - 1); int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket]; int index = _buckets[bucket];
while (index >= 0) while (index >= 0)
{ {
ref var check = ref GetEntry(index); ref var check = ref GetEntry(index);
if (check.HashCode == hash && EqualityComparer<TKey>.Default.Equals(check.Key, key)) if (check.HashCode == hash && EqualityComparer<TKey>.Default.Equals(check.Key, key))
{ {
throw new ArgumentException("An element with the same key already exists.", nameof(key)); throw new ArgumentException("An element with the same key already exists.", nameof(key));
} }
index = check.Next; index = check.Next;
} }
int newIndex = _count++; int newIndex = _count++;
ref var entry = ref GetEntry(newIndex); ref var entry = ref GetEntry(newIndex);
entry.HashCode = hash; entry.HashCode = hash;
entry.Key = key; entry.Key = key;
entry.Value = value; entry.Value = value;
entry.Next = _buckets[bucket]; entry.Next = _buckets[bucket];
_buckets[bucket] = newIndex; _buckets[bucket] = newIndex;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(TKey key) => FindIndex(key, out _) >= 0; public bool ContainsKey(TKey key) => FindIndex(key, out _) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TKey key, out TValue value) public bool TryGetValue(TKey key, out TValue value)
{ {
var index = FindIndex(key, out _); var index = FindIndex(key, out _);
if (index >= 0) if (index >= 0)
{ {
value = GetEntry(index).Value; value = GetEntry(index).Value;
return true; return true;
} }
value = default!; value = default!;
return false; return false;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(TKey key) public bool Remove(TKey key)
{ {
if (_count == 0) if (_count == 0)
{ {
return false; return false;
} }
var hash = key.GetHashCode() & int.MaxValue; var hash = key.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1); int bucket = hash & (_buckets.Length - 1);
int prev = -1; int prev = -1;
int index = _buckets[bucket]; int index = _buckets[bucket];
while (index >= 0) while (index >= 0)
{ {
ref var entry = ref GetEntry(index); ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key)) if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key))
{ {
if (prev < 0) if (prev < 0)
{ {
_buckets[bucket] = entry.Next; _buckets[bucket] = entry.Next;
} }
else else
{ {
GetEntry(prev).Next = entry.Next; GetEntry(prev).Next = entry.Next;
} }
int lastIndex = _count - 1; int lastIndex = _count - 1;
if (index != lastIndex) if (index != lastIndex)
{ {
MoveEntry(lastIndex, index); MoveEntry(lastIndex, index);
} }
ClearEntry(lastIndex); ClearEntry(lastIndex);
_count--; _count--;
return true; return true;
} }
prev = index; prev = index;
index = entry.Next; index = entry.Next;
} }
return false; return false;
} }
public void Clear() public void Clear()
{ {
if (_count == 0) if (_count == 0)
{ {
Array.Fill(_buckets, -1); Array.Fill(_buckets, -1);
return; return;
} }
int blockIndex = 0; int blockIndex = 0;
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength) if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
{ {
while (blockIndex < _entries.Length && _entries[blockIndex] != null) while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{ {
RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing); RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing);
_entries[blockIndex++] = null!; _entries[blockIndex++] = null!;
} }
} }
else else
{ {
if (_needsClearing) if (_needsClearing)
{ {
while (blockIndex < _entries.Length && _entries[blockIndex] != null) while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{ {
Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length); Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length);
_entries[blockIndex++] = null!; _entries[blockIndex++] = null!;
} }
} }
else else
{ {
while (blockIndex < _entries.Length && _entries[blockIndex] != null) while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{ {
_entries[blockIndex++] = null!; _entries[blockIndex++] = null!;
} }
} }
} }
_count = 0; _count = 0;
Array.Fill(_buckets, -1); Array.Fill(_buckets, -1);
} }
public TKey GetKey(int index) => GetEntry(index).Key; public TKey GetKey(int index) => GetEntry(index).Key;
public TValue GetValue(int index) => GetEntry(index).Value; public TValue GetValue(int index) => GetEntry(index).Value;
public Enumerator GetEnumerator() => new(this); public Enumerator GetEnumerator() => new(this);
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => new Enumerator(this); IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
public void Dispose() public void Dispose()
{ {
if (_disposed) if (_disposed)
{ {
return; return;
} }
Clear(); Clear();
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength) if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false); RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
} }
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength) if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<int>.ReturnShared(_buckets, false); RecyclableArrayPool<int>.ReturnShared(_buckets, false);
} }
_entries = Array.Empty<Entry[]>(); _entries = Array.Empty<Entry[]>();
_buckets = Array.Empty<int>(); _buckets = Array.Empty<int>();
_disposed = true; _disposed = true;
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
private ref Entry GetEntry(int index) private ref Entry GetEntry(int index)
{ {
return ref _entries[index >> _blockShift][index & _blockSizeMinus1]; return ref _entries[index >> _blockShift][index & _blockSizeMinus1];
} }
private void ClearEntry(int index) private void ClearEntry(int index)
{ {
ref var entry = ref GetEntry(index); ref var entry = ref GetEntry(index);
entry.HashCode = 0; entry.HashCode = 0;
entry.Next = -1; entry.Next = -1;
if (_needsClearing) if (_needsClearing)
{ {
entry.Key = default!; entry.Key = default!;
entry.Value = default!; entry.Value = default!;
} }
} }
private void MoveEntry(int sourceIndex, int targetIndex) private void MoveEntry(int sourceIndex, int targetIndex)
{ {
ref var source = ref GetEntry(sourceIndex); ref var source = ref GetEntry(sourceIndex);
ref var target = ref GetEntry(targetIndex); ref var target = ref GetEntry(targetIndex);
target = source; target = source;
int bucket = source.HashCode & (_buckets.Length - 1); int bucket = source.HashCode & (_buckets.Length - 1);
int cur = _buckets[bucket]; int cur = _buckets[bucket];
int prev = -1; int prev = -1;
while (cur != sourceIndex) while (cur != sourceIndex)
{ {
prev = cur; prev = cur;
cur = GetEntry(cur).Next; cur = GetEntry(cur).Next;
} }
if (prev < 0) if (prev < 0)
{ {
_buckets[bucket] = targetIndex; _buckets[bucket] = targetIndex;
} }
else else
{ {
GetEntry(prev).Next = targetIndex; GetEntry(prev).Next = targetIndex;
} }
} }
private void EnsureCapacity(int min) private void EnsureCapacity(int min)
{ {
int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift; int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift;
if (_entries.Length < requiredBlocks) if (_entries.Length < requiredBlocks)
{ {
var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks) ? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks)
: new Entry[requiredBlocks][]; : new Entry[requiredBlocks][];
Array.Copy(_entries, newBlocks, _entries.Length); Array.Copy(_entries, newBlocks, _entries.Length);
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength) if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false); RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
} }
_entries = newBlocks; _entries = newBlocks;
} }
for (int i = 0; i < requiredBlocks; i++) for (int i = 0; i < requiredBlocks; i++)
{ {
if (_entries[i] == null) if (_entries[i] == null)
{ {
_entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength _entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(_blockSize) ? RecyclableArrayPool<Entry>.RentShared(_blockSize)
: new Entry[_blockSize]; : new Entry[_blockSize];
} }
} }
} }
private void ResizeBuckets(int newSize) private void ResizeBuckets(int newSize)
{ {
var newBuckets = new int[newSize]; var newBuckets = new int[newSize];
Array.Fill(newBuckets, -1); Array.Fill(newBuckets, -1);
for (int i = 0; i < _count; i++) for (int i = 0; i < _count; i++)
{ {
ref var entry = ref GetEntry(i); ref var entry = ref GetEntry(i);
int bucket = entry.HashCode & (newSize - 1); int bucket = entry.HashCode & (newSize - 1);
entry.Next = newBuckets[bucket]; entry.Next = newBuckets[bucket];
newBuckets[bucket] = i; newBuckets[bucket] = i;
} }
_buckets = newBuckets; _buckets = newBuckets;
} }
private int FindIndex(TKey key, out int hash) private int FindIndex(TKey key, out int hash)
{ {
if (_count == 0) if (_count == 0)
{ {
hash = 0; hash = 0;
return -1; return -1;
} }
hash = key.GetHashCode() & int.MaxValue; hash = key.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1); int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket]; int index = _buckets[bucket];
while (index >= 0) while (index >= 0)
{ {
ref var entry = ref GetEntry(index); ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key)) if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key))
{ {
return index; return index;
} }
index = entry.Next; index = entry.Next;
} }
return -1; return -1;
} }
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
{ {
private readonly RecyclableDictionary<TKey, TValue> _dictionary; private readonly RecyclableDictionary<TKey, TValue> _dictionary;
private int _index; private int _index;
private KeyValuePair<TKey, TValue> _current; private KeyValuePair<TKey, TValue> _current;
internal Enumerator(RecyclableDictionary<TKey, TValue> dictionary) internal Enumerator(RecyclableDictionary<TKey, TValue> dictionary)
{ {
_dictionary = dictionary; _dictionary = dictionary;
_index = 0; _index = 0;
_current = default; _current = default;
} }
public KeyValuePair<TKey, TValue> Current => _current; public KeyValuePair<TKey, TValue> Current => _current;
object IEnumerator.Current => _current; object IEnumerator.Current => _current;
public bool MoveNext() public bool MoveNext()
{ {
if (_index >= _dictionary._count) if (_index >= _dictionary._count)
{ {
return false; return false;
} }
var entry = _dictionary.GetEntry(_index++); var entry = _dictionary.GetEntry(_index++);
_current = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value); _current = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
return true; return true;
} }
public void Reset() public void Reset()
{ {
_index = 0; _index = 0;
_current = default; _current = default;
} }
public void Dispose() public void Dispose()
{ {
} }
} }
} }
} }
File diff suppressed because it is too large. Load diff
+362 -362
View File
@@ -1,362 +1,362 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools; using Recyclable.Collections.Pools;
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
internal sealed class RecyclableHashSet<T> : IEnumerable<T>, IDisposable internal sealed class RecyclableHashSet<T> : IEnumerable<T>, IDisposable
where T : notnull where T : notnull
{ {
internal static readonly bool _needsClearing = !typeof(T).IsValueType; internal static readonly bool _needsClearing = !typeof(T).IsValueType;
internal readonly int _blockSize; internal readonly int _blockSize;
internal readonly int _blockSizeMinus1; internal readonly int _blockSizeMinus1;
internal readonly byte _blockShift; internal readonly byte _blockShift;
#nullable disable #nullable disable
internal Entry[][] _entries; internal Entry[][] _entries;
#nullable restore #nullable restore
internal int[] _buckets; internal int[] _buckets;
internal int _count; internal int _count;
internal bool _disposed; internal bool _disposed;
internal struct Entry internal struct Entry
{ {
public int HashCode; public int HashCode;
public int Next; public int Next;
public T Value; public T Value;
} }
public RecyclableHashSet(int blockSize = RecyclableDefaults.BlockSize) public RecyclableHashSet(int blockSize = RecyclableDefaults.BlockSize)
{ {
if (!BitOperations.IsPow2((uint)blockSize)) if (!BitOperations.IsPow2((uint)blockSize))
{ {
blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize); blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize);
} }
_blockSize = blockSize; _blockSize = blockSize;
_blockSizeMinus1 = blockSize - 1; _blockSizeMinus1 = blockSize - 1;
_blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize)); _blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize));
_entries = 4 >= RecyclableDefaults.MinPooledArrayLength _entries = 4 >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(4) ? RecyclableArrayPool<Entry[]>.RentShared(4)
: new Entry[4][]; : new Entry[4][];
_entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength _entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(blockSize) ? RecyclableArrayPool<Entry>.RentShared(blockSize)
: new Entry[blockSize]; : new Entry[blockSize];
_buckets = new int[4]; _buckets = new int[4];
Array.Fill(_buckets, -1); Array.Fill(_buckets, -1);
} }
public int Count => _count; public int Count => _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add(T item) public bool Add(T item)
{ {
var hash = item.GetHashCode() & int.MaxValue; var hash = item.GetHashCode() & int.MaxValue;
if (_count >= (_buckets.Length * 3) / 4) if (_count >= (_buckets.Length * 3) / 4)
{ {
ResizeBuckets(_buckets.Length * 2); ResizeBuckets(_buckets.Length * 2);
} }
EnsureCapacity(_count + 1); EnsureCapacity(_count + 1);
int bucket = hash & (_buckets.Length - 1); int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket]; int index = _buckets[bucket];
while (index >= 0) while (index >= 0)
{ {
ref var check = ref GetEntry(index); ref var check = ref GetEntry(index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, item)) if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, item))
{ {
return false; return false;
} }
index = check.Next; index = check.Next;
} }
int newIndex = _count++; int newIndex = _count++;
ref var entry = ref GetEntry(newIndex); ref var entry = ref GetEntry(newIndex);
entry.HashCode = hash; entry.HashCode = hash;
entry.Value = item; entry.Value = item;
entry.Next = _buckets[bucket]; entry.Next = _buckets[bucket];
_buckets[bucket] = newIndex; _buckets[bucket] = newIndex;
return true; return true;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(T item) => FindIndex(item, out _) >= 0; public bool Contains(T item) => FindIndex(item, out _) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(T item) public bool Remove(T item)
{ {
if (_count == 0) if (_count == 0)
{ {
return false; return false;
} }
var hash = item.GetHashCode() & int.MaxValue; var hash = item.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1); int bucket = hash & (_buckets.Length - 1);
int prev = -1; int prev = -1;
int index = _buckets[bucket]; int index = _buckets[bucket];
while (index >= 0) while (index >= 0)
{ {
ref var entry = ref GetEntry(index); ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item)) if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item))
{ {
if (prev < 0) if (prev < 0)
{ {
_buckets[bucket] = entry.Next; _buckets[bucket] = entry.Next;
} }
else else
{ {
GetEntry(prev).Next = entry.Next; GetEntry(prev).Next = entry.Next;
} }
int lastIndex = _count - 1; int lastIndex = _count - 1;
if (index != lastIndex) if (index != lastIndex)
{ {
MoveEntry(lastIndex, index); MoveEntry(lastIndex, index);
} }
ClearEntry(lastIndex); ClearEntry(lastIndex);
_count--; _count--;
return true; return true;
} }
prev = index; prev = index;
index = entry.Next; index = entry.Next;
} }
return false; return false;
} }
public void Clear() public void Clear()
{ {
if (_count == 0) if (_count == 0)
{ {
Array.Fill(_buckets, -1); Array.Fill(_buckets, -1);
return; return;
} }
int blockIndex = 0; int blockIndex = 0;
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength) if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
{ {
while (blockIndex < _entries.Length && _entries[blockIndex] != null) while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{ {
RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing); RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing);
_entries[blockIndex++] = null!; _entries[blockIndex++] = null!;
} }
} }
else else
{ {
if (_needsClearing) if (_needsClearing)
{ {
while (blockIndex < _entries.Length && _entries[blockIndex] != null) while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{ {
Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length); Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length);
_entries[blockIndex++] = null!; _entries[blockIndex++] = null!;
} }
} }
else else
{ {
while (blockIndex < _entries.Length && _entries[blockIndex] != null) while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{ {
_entries[blockIndex++] = null!; _entries[blockIndex++] = null!;
} }
} }
} }
_count = 0; _count = 0;
Array.Fill(_buckets, -1); Array.Fill(_buckets, -1);
} }
public Enumerator GetEnumerator() => new(this); public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this); IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
public void Dispose() public void Dispose()
{ {
if (_disposed) if (_disposed)
{ {
return; return;
} }
Clear(); Clear();
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength) if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false); RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
} }
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength) if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<int>.ReturnShared(_buckets, false); RecyclableArrayPool<int>.ReturnShared(_buckets, false);
} }
_entries = Array.Empty<Entry[]>(); _entries = Array.Empty<Entry[]>();
_buckets = Array.Empty<int>(); _buckets = Array.Empty<int>();
_disposed = true; _disposed = true;
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
private ref Entry GetEntry(int index) private ref Entry GetEntry(int index)
{ {
return ref _entries[index >> _blockShift][index & _blockSizeMinus1]; return ref _entries[index >> _blockShift][index & _blockSizeMinus1];
} }
private void ClearEntry(int index) private void ClearEntry(int index)
{ {
ref var entry = ref GetEntry(index); ref var entry = ref GetEntry(index);
entry.HashCode = 0; entry.HashCode = 0;
entry.Next = -1; entry.Next = -1;
if (_needsClearing) if (_needsClearing)
{ {
entry.Value = default!; entry.Value = default!;
} }
} }
private void MoveEntry(int sourceIndex, int targetIndex) private void MoveEntry(int sourceIndex, int targetIndex)
{ {
ref var source = ref GetEntry(sourceIndex); ref var source = ref GetEntry(sourceIndex);
ref var target = ref GetEntry(targetIndex); ref var target = ref GetEntry(targetIndex);
target = source; target = source;
int bucket = source.HashCode & (_buckets.Length - 1); int bucket = source.HashCode & (_buckets.Length - 1);
int cur = _buckets[bucket]; int cur = _buckets[bucket];
int prev = -1; int prev = -1;
while (cur != sourceIndex) while (cur != sourceIndex)
{ {
prev = cur; prev = cur;
cur = GetEntry(cur).Next; cur = GetEntry(cur).Next;
} }
if (prev < 0) if (prev < 0)
{ {
_buckets[bucket] = targetIndex; _buckets[bucket] = targetIndex;
} }
else else
{ {
GetEntry(prev).Next = targetIndex; GetEntry(prev).Next = targetIndex;
} }
} }
private void EnsureCapacity(int min) private void EnsureCapacity(int min)
{ {
int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift; int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift;
if (_entries.Length < requiredBlocks) if (_entries.Length < requiredBlocks)
{ {
var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks) ? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks)
: new Entry[requiredBlocks][]; : new Entry[requiredBlocks][];
Array.Copy(_entries, newBlocks, _entries.Length); Array.Copy(_entries, newBlocks, _entries.Length);
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength) if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false); RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
} }
_entries = newBlocks; _entries = newBlocks;
} }
for (int i = 0; i < requiredBlocks; i++) for (int i = 0; i < requiredBlocks; i++)
{ {
if (_entries[i] == null) if (_entries[i] == null)
{ {
_entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength _entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(_blockSize) ? RecyclableArrayPool<Entry>.RentShared(_blockSize)
: new Entry[_blockSize]; : new Entry[_blockSize];
} }
} }
} }
private void ResizeBuckets(int newSize) private void ResizeBuckets(int newSize)
{ {
int[] newBuckets = newSize >= RecyclableDefaults.MinPooledArrayLength int[] newBuckets = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<int>.RentShared(newSize) ? RecyclableArrayPool<int>.RentShared(newSize)
: new int[newSize]; : new int[newSize];
Array.Fill(newBuckets, -1); Array.Fill(newBuckets, -1);
for (int i = 0; i < _count; i++) for (int i = 0; i < _count; i++)
{ {
ref var entry = ref GetEntry(i); ref var entry = ref GetEntry(i);
int bucket = entry.HashCode & (newSize - 1); int bucket = entry.HashCode & (newSize - 1);
entry.Next = newBuckets[bucket]; entry.Next = newBuckets[bucket];
newBuckets[bucket] = i; newBuckets[bucket] = i;
} }
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength) if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<int>.ReturnShared(_buckets, false); RecyclableArrayPool<int>.ReturnShared(_buckets, false);
} }
_buckets = newBuckets; _buckets = newBuckets;
} }
private int FindIndex(T item, out int hash) private int FindIndex(T item, out int hash)
{ {
if (_count == 0) if (_count == 0)
{ {
hash = 0; hash = 0;
return -1; return -1;
} }
hash = item.GetHashCode() & int.MaxValue; hash = item.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1); int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket]; int index = _buckets[bucket];
while (index >= 0) while (index >= 0)
{ {
ref var entry = ref GetEntry(index); ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item)) if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item))
{ {
return index; return index;
} }
index = entry.Next; index = entry.Next;
} }
return -1; return -1;
} }
public struct Enumerator : IEnumerator<T> public struct Enumerator : IEnumerator<T>
{ {
private readonly RecyclableHashSet<T> _set; private readonly RecyclableHashSet<T> _set;
private int _index; private int _index;
private T? _current; private T? _current;
internal Enumerator(RecyclableHashSet<T> set) internal Enumerator(RecyclableHashSet<T> set)
{ {
_set = set; _set = set;
_index = 0; _index = 0;
_current = default; _current = default;
} }
public T Current => _current!; public T Current => _current!;
object IEnumerator.Current => _current!; object IEnumerator.Current => _current!;
public bool MoveNext() public bool MoveNext()
{ {
if (_index >= _set._count) if (_index >= _set._count)
{ {
return false; return false;
} }
_current = _set.GetEntry(_index++).Value; _current = _set.GetEntry(_index++).Value;
return true; return true;
} }
public void Reset() public void Reset()
{ {
_index = 0; _index = 0;
_current = default; _current = default;
} }
public void Dispose() public void Dispose()
{ {
} }
} }
} }
} }
File diff suppressed because it is too large. Load diff
+323 -323
View File
@@ -1,323 +1,323 @@
using System.Collections; using System.Collections;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools; using Recyclable.Collections.Pools;
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
internal sealed class RecyclableLinkedList<T> : IEnumerable<T>, IDisposable internal sealed class RecyclableLinkedList<T> : IEnumerable<T>, IDisposable
{ {
internal static readonly bool _needsClearing = !typeof(T).IsValueType; internal static readonly bool _needsClearing = !typeof(T).IsValueType;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous, bool forHead) private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous, bool forHead)
{ {
var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent(); var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent();
if (chunk.Value.Length < size) if (chunk.Value.Length < size)
{ {
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength) if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing); RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
} }
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(size) ? RecyclableArrayPool<T>.RentShared(size)
: new T[size]; : new T[size];
} }
if (forHead) if (forHead)
{ {
chunk.Top = size; chunk.Top = size;
chunk.Bottom = size; chunk.Bottom = size;
} }
else else
{ {
chunk.Top = 0; chunk.Top = 0;
chunk.Bottom = 0; chunk.Bottom = 0;
} }
chunk.Previous = previous; chunk.Previous = previous;
chunk.Next = null; chunk.Next = null;
return chunk; return chunk;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReturnChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk) private static void ReturnChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{ {
BiDirectionalRecyclableArrayPoolChunkPool<T>.Return(chunk); BiDirectionalRecyclableArrayPoolChunkPool<T>.Return(chunk);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DisposeChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk) private static void DisposeChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{ {
BiDirectionalRecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing); BiDirectionalRecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing);
} }
internal BiDirectionalRecyclableArrayPoolChunk<T> _head; internal BiDirectionalRecyclableArrayPoolChunk<T> _head;
internal BiDirectionalRecyclableArrayPoolChunk<T> _tail; internal BiDirectionalRecyclableArrayPoolChunk<T> _tail;
internal bool _disposed; internal bool _disposed;
internal long _capacity; internal long _capacity;
internal long _count; internal long _count;
public RecyclableLinkedList(int initialCapacity = RecyclableDefaults.InitialCapacity) public RecyclableLinkedList(int initialCapacity = RecyclableDefaults.InitialCapacity)
{ {
if (initialCapacity < 1) if (initialCapacity < 1)
{ {
initialCapacity = 1; initialCapacity = 1;
} }
if (!BitOperations.IsPow2((uint)initialCapacity)) if (!BitOperations.IsPow2((uint)initialCapacity))
{ {
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity); initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
} }
_head = RentChunk(initialCapacity, null, false); _head = RentChunk(initialCapacity, null, false);
_tail = _head; _tail = _head;
_capacity = initialCapacity; _capacity = initialCapacity;
_count = 0; _count = 0;
} }
public RecyclableLinkedList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity) public RecyclableLinkedList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity)
{ {
foreach (var item in source) foreach (var item in source)
{ {
AddLast(item); AddLast(item);
} }
} }
public int Count => checked((int)_count); public int Count => checked((int)_count);
public long LongCount => _count; public long LongCount => _count;
public void AddLast(T item) public void AddLast(T item)
{ {
if (_tail.Top == _tail.Value.Length) if (_tail.Top == _tail.Value.Length)
{ {
GrowTail(); GrowTail();
} }
_tail.Value[_tail.Top++] = item; _tail.Value[_tail.Top++] = item;
_count++; _count++;
} }
public void AddFirst(T item) public void AddFirst(T item)
{ {
if (_head.Bottom == 0) if (_head.Bottom == 0)
{ {
GrowHead(); GrowHead();
} }
_head.Value[--_head.Bottom] = item; _head.Value[--_head.Bottom] = item;
_count++; _count++;
} }
public T RemoveFirst() public T RemoveFirst()
{ {
if (_count == 0) if (_count == 0)
{ {
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty"); ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
} }
_count--; _count--;
var value = _head.Value[_head.Bottom]; var value = _head.Value[_head.Bottom];
if (_needsClearing) if (_needsClearing)
{ {
_head.Value[_head.Bottom] = default!; _head.Value[_head.Bottom] = default!;
} }
_head.Bottom++; _head.Bottom++;
if (_head.Bottom == _head.Top && _head.Next != null) if (_head.Bottom == _head.Top && _head.Next != null)
{ {
ReleaseHeadChunk(); ReleaseHeadChunk();
} }
else if (_head.Bottom == _head.Top) else if (_head.Bottom == _head.Top)
{ {
_head.Bottom = _head.Top; _head.Bottom = _head.Top;
} }
return value; return value;
} }
public T RemoveLast() public T RemoveLast()
{ {
if (_count == 0) if (_count == 0)
{ {
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty"); ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
} }
_count--; _count--;
_tail.Top--; _tail.Top--;
var value = _tail.Value[_tail.Top]; var value = _tail.Value[_tail.Top];
if (_needsClearing) if (_needsClearing)
{ {
_tail.Value[_tail.Top] = default!; _tail.Value[_tail.Top] = default!;
} }
if (_tail.Top == _tail.Bottom && _tail.Previous != null) if (_tail.Top == _tail.Bottom && _tail.Previous != null)
{ {
ReleaseTailChunk(); ReleaseTailChunk();
} }
else if (_tail.Top == _tail.Bottom) else if (_tail.Top == _tail.Bottom)
{ {
_tail.Top = _tail.Bottom; _tail.Top = _tail.Bottom;
} }
return value; return value;
} }
public void Clear() public void Clear()
{ {
while (_head.Next != null) while (_head.Next != null)
{ {
ReleaseHeadChunk(); ReleaseHeadChunk();
} }
while (_tail.Previous != null) while (_tail.Previous != null)
{ {
ReleaseTailChunk(); ReleaseTailChunk();
} }
if (_needsClearing && _head.Top > _head.Bottom) if (_needsClearing && _head.Top > _head.Bottom)
{ {
Array.Clear(_head.Value, _head.Bottom, _head.Top - _head.Bottom); Array.Clear(_head.Value, _head.Bottom, _head.Top - _head.Bottom);
} }
_capacity = _head.Value.Length; _capacity = _head.Value.Length;
_head.Bottom = _head.Top; _head.Bottom = _head.Top;
_tail = _head; _tail = _head;
_count = 0; _count = 0;
} }
public Enumerator GetEnumerator() => new(this); public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this); IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
private void GrowTail() private void GrowTail()
{ {
int newSize; int newSize;
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize) if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
{ {
newSize = RecyclableDefaults.MaxPooledBlockSize; newSize = RecyclableDefaults.MaxPooledBlockSize;
} }
else else
{ {
var doubled = _capacity << 1; var doubled = _capacity << 1;
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize); newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
} }
var newChunk = RentChunk(newSize, _tail, false); var newChunk = RentChunk(newSize, _tail, false);
_tail.Next = newChunk; _tail.Next = newChunk;
_tail = newChunk; _tail = newChunk;
_capacity += newSize; _capacity += newSize;
} }
private void GrowHead() private void GrowHead()
{ {
int newSize; int newSize;
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize) if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
{ {
newSize = RecyclableDefaults.MaxPooledBlockSize; newSize = RecyclableDefaults.MaxPooledBlockSize;
} }
else else
{ {
var doubled = _capacity << 1; var doubled = _capacity << 1;
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize); newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
} }
var newChunk = RentChunk(newSize, null, true); var newChunk = RentChunk(newSize, null, true);
newChunk.Next = _head; newChunk.Next = _head;
_head.Previous = newChunk; _head.Previous = newChunk;
_head = newChunk; _head = newChunk;
_capacity += newSize; _capacity += newSize;
} }
private void ReleaseHeadChunk() private void ReleaseHeadChunk()
{ {
var toReturn = _head; var toReturn = _head;
_head = toReturn.Next!; _head = toReturn.Next!;
_head.Previous = null; _head.Previous = null;
_capacity -= toReturn.Value.Length; _capacity -= toReturn.Value.Length;
ReturnChunk(toReturn); ReturnChunk(toReturn);
} }
private void ReleaseTailChunk() private void ReleaseTailChunk()
{ {
var toReturn = _tail; var toReturn = _tail;
_tail = toReturn.Previous!; _tail = toReturn.Previous!;
_tail.Next = null; _tail.Next = null;
_capacity -= toReturn.Value.Length; _capacity -= toReturn.Value.Length;
ReturnChunk(toReturn); ReturnChunk(toReturn);
} }
public void Dispose() public void Dispose()
{ {
if (_disposed) if (_disposed)
{ {
return; return;
} }
var chunk = _head; var chunk = _head;
while (chunk != null) while (chunk != null)
{ {
var next = chunk.Next; var next = chunk.Next;
DisposeChunk(chunk); DisposeChunk(chunk);
chunk = next; chunk = next;
} }
_disposed = true; _disposed = true;
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public struct Enumerator : IEnumerator<T> public struct Enumerator : IEnumerator<T>
{ {
private readonly BiDirectionalRecyclableArrayPoolChunk<T>? _start; private readonly BiDirectionalRecyclableArrayPoolChunk<T>? _start;
private BiDirectionalRecyclableArrayPoolChunk<T>? _chunk; private BiDirectionalRecyclableArrayPoolChunk<T>? _chunk;
private int _index; private int _index;
private T? _current; private T? _current;
internal Enumerator(RecyclableLinkedList<T> list) internal Enumerator(RecyclableLinkedList<T> list)
{ {
_start = list._head; _start = list._head;
_chunk = list._head; _chunk = list._head;
_index = _chunk != null ? _chunk.Bottom : 0; _index = _chunk != null ? _chunk.Bottom : 0;
_current = default; _current = default;
} }
public T Current => _current!; public T Current => _current!;
object IEnumerator.Current => _current!; object IEnumerator.Current => _current!;
public bool MoveNext() public bool MoveNext()
{ {
if (_chunk == null) if (_chunk == null)
{ {
return false; return false;
} }
while (_index >= _chunk.Top) while (_index >= _chunk.Top)
{ {
_chunk = _chunk.Next; _chunk = _chunk.Next;
if (_chunk == null) if (_chunk == null)
{ {
return false; return false;
} }
_index = _chunk.Bottom; _index = _chunk.Bottom;
} }
_current = _chunk.Value[_index++]; _current = _chunk.Value[_index++];
return true; return true;
} }
public void Reset() public void Reset()
{ {
_chunk = _start; _chunk = _start;
_index = _chunk != null ? _chunk.Bottom : 0; _index = _chunk != null ? _chunk.Bottom : 0;
} }
public void Dispose() public void Dispose()
{ {
_chunk = null; _chunk = null;
_current = default; _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