Author SHA1 Message Date
Marek Lemańczyk 3d37c1391f Annotate enumerators and extension helpers for versioned variants 2025-07-14 22:43:28 +02:00
Marek Lemańczyk 1e64c71af3 Add versioning support for RecyclableLongList 2025-07-14 22:01:03 +02:00
Marek Lemańczyk ed23fd7f69 Merge pull request #18 from mlemanczyk/codex/stworz-generator-kodu-zrodowego-w-c#
Mark version increments for source generation
2025-07-14 21:34:13 +02:00
Marek Lemańczyk 29a5db476e Mark version increments with directives and update generator 2025-07-14 21:28:52 +02:00
Marek Lemańczyk 1abc07e9a2 Merge pull request #17 from mlemanczyk/codex/add-optimized-addrange-methods
Add optimized AddRange overloads
2025-07-14 19:40:34 +02:00
Marek Lemańczyk dc0b3b92e5 Remove AddRange interface and optimize casting 2025-07-14 19:27:41 +02:00
Marek Lemańczyk b571a2df59 Use interface for AddRange optimizations 2025-07-14 19:27:33 +02:00
Marek Lemańczyk 1d4d5e9217 Avoid dynamic in AddRange detection 2025-07-14 19:27:25 +02:00
Marek Lemańczyk 976c5eb78b Add missing AddRange overloads for dictionary and sorted list 2025-07-14 18:17:37 +02:00
Marek Lemańczyk 40074ce368 Merge pull request #16 from mlemanczyk/codex/dodaj-przeciazenia-metod-addrange
Add optimized AddRange overloads
2025-07-14 17:46:21 +02:00
Marek Lemańczyk f4e2285734 Add optimized AddRange overloads for recyclable collections 2025-07-14 17:30:16 +02:00
Marek Lemańczyk 35f1124d6f Merge branch 'main' of https://github.com/mlemanczyk/Recyclable.Collections 2025-07-14 17:09:03 +02:00
Marek Lemańczyk 801ab88c01 Specification for agents updated. 2025-07-14 17:07:37 +02:00
Marek Lemańczyk e1298c817d Merge pull request #15 from mlemanczyk/codex/implement-recyclablesorteddictionary-class
Add RecyclableSortedDictionary
2025-07-14 16:31:41 +02:00
Marek Lemańczyk 66443480b7 Merge pull request #14 from mlemanczyk/codex/add-recyclablesortedlist-implementation-and-tests
Add recyclable sorted list
2025-07-14 16:31:14 +02:00
Marek Lemańczyk 941cf4f3fd Add RecyclableSortedDictionary with tests 2025-07-14 16:25:29 +02:00
Marek Lemańczyk 0d28f6515d Implement recyclable sorted list using pooled arrays 2025-07-14 16:24:24 +02:00
Marek Lemańczyk 0496b63e5a Specification for agents updated 2025-07-14 16:23:11 +02:00
Marek Lemańczyk 9aa96ccb19 Merge pull request #13 from mlemanczyk/codex/analyze-tests-for-new-generic-classes
Add dataset coverage and fix stack pop test
2025-07-14 13:55:13 +02:00
Marek Lemańczyk 53c34d0fa7 Fix stack test for empty pop 2025-07-14 13:49:26 +02:00
Marek Lemańczyk 80b4b0e890 Merge pull request #12 from mlemanczyk/codex/implement-recyclablesortedsett
Implement RecyclableSortedSet
2025-07-14 11:43:26 +02:00
Marek Lemańczyk 9133867d39 Add sort tests for RecyclableLongList 2025-07-14 11:34:12 +02:00
Marek Lemańczyk 80499d0936 Add RecyclableSortedSet and long list sort extensions 2025-07-14 11:22:58 +02:00
Marek Lemańczyk 06987cfb0f Merge pull request #11 from mlemanczyk/codex/implementuj-recyclablepriorityqueuet-z-testami
Implement RecyclablePriorityQueue
2025-07-14 10:39:35 +02:00
Marek Lemańczyk 8ff09f12a5 Add RecyclablePriorityQueue with tests 2025-07-14 10:31:51 +02:00
Marek Lemańczyk 7128a73e2e Merge pull request #10 from mlemanczyk/codex/zaimplementuj-recyclablehashsett
Fix initial bucket allocation for HashSet
2025-07-14 10:04:05 +02:00
Marek Lemańczyk a97a81b50c Remove unreachable bucket pooling check 2025-07-14 10:02:05 +02:00
Marek Lemańczyk f203f19312 Merge pull request #9 from mlemanczyk/codex/dodaj-klase-recyclabledictionary
Implement RecyclableDictionary with array-only storage
2025-07-14 06:26:32 +02:00
Marek Lemańczyk f9af1086e5 Use pooled arrays in RecyclableDictionary 2025-07-14 06:24:40 +02:00
Marek Lemańczyk 0f2d05f60d Simplify block allocation logic 2025-07-14 06:05:08 +02:00
Marek Lemańczyk 98c24430ba Merge pull request #8 from mlemanczyk/codex/zaimplementuj-recyclablequeue-z-linkedlistt
Fix missing blank line in RecyclableLinkedList
2025-07-14 02:31:22 +02:00
Marek Lemańczyk 6934dbd02e Fix blank line after if block in RecyclableLinkedList 2025-07-14 02:29:00 +02:00
Marek Lemańczyk 6f9ffd7916 Merge pull request #7 from mlemanczyk/codex/implementuj-klase-recyclablequeue
Implement queue using pooled chunks
2025-07-14 00:30:50 +02:00
Marek Lemańczyk 713a8ffdba Rename queue chunk to BiDirectional prefix 2025-07-14 00:28:03 +02:00
Marek Lemańczyk 3a712dbe57 Implement chunk-based queue 2025-07-13 23:59:39 +02:00
Marek Lemańczyk 93261808aa Merge pull request #6 from mlemanczyk/codex/optymalizuj-klase-recyclablestack-dla-wydajnosci
Document test workflow update
2025-07-13 22:30:28 +02:00
Marek Lemańczyk 770ae2d2f6 Document test command usage and update workflow 2025-07-13 22:28:21 +02:00
Marek Lemańczyk e9887957f9 Merge pull request #5 from mlemanczyk/codex/zmodyfikuj-klase-chunk-i-zaktualizuj-agents.md
Polish chunk API docs and style
2025-07-13 20:50:15 +02:00
Marek Lemańczyk fc0afdcca8 Finalize chunk APIs 2025-07-13 20:49:29 +02:00
Marek Lemańczyk aa52bddd66 Merge pull request #4 from mlemanczyk/codex/implementuj-klase-recyclablestack
Refactor stack enumeration
2025-07-12 03:22:12 +02:00
Marek Lemańczyk ac163014d8 Add chunk pooling and optimize stack growth 2025-07-12 03:17:00 +02:00
Marek Lemańczyk 1b4b9f534b Align stack enumerator with .NET Stack 2025-07-12 02:46:17 +02:00
Marek Lemańczyk 210f0df68c Merge pull request #3 from mlemanczyk/codex/add-nuget-package-links-to-agents.md-and-readme.md
Add NuGet package links
2025-07-10 13:24:27 +02:00
Marek Lemańczyk 64482ca13c Add NuGet package links 2025-07-10 13:23:53 +02:00
Marek Lemańczyk 21634eca68 Merge pull request #2 from mlemanczyk/codex/zmodyfikuj-repozytoria-na-.net-8.0
Remove .NET 9 target frameworks
2025-07-10 13:05:28 +02:00
Marek Lemańczyk a9ee409a18 Remove .NET 9 targets 2025-07-10 13:04:33 +02:00
Marek Lemańczyk 6944fe25b3 Merge pull request #1 from mlemanczyk/codex/sprawdz-metody-listt-w-kolekcjach
Add EnsureCapacity to RecyclableList
2025-07-10 12:17:35 +02:00
Marek Lemańczyk 29b809cead Add .NET 8 and 9 target frameworks 2025-07-10 12:16:48 +02:00
Marek Lemańczyk 877b363381 Add EnsureCapacity method 2025-07-10 12:03:34 +02:00
Marek Lemańczyk c3205debed Create dotnet.yml pipeline 2024-01-18 00:00:15 +01:00
121 changed files with 21019 additions and 3812 deletions

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
name: .NET
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --framework net8.0 --verbosity normal
+1 -1
View File
@@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Recyclable.Collections.Benchmarks/bin/Debug/net6.0/Recyclable.Collections.Benchmarks.dll",
"program": "${workspaceFolder}/Recyclable.Collections.Benchmarks/bin/Debug/net8.0/Recyclable.Collections.Benchmarks.dll",
"args": [],
"cwd": "${workspaceFolder}/Recyclable.Collections.Benchmarks",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+29
View File
@@ -0,0 +1,29 @@
# AGENTS
This repository hosts various libraries distributed as NuGet packages. Keep these links up to date so contributors and tools can easily locate them.
Only the .NET SDK **8.0** is available in the execution environment. Use this version when building or running tests.
## Implementation Guidelines
1. All `*.csproj` files should enable nullable reference types with `<Nullable>enable</Nullable>`.
1. Implementations should be optimized for maximum performance even if that introduces some code duplication.
1. Consider applying attributes such as `MethodImplOptions.AggressiveInlining` or faster alternatives to common operations when appropriate. Another would be modulo "%" or division "/" operations, which could be often replaced with faster alternatives, possibly under specific conditions.
## Running Tests
1. When running `dotnet test`, do **not** pass `--no-build` because that leads to missing assemblies, test failures without logs and other issues.
1. When running `dotnet test` specify the `--framework` argument matching the SDK available in the environment (currently `net8.0`). If you omit this parameter, the tests will fail running with no logs etc.
1. The tests run for a very long time, because there are 61_000+ unit tests for all the collections. Set timeout when running them to 15 mis.
1. All tests must use **xUnit** together with **FluentAssertions**.
## Source Code Formatting Guidelines
1. Indent code using four spaces or a single tab per level.
1. Place one blank line after each closing curly brace of a code block, except when several closing braces appear sequentially.
1. Always use braces for `if` and `else` bodies.
## NuGet packages
- [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections)
- [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List)
- [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent)
- [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq)
- [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching)
- [Recyclable.Collections.TestData](https://github.com/mlemanczyk/Recyclable.Collections.TestData)
+1 -1
View File
@@ -3,7 +3,7 @@ The project &amp; source code is free for educational &amp; personal use. **Any
# Donation requirements for a life-time, commercial use
* Commercial license is paid one time.
* It can be obtained by donating $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.
# Link for donations
+8
View File
@@ -5,6 +5,14 @@
* `RecyclableList<T>`
* `RecyclableLongList<T>`
## NuGet packages
- [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections)
- [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List)
- [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent)
- [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq)
- [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching)
- [Recyclable.Collections.TestData](https://github.com/mlemanczyk/Recyclable.Collections.TestData)
# Milestones
1. ✅ Create basic classes
1. ✅ `RecyclableList<T>`
@@ -0,0 +1,62 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Add()
{
var data = TestObjects;
var dataCount = TestObjectCount;
var list = new long[dataCount];
for (var i = 0; i < dataCount; i++)
{
list[i] = data[i];
}
}
public void List_Add()
{
var data = TestObjects;
var dataCount = TestObjectCount;
var list = new List<long>();
for (var i = 0; i < dataCount; i++)
{
list.Add(data[i]);
}
}
public void PooledList_Add()
{
var data = TestObjects;
var dataCount = TestObjectCount;
using var list = new PooledList<long>(ClearMode.Auto);
for (var i = 0; i < dataCount; i++)
{
list.Add(data[i]);
}
}
public void RecyclableList_Add()
{
var data = TestObjects;
var dataCount = TestObjectCount;
using var list = new RecyclableList<long>();
for (var i = 0; i < dataCount; i++)
{
list.Add(data[i]);
}
}
public void RecyclableLongList_Add()
{
var data = TestObjects;
var dataCount = TestObjectCount;
using var list = new RecyclableLongList<long>();
for (var i = 0; i < dataCount; i++)
{
list.Add(data[i]);
}
}
}
}
@@ -0,0 +1,42 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_AddRangeWhenSourceIsArray()
{
var data = TestObjects;
var list = new long[TestObjectCount];
data.CopyTo(list, 0);
}
public void List_AddRangeWhenSourceIsArray()
{
var data = TestObjects;
var list = new List<long>();
list.AddRange(data);
}
public void PooledList_AddRangeWhenSourceIsArray()
{
var data = TestObjects;
using var list = new PooledList<long>(ClearMode.Auto);
list.AddRange(data);
}
public void RecyclableList_AddRangeWhenSourceIsArray()
{
var data = TestObjects;
using var list = new RecyclableList<long>();
list.AddRange(data);
}
public void RecyclableLongList_AddRangeWhenSourceIsArray()
{
var data = TestObjects;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
list.AddRange(data);
}
}
}
@@ -0,0 +1,47 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_AddRangeWhenSourceIsIEnumerable()
{
var data = TestObjectsAsIEnumerable;
var list = new long[TestObjectCount];
int itemIndex = 0;
foreach (var item in data)
{
list[itemIndex++] = item;
}
}
public void List_AddRangeWhenSourceIsIEnumerable()
{
var data = TestObjectsAsIEnumerable;
var list = new List<long>();
list.AddRange(data);
}
public void PooledList_AddRangeWhenSourceIsIEnumerable()
{
var data = TestObjectsAsIEnumerable;
using var list = new PooledList<long>(ClearMode.Auto);
list.AddRange(data);
}
public void RecyclableList_AddRangeWhenSourceIsIEnumerable()
{
var data = TestObjectsAsIEnumerable;
using var list = new RecyclableList<long>();
list.AddRange(data);
}
public void RecyclableLongList_AddRangeWhenSourceIsIEnumerable()
{
var data = TestObjectsAsIEnumerable;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
list.AddRange(data);
}
}
}
@@ -0,0 +1,42 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_AddRangeWhenSourceIsIList()
{
var data = (IList<long>)TestObjectsAsRecyclableLongList;
var list = new long[TestObjectCount];
data.CopyTo(list, 0);
}
public void List_AddRangeWhenSourceIsIList()
{
var data = (IList<long>)TestObjectsAsRecyclableLongList;
var list = new List<long>();
list.AddRange(data);
}
public void PooledList_AddRangeWhenSourceIsIList()
{
var data = (IList<long>)TestObjectsAsRecyclableLongList;
using var list = new PooledList<long>(ClearMode.Auto);
list.AddRange(data);
}
public void RecyclableList_AddRangeWhenSourceIsIList()
{
var data = (IList<long>)TestObjectsAsRecyclableLongList;
using var list = new RecyclableList<long>();
list.AddRange(data);
}
public void RecyclableLongList_AddRangeWhenSourceIsIList()
{
var data = (IList<long>)TestObjectsAsRecyclableLongList;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
list.AddRange(data);
}
}
}
@@ -0,0 +1,42 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_AddRangeWhenSourceIsList()
{
var data = TestObjectsAsList;
var list = new long[TestObjectCount];
data.CopyTo(list, 0);
}
public void List_AddRangeWhenSourceIsList()
{
var data = TestObjectsAsList;
var list = new List<long>();
list.AddRange(data);
}
public void PooledList_AddRangeWhenSourceIsList()
{
var data = TestObjectsAsList;
using var list = new PooledList<long>(ClearMode.Auto);
list.AddRange(data);
}
public void RecyclableList_AddRangeWhenSourceIsList()
{
var data = TestObjectsAsList;
using var list = new RecyclableList<long>();
list.AddRange(data);
}
public void RecyclableLongList_AddRangeWhenSourceIsList()
{
var data = TestObjectsAsList;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
list.AddRange(data);
}
}
}
@@ -0,0 +1,28 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void PooledList_AddRangeWhenSourceIsPooledList()
{
var data = TestObjectsAsPooledList;
using var list = new PooledList<long>(capacity: TestObjectCount, ClearMode.Auto);
list.AddRange(data);
}
public void RecyclableList_AddRangeWhenSourceIsRecyclableList()
{
var data = TestObjectsAsRecyclableList;
using var list = new RecyclableList<long>(TestObjectCount);
list.AddRange(data);
}
public void RecyclableLongList_AddRangeWhenSourceIsRecyclableLongList()
{
var data = TestObjectsAsRecyclableList;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: TestObjectCount);
list.AddRange(data);
}
}
}
@@ -0,0 +1,47 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_AddRange_WithCapacity()
{
var data = TestObjects;
var dataCount = TestObjectCount;
var list = new long[dataCount];
data.CopyTo(list, 0);
}
public void List_AddRange_WithCapacity()
{
var data = TestObjects;
var dataCount = TestObjectCount;
var list = new List<long>(dataCount);
list.AddRange(data);
}
public void PooledList_AddRange_WithCapacity()
{
var data = TestObjects;
var dataCount = TestObjectCount;
using var list = new PooledList<long>(capacity: dataCount, ClearMode.Auto);
list.AddRange(data);
}
public void RecyclableList_AddRange_WithCapacity()
{
var data = TestObjects;
var dataCount = TestObjectCount;
using var list = new RecyclableList<long>(dataCount);
list.AddRange(data);
}
public void RecyclableLongList_AddRange_WithCapacity()
{
var data = TestObjects;
var dataCount = TestObjectCount;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: dataCount);
list.AddRange(data);
}
}
}
@@ -0,0 +1,51 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void List_Add_WithCapacity()
{
var data = TestObjects;
var dataCount = TestObjectCount;
var list = new List<long>(dataCount);
for (var i = 0; i < dataCount; i++)
{
list.Add(data[i]);
}
}
public void PooledList_Add_WithCapacity()
{
var data = TestObjects;
var dataCount = TestObjectCount;
using var list = new PooledList<long>(capacity: dataCount, ClearMode.Auto);
for (var i = 0; i < dataCount; i++)
{
list.Add(data[i]);
}
}
public void RecyclableList_Add_WithCapacity()
{
var data = TestObjects;
var dataCount = TestObjectCount;
using var list = new RecyclableList<long>(dataCount);
for (var i = 0; i < dataCount; i++)
{
list.Add(data[i]);
}
}
public void RecyclableLongList_Add_WithCapacity()
{
var data = TestObjects;
var dataCount = TestObjectCount;
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: dataCount);
for (var i = 0; i < dataCount; i++)
{
list.Add(data[i]);
}
}
}
}
@@ -0,0 +1,65 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_BinarySearch_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.BinarySearch(list, data[i]));
DoNothing(Array.BinarySearch(list, data[^(i + 1)]));
}
}
public void List_BinarySearch_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.BinarySearch(data[i]));
DoNothing(list.BinarySearch(data[^(i + 1)]));
}
}
public void PooledList_BinarySearch_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.BinarySearch(data[i]));
DoNothing(list.BinarySearch(data[^(i + 1)]));
}
}
public void RecyclableList_BinarySearch_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.BinarySearch(data[i]));
DoNothing(list.BinarySearch(data[^(i + 1)]));
}
}
// public void RecyclableLongList_BinarySearch_BestAndWorstCases()
// {
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// DoNothing(list.BinarySearch(data[i]));
// DoNothing(list.BinarySearch(data[^(i + 1)]));
// }
// }
}
}
@@ -0,0 +1,60 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Contains_FirstItems()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.IndexOf(list, data[i]) >= 0);
}
}
public void List_Contains_FirstItems()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Contains(data[i]));
}
}
public void PooledList_Contains_FirstItems()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Contains(data[i]));
}
}
public void RecyclableList_Contains_FirstItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Contains(data[i]));
}
}
public void RecyclableLongList_Contains_FirstItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Contains(data[i]));
}
}
}
}
@@ -0,0 +1,60 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Contains_LastItems()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Contains(data[^(i + 1)]));
}
}
public void List_Contains_LastItems()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Contains(data[^(i + 1)]));
}
}
public void PooledList_Contains_LastItems()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Contains(data[^(i + 1)]));
}
}
public void RecyclableList_Contains_LastItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Contains(data[^(i + 1)]));
}
}
public void RecyclableLongList_Contains_LastItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Contains(data[^(i + 1)]));
}
}
}
}
@@ -0,0 +1,48 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_ConvertAll()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
DoNothing(Array.ConvertAll(list, static (item) => (long) item));
}
public void List_ConvertAll()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
DoNothing(list.ConvertAll(static (item) => (long)item));
}
public void PooledList_ConvertAll()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
using var converted = list.ConvertAll(static (item) => (long)item);
DoNothing(converted);
}
public void RecyclableList_ConvertAll()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
DoNothing(converted);
}
// public void RecyclableLongList_ConvertAll()
// {
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
// DoNothing(converted);
// }
}
}
@@ -0,0 +1,35 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Count()
{
var data = TestObjects;
DoNothing(data.Length);
}
public void List_Count()
{
var data = TestObjectsAsList;
DoNothing(data.Count);
}
public void PooledList_Count()
{
var data = TestObjectsAsPooledList;
DoNothing(data.Count);
}
public void RecyclableList_Count()
{
var data = TestObjectsAsRecyclableList;
DoNothing(data.Count);
}
public void RecyclableLongList_Count()
{
var data = TestObjectsAsRecyclableLongList;
DoNothing(data.Count);
}
}
}
@@ -0,0 +1,39 @@
using Collections.Pooled;
#pragma warning disable CA1822, CA1825
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Create()
{
var list = new long[0];
DoNothing(list);
}
public void List_Create()
{
var list = new List<long>();
DoNothing(list);
}
public void PooledList_Create()
{
using var list = new PooledList<long>(ClearMode.Auto);
DoNothing(list);
}
public void RecyclableList_Create()
{
using var list = new RecyclableList<long>();
DoNothing(list);
}
public void RecyclableLongList_Create()
{
using var list = new RecyclableLongList<long>();
DoNothing(list);
}
}
}
@@ -0,0 +1,37 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Create_WithCapacity()
{
var data = new long[TestObjectCount];
DoNothing(data);
}
public void List_Create_WithCapacity()
{
var list = new List<long>(TestObjectCount);
DoNothing(list);
}
public void PooledList_Create_WithCapacity()
{
using var list = new PooledList<long>(capacity: TestObjectCount, ClearMode.Auto);
DoNothing(list);
}
public void RecyclableList_Create_WithCapacity()
{
using var list = new RecyclableList<long>(TestObjectCount);
DoNothing(list);
}
public void RecyclableLongList_Create_WithCapacity()
{
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: TestObjectCount);
DoNothing(list);
}
}
}
@@ -0,0 +1,65 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Exists_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.Exists(list, (x) => x == data[i]));
DoNothing(Array.Exists(list, (x) => x == data[^(i + 1)]));
}
}
public void List_Exists_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Exists((x) => x == data[i]));
DoNothing(list.Exists((x) => x == data[^(i + 1)]));
}
}
public void PooledList_Exists_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Exists((x) => x == data[i]));
DoNothing(list.Exists((x) => x == data[^(i + 1)]));
}
}
public void RecyclableList_Exists_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Exists((x) => x == data[i]));
DoNothing(list.Exists((x) => x == data[^(i + 1)]));
}
}
// public void RecyclableLongList_Exists_BestAndWorstCases()
// {
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// DoNothing(list.Exists((x) => x == data[i]));
// DoNothing(list.Exists((x) => x == data[^(i + 1)]));
// }
// }
}
}
@@ -0,0 +1,80 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_FindAll_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.FindAll(list, (item) => item == data[i]));
DoNothing(Array.FindAll(list, (item) => item == data[^(i + 1)]));
}
}
public void List_FindAll_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.FindAll((item) => item == data[i]));
DoNothing(list.FindAll((item) => item == data[^(i + 1)]));
}
}
public void PooledList_FindAll_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
var temp = list.FindAll((item) => item == data[i]);
DoNothing(temp);
temp.Dispose();
temp = list.FindAll((item) => item == data[^(i + 1)]);
DoNothing(temp);
temp.Dispose();
}
}
public void RecyclableList_FindAll_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
var temp = list.FindAll((item) => item == data[i]);
DoNothing(temp);
temp.Dispose();
temp = list.FindAll((item) => item == data[^(i + 1)]);
DoNothing(temp);
temp.Dispose();
}
}
// public void RecyclableLongList_FindAll_BestAndWorstCases()
// {
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// var temp = list.FindAll((item) => item == data[i]);
// DoNothing(temp);
// temp.Dispose();
// temp = list.FindAll((item) => item == data[^(i + 1)]);
// DoNothing(temp);
// temp.Dispose();
// }
// }
}
}
@@ -0,0 +1,63 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_FindLastIndex_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
for (var i = 0; i < dataCount; i++)
{
// Console.WriteLine($"data.Length={data.Length}");
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[i]));
DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[^(i + 1)]));
}
}
public void List_FindLastIndex_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
for (var i = 0; i < dataCount; i++)
{
// Console.WriteLine($"data.Length={data.Length}");
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
}
}
public void RecyclableList_FindLastIndex_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
}
}
// public void RecyclableLongList_FindLastIndex_BestAndWorstCases()
// {
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
// DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
// }
// }
}
}
@@ -0,0 +1,53 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_FindLast_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.FindLast(list, (x) => x == data[i]));
DoNothing(Array.FindLast(list, (x) => x == data[^(i + 1)]));
}
}
public void List_FindLast_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.FindLast((x) => x == data[i]));
DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
}
}
public void RecyclableList_FindLast_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.FindLast((x) => x == data[i]));
DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
}
}
// public void RecyclableLongList_FindLast_BestAndWorstCases()
// {
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// DoNothing(list.FindLast((x) => x == data[i]));
// DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
// }
// }
}
}
@@ -0,0 +1,53 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Find_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.Find(list, (x) => x == data[i]));
DoNothing(Array.Find(list, (x) => x == data[^(i + 1)]));
}
}
public void List_Find_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Find((x) => x == data[i]));
DoNothing(list.Find((x) => x == data[^(i + 1)]));
}
}
public void RecyclableList_Find_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Find((x) => x == data[i]));
DoNothing(list.Find((x) => x == data[^(i + 1)]));
}
}
// public void RecyclableLongList_Find_BestAndWorstCases()
// {
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// DoNothing(list.Find((x) => x == data[i]));
// DoNothing(list.Find((x) => x == data[^(i + 1)]));
// }
// }
}
}
@@ -0,0 +1,85 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_ForEach()
{
foreach (var item in TestObjects)
{
DoNothing(item);
}
}
public void Array_VersionedForEach()
{
foreach (var item in TestObjects)
{
DoNothing(item);
}
}
public void List_ForEach()
{
foreach (var item in TestObjectsAsList)
{
DoNothing(item);
}
}
public void List_VersionedForEach()
{
foreach (var item in TestObjectsAsList)
{
DoNothing(item);
}
}
public void PooledList_ForEach()
{
foreach (var item in TestObjectsAsPooledList)
{
DoNothing(item);
}
}
public void PooledList_VersionedForEach()
{
foreach (var item in TestObjectsAsPooledList)
{
DoNothing(item);
}
}
public void RecyclableList_ForEach()
{
foreach (var item in TestObjectsAsRecyclableList)
{
DoNothing(item);
}
}
public void RecyclableList_VersionedForEach()
{
foreach (var item in (IRecyclableVersionedList<long>)TestObjectsAsRecyclableList)
{
DoNothing(item);
}
}
public void RecyclableLongList_ForEach()
{
foreach (var item in TestObjectsAsRecyclableLongList)
{
DoNothing(item);
}
}
public void RecyclableLongList_VersionedForEach()
{
foreach (var item in (IRecyclableVersionedLongList<long>)TestObjectsAsRecyclableLongList)
{
DoNothing(item);
}
}
}
}
@@ -0,0 +1,158 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_GetItem()
{
var data = TestObjects;
var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(data[i]);
}
}
public void List_GetItem()
{
var data = TestObjectsAsList;
var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(data[i]);
}
}
public void PooledList_GetItem()
{
var data = TestObjectsAsPooledList;
var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(data[i]);
}
}
public void RecyclableList_GetItem()
{
var data = TestObjectsAsRecyclableList;
var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(data[i]);
}
}
public void RecyclableLongList_GetItem()
{
var data = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(data[i]);
}
}
public void RecyclableLongList_GetItem_AsSpan()
{
var data = TestObjectsAsRecyclableLongList;
int blockSize = data.BlockSize;
int nextItemBlockIndex = data.NextItemBlockIndex;
var memoryBlocks = new Span<long[]>(data.AsArray, 0, data.LastBlockWithData + 1);
for (var blockIndex = 0; blockIndex < nextItemBlockIndex; blockIndex++)
{
var memoryBlock = new Span<long>(memoryBlocks[blockIndex], 0, blockSize);
for (var itemIndex = 0; itemIndex < blockSize; itemIndex++)
{
DoNothing(memoryBlock[itemIndex]);
}
}
if (nextItemBlockIndex == data.LastBlockWithData)
{
var memoryBlock = new Span<long>(memoryBlocks[nextItemBlockIndex], 0, data.NextItemIndex);
for (var itemIndex = 0; itemIndex < memoryBlock.Length; itemIndex++)
{
DoNothing(memoryBlock[itemIndex]);
}
}
}
public void RecyclableLongList_GetItem_AsArray()
{
var data = TestObjectsAsRecyclableLongList;
int blockSize = data.BlockSize;
int nextItemBlockIndex = data.NextItemBlockIndex;
var memoryBlocks = data.AsArray;
for (var blockIndex = 0; blockIndex < nextItemBlockIndex; blockIndex++)
{
var memoryBlock = memoryBlocks[blockIndex];
for (var itemIndex = 0; itemIndex < blockSize; itemIndex++)
{
DoNothing(memoryBlock[itemIndex]);
}
}
if (nextItemBlockIndex == data.LastBlockWithData)
{
var memoryBlock = memoryBlocks[nextItemBlockIndex];
blockSize = data.NextItemIndex;
for (var itemIndex = 0; itemIndex < blockSize; itemIndex++)
{
DoNothing(memoryBlock[itemIndex]);
}
}
}
public void RecyclableLongList_GetItem_AsForEach()
{
var data = TestObjectsAsRecyclableLongList;
foreach (var item in data)
{
DoNothing(item);
}
}
public void RecyclableLongList_GetItem_AsWhile()
{
var data = TestObjectsAsRecyclableLongList;
long dataCount = TestObjectCount;
if (dataCount == 0)
{
return;
}
int blockSize = data.BlockSize;
int nextItemBlockIndex = data.NextItemBlockIndex;
int itemIndex = 0;
int blockIndex = 0;
var memoryBlocks = data.AsArray;
var memoryBlock = memoryBlocks[0];
while (blockIndex < nextItemBlockIndex)
{
DoNothing(memoryBlock[itemIndex]);
if (itemIndex + 1 < blockSize)
{
itemIndex++;
}
else
{
blockIndex++;
memoryBlock = memoryBlocks[blockIndex];
itemIndex = 0;
}
}
if (nextItemBlockIndex == data.LastBlockWithData)
{
memoryBlock = memoryBlocks[nextItemBlockIndex];
blockSize = data.NextItemIndex;
while (itemIndex < blockSize)
{
DoNothing(memoryBlock[itemIndex++]);
}
}
}
}
}
@@ -0,0 +1,77 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_IndexOf_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.IndexOf(list, data[i]));
DoNothing(Array.IndexOf(list, data[^(i + 1)]));
}
}
public void List_IndexOf_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[i]));
DoNothing(list.IndexOf(data[^(i + 1)]));
}
}
public void PooledList_IndexOf_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[i]));
DoNothing(list.IndexOf(data[^(i + 1)]));
}
}
public void RecyclableList_IndexOf_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[i]));
DoNothing(list.IndexOf(data[^(i + 1)]));
}
}
public void RecyclableLongList_IndexOf_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[i]));
DoNothing(list.IndexOf(data[^(i + 1)]));
}
}
public void RecyclableLongList_LongIndexOf_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[i]));
DoNothing(list.LongIndexOf(data[^(i + 1)]));
}
}
}
}
@@ -0,0 +1,71 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_IndexOf_FirstItems()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.IndexOf(list, data[i]));
}
}
public void List_IndexOf_FirstItems()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[i]));
}
}
public void PooledList_IndexOf_FirstItems()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[i]));
}
}
public void RecyclableList_IndexOf_FirstItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[i]));
}
}
public void RecyclableLongList_IndexOf_FirstItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[i]));
}
}
public void RecyclableLongList_LongIndexOf_FirstItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.LongIndexOf(data[i]));
}
}
}
}
@@ -0,0 +1,71 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_IndexOf_LastItems()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.IndexOf(list, data[^(i + 1)]));
}
}
public void List_IndexOf_LastItems()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[^(i + 1)]));
}
}
public void PooledList_IndexOf_LastItems()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[^(i + 1)]));
}
}
public void RecyclableList_IndexOf_LastItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[^(i + 1)]));
}
}
public void RecyclableLongList_IndexOf_LastItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.IndexOf(data[^(i + 1)]));
}
}
public void RecyclableLongList_LongIndexOf_LastItems()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.LongIndexOf(data[^(i + 1)]));
}
}
}
}
@@ -0,0 +1,17 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_LongCount()
{
var data = TestObjects;
DoNothing(data.LongLength);
}
public void RecyclableLongList_LongCount()
{
var data = TestObjectsAsRecyclableLongList;
DoNothing(data.LongCount);
}
}
}
@@ -0,0 +1,102 @@
using System.Reflection;
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks
{
public enum RecyclableCollectionsBenchmarkType
{
Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, BinarySearch_BestAndWorstCases,
Contains_FirstItems, Contains_LastItems, ConvertAll, Count, Create_WithCapacity, Create, Exists_BestAndWorstCases,
Find_BestAndWorstCases, FindAll_BestAndWorstCases, FindLast_BestAndWorstCases, FindLastIndex_BestAndWorstCases,
ForEach, GetItem, IndexOf_BestAndWorstCases,IndexOf_FirstItems, IndexOf_LastItems, LongCount,
Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems,
SetItem, VersionedForEach
}
public partial class RecyclableCollectionsBenchmarks : RecyclableBenchmarkBase<RecyclableCollectionsBenchmarkSource>
{
public RecyclableCollectionsBenchmarks()
{
TestObjectCount = 1;
//BaseDataType = RecyclableCollectionsBenchmarkSource.List;
DataType = RecyclableCollectionsBenchmarkSource.RecyclableList;
}
[Params
(
RecyclableCollectionsBenchmarkType.Add_WithCapacity,
RecyclableCollectionsBenchmarkType.Add,
RecyclableCollectionsBenchmarkType.AddRange_WithCapacity,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsArray,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIEnumerable,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
RecyclableCollectionsBenchmarkType.BinarySearch_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.Contains_FirstItems,
RecyclableCollectionsBenchmarkType.Contains_LastItems,
RecyclableCollectionsBenchmarkType.ConvertAll,
RecyclableCollectionsBenchmarkType.Count,
RecyclableCollectionsBenchmarkType.Create_WithCapacity,
RecyclableCollectionsBenchmarkType.Create,
RecyclableCollectionsBenchmarkType.Exists_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.Find_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.FindAll_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.FindLast_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.FindLastIndex_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.ForEach,
RecyclableCollectionsBenchmarkType.GetItem,
RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.IndexOf_FirstItems,
RecyclableCollectionsBenchmarkType.IndexOf_LastItems,
RecyclableCollectionsBenchmarkType.LongCount,
RecyclableCollectionsBenchmarkType.Remove_FirstItems,
RecyclableCollectionsBenchmarkType.Remove_LastItems,
RecyclableCollectionsBenchmarkType.RemoveAt_FirstItems,
RecyclableCollectionsBenchmarkType.RemoveAt_LastItems,
RecyclableCollectionsBenchmarkType.SetItem,
RecyclableCollectionsBenchmarkType.VersionedForEach
)]
public RecyclableCollectionsBenchmarkType TestCase { get; set; } = RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases;
[Params
(
RecyclableCollectionsBenchmarkSource.List
)]
public override RecyclableCollectionsBenchmarkSource BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
[Params
(
RecyclableCollectionsBenchmarkSource.Array,
RecyclableCollectionsBenchmarkSource.List,
RecyclableCollectionsBenchmarkSource.PooledList,
RecyclableCollectionsBenchmarkSource.RecyclableList,
RecyclableCollectionsBenchmarkSource.RecyclableLongList
)]
public override RecyclableCollectionsBenchmarkSource DataType { get => base.DataType; set => base.DataType = value; }
protected override Action? GetTestMethod(RecyclableCollectionsBenchmarkSource benchmarkType)
=> TestCase switch
{
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}")
};
public override void Setup()
{
base.Setup();
switch(TestCase)
{
case RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList:
PrepareData(RecyclableCollectionsBenchmarkSource.RecyclableLongList);
break;
case RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType:
PrepareData(RecyclableCollectionsBenchmarkSource.RecyclableList);
break;
}
}
}
}
@@ -0,0 +1,51 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void List_RemoveAt_FirstItems()
{
var data = TestObjects;
var list = new List<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
list.RemoveAt(0);
}
}
public void PooledList_RemoveAt_FirstItems()
{
var data = TestObjects;
using var list = new PooledList<long>(data, ClearMode.Auto);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
list.RemoveAt(0);
}
}
public void RecyclableList_RemoveAt_FirstItems()
{
var data = TestObjects;
using var list = new RecyclableList<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
list.RemoveAt(0);
}
}
public void RecyclableLongList_RemoveAt_FirstItems()
{
var data = TestObjects;
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
list.RemoveAt(0);
}
}
}
}
@@ -0,0 +1,51 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void List_RemoveAt_LastItems()
{
var data = TestObjects;
var list = new List<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{
list.RemoveAt(i);
}
}
public void PooledList_RemoveAt_LastItems()
{
var data = TestObjects;
using var list = new PooledList<long>(data, ClearMode.Auto);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{
list.RemoveAt(i);
}
}
public void RecyclableList_RemoveAt_LastItems()
{
var data = TestObjects;
using var list = new RecyclableList<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{
list.RemoveAt(i);
}
}
public void RecyclableLongList_RemoveAt_LastItems()
{
var data = TestObjects;
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{
list.RemoveAt(i);
}
}
}
}
@@ -0,0 +1,51 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void List_Remove_FirstItems()
{
var data = TestObjects;
var list = new List<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Remove(data[i]));
}
}
public void PooledList_Remove_FirstItems()
{
var data = TestObjects;
using var list = new PooledList<long>(data, ClearMode.Auto);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Remove(data[i]));
}
}
public void RecyclableList_Remove_FirstItems()
{
var data = TestObjects;
using var list = new RecyclableList<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Remove(data[i]));
}
}
public void RecyclableLongList_Remove_FirstItems()
{
var data = TestObjects;
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
DoNothing(list.Remove(data[i]));
}
}
}
}
@@ -0,0 +1,51 @@
using Collections.Pooled;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void List_Remove_LastItems()
{
var data = TestObjects;
var list = new List<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{
DoNothing(list.Remove(data[i]));
}
}
public void PooledList_Remove_LastItems()
{
var data = TestObjects;
using var list = new PooledList<long>(data, ClearMode.Auto);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{
DoNothing(list.Remove(data[i]));
}
}
public void RecyclableList_Remove_LastItems()
{
var data = TestObjects;
using var list = new RecyclableList<long>(data);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{
DoNothing(list.Remove(data[i]));
}
}
public void RecyclableLongList_Remove_LastItems()
{
var data = TestObjects;
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity:base.TestObjectCount);
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
{
DoNothing(list.Remove(data[i]));
}
}
}
}
@@ -0,0 +1,56 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_SetItem()
{
var data = TestObjects;
var dataCount = TestObjectCount;
long index = 0;
for (var i = 0; i < dataCount; i++)
{
data[index] = TestObjects[index++];
}
}
public void List_SetItem()
{
var data = TestObjectsAsList;
var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
data[i] = TestObjects[i];
}
}
public void PooledList_SetItem()
{
var data = TestObjectsAsPooledList;
var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
data[i] = TestObjects[i];
}
}
public void RecyclableList_SetItem()
{
var data = TestObjectsAsRecyclableList;
var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
data[i] = TestObjects[i];
}
}
public void RecyclableLongList_SetItem()
{
var data = TestObjectsAsRecyclableLongList;
var dataCount = TestObjectCount;
for (var i = 0; i < dataCount; i++)
{
data[i] = TestObjects[i];
}
}
}
}
@@ -0,0 +1,19 @@
namespace Recyclable.Collections.Benchmarks.Core
{
public static class DataGenerator
{
public static long[] EnumerateTestObjects(long testObjectCount)
{
var result = new long[testObjectCount];
Console.WriteLine("******* TEST DATA ARRAY CREATED *******");
Span<long> resultSpan = result;
for (int i = 0; i < testObjectCount; i++)
{
resultSpan[i] = i;
}
return result;
}
}
}
@@ -0,0 +1,70 @@
using BenchmarkDotNet.Attributes;
using System.Buffers;
namespace Recyclable.Collections.Benchmarks.POC
{
public enum ArraySizeLimitPocBenchmarkType
{
ArraySizeLimit
}
public class ArraySizeLimitPocBenchmarks : PocBenchmarkBase<ArraySizeLimitPocBenchmarkType>
{
public static void Run()
{
var benchmark = new ArraySizeLimitPocBenchmarks();
benchmark.Setup();
FindArraySizeLimit();
benchmark.Cleanup();
}
[Params(2_147_483_605)]
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
[Benchmark(OperationsPerInvoke = 1)]
public void Max_Allowed_Size_2_147_483_605()
{
long[] arr = ArrayPool<long>.Shared.Rent(TestObjectCount);
DoNothing(arr);
}
[Benchmark(OperationsPerInvoke = 1)]
public static void FindArraySizeLimit()
{
var pool = ArrayPool<long>.Shared;
var bufferSize = int.MaxValue / 2;
var oldValue = 0;
while (true)
{
try
{
Console.Write($"Allocating {bufferSize}...");
var arr = pool.Rent(bufferSize);
Console.WriteLine("success");
pool.Return(arr);
arr = null;
var nextValue = bufferSize - oldValue;
oldValue = bufferSize;
bufferSize = checked(bufferSize + nextValue);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
}
catch (Exception)
{
Console.WriteLine("error");
var nextValue = (bufferSize - oldValue) >> 1;
if (nextValue == 0)
{
break;
}
bufferSize = oldValue + nextValue;
}
}
Console.WriteLine("All done");
throw new Exception($"Everything was successful. You can create {oldValue} byte big arrays. Exception is logged to prevent more benchmarks.");
}
}
}
@@ -0,0 +1,63 @@
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC
{
public enum BoolOrComparePocBenchmarkType
{
Bool,
Compare,
}
[MemoryDiagnoser]
public class BoolOrComparePocBenchmarks : PocBenchmarkBase<BoolOrComparePocBenchmarkType>
{
[Params(BoolOrComparePocBenchmarkType.Bool, BoolOrComparePocBenchmarkType.Compare)]
public override BoolOrComparePocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
[Params(8)]
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
[Params(true)]
public bool TestBool = true;
public static void Run()
{
var benchmark = new BoolOrComparePocBenchmarks();
benchmark.Setup();
benchmark.Bool();
benchmark.Compare();
benchmark.Cleanup();
}
public void Bool()
{
if (TestBool)
{
DoNothing(TestBool);
}
else
{
DoNothing(TestBool);
}
}
public void Compare()
{
if (TestObjectCount >= RecyclableDefaults.MinPooledArrayLength)
{
DoNothing(TestBool);
}
else
{
DoNothing(TestBool);
}
}
protected override Action? GetTestMethod(BoolOrComparePocBenchmarkType benchmarkType) => benchmarkType switch
{
BoolOrComparePocBenchmarkType.Bool => Bool,
BoolOrComparePocBenchmarkType.Compare => Compare,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
};
}
}
@@ -0,0 +1,346 @@
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC
{
public enum DelegateVsComparerPocBenchmarkType
{
Comparer,
Delegate,
Func,
ListOfBoxedStructs,
ListOfClasses,
ListOfComparers,
ListOfDelegates,
ListOfFuncs,
ListOfLocalFuncs,
ListOfStructs,
ListOfUnboxedReadOnlyStructs,
ListOfUnboxedStructs,
StaticFunc,
}
[MemoryDiagnoser]
public class DelegateVsComparerPocBenchmarks : PocBenchmarkBase<DelegateVsComparerPocBenchmarkType>
{
[Params
(
DelegateVsComparerPocBenchmarkType.Delegate,
DelegateVsComparerPocBenchmarkType.Func,
DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs,
DelegateVsComparerPocBenchmarkType.ListOfClasses,
DelegateVsComparerPocBenchmarkType.ListOfComparers,
DelegateVsComparerPocBenchmarkType.ListOfDelegates,
DelegateVsComparerPocBenchmarkType.ListOfFuncs,
DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs,
DelegateVsComparerPocBenchmarkType.ListOfStructs,
DelegateVsComparerPocBenchmarkType.ListOfUnboxedReadOnlyStructs,
DelegateVsComparerPocBenchmarkType.ListOfUnboxedStructs,
DelegateVsComparerPocBenchmarkType.StaticFunc
)]
public override DelegateVsComparerPocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
[Params(DelegateVsComparerPocBenchmarkType.Comparer)]
public override DelegateVsComparerPocBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
#region Dummy classes
private delegate T Compare<T>(T x, T y);
public readonly struct MyComparerReadOnlyStruct<T> : IComparer<T>
{
public int Compare(T? x, T? y) => Comparer<T>.Default.Compare(x, y);
}
private struct MyComparerStruct<T> : IComparer<T>
{
public readonly int Compare(T? x, T? y) => Comparer<T>.Default.Compare(x, y);
}
private class MyComparer : IComparer<long>
{
public int Compare(long x, long y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0,
};
}
#endregion
public static void Run()
{
var benchmark = new DelegateVsComparerPocBenchmarks();
benchmark.Setup();
benchmark.Comparer();
benchmark.Delegate();
benchmark.Func();
benchmark.ListOfBoxedStructs();
benchmark.ListOfClasses();
benchmark.ListOfComparers();
benchmark.ListOfDelegates();
benchmark.ListOfFuncs();
benchmark.ListOfLocalFuncs();
benchmark.ListOfStructs();
benchmark.ListOfUnboxedReadOnlyStructs();
benchmark.ListOfUnboxedStructs();
benchmark.StaticFunc();
benchmark.Cleanup();
}
private static readonly Comparer<long> _comparer = Comparer<long>.Default;
private IEnumerable<long>? _testObjects;
private IEnumerable<Func<long, long, int>>? _testListOfFunctions;
private IEnumerable<IComparer<long>>? _testListOfComparers;
private IEnumerable<Compare<long>>? _testListOfDelegates;
private IEnumerable<Func<long, long, int>>? _testListOfLocalFunctions;
private IEnumerable<MyComparer>? _testListOfClasses;
private IEnumerable<MyComparerStruct<long>>? _testListOfStructs;
private IEnumerable<IComparer<long>>? _testListOfBoxedStructs;
private IEnumerable<TComparer> TestListOfUnboxedStructs<TComparer, TValue>() where TComparer : IComparer<TValue>, new()
{
foreach (var _ in _testObjects!)
{
yield return new TComparer();
}
}
private static IEnumerable<Func<long, long, int>> GetLocalFunctions(IEnumerable<long> testObjects)
{
static int compareFunc(long x, long y)
{
return (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
};
}
foreach (var _ in testObjects)
{
yield return compareFunc;
}
}
private void TestWithComparer(IComparer<long> comparer)
{
var testData = _testObjects!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{
_ = comparer.Compare(testData[itemIdx], testData[itemIdx + 1]);
}
}
private void TestWithDelegate(Compare<long> compareFunc)
{
var testData = _testObjects!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
}
}
private void TestWithFunc(Func<long, long, int> compareFunc)
{
var testData = _testObjects!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
}
}
public void Comparer()
{
TestWithComparer(_comparer);
}
public void Func()
{
TestWithFunc((x, y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
});
}
public void StaticFunc()
{
TestWithFunc(static (x, y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
});
}
public void Delegate()
{
TestWithDelegate((x, y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
});
}
public void ListOfFuncs()
{
const long x = 2, y = 5;
var testData = _testListOfFunctions!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var compareFunc = testData[itemIdx];
_ = compareFunc(x, y);
}
}
public void ListOfLocalFuncs()
{
const long x = 2, y = 5;
var testData = _testListOfLocalFunctions!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var compareFunc = testData[itemIdx];
_ = compareFunc(x, y);
}
}
public void ListOfComparers()
{
const long x = 2, y = 5;
var testData = _testListOfComparers!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
public void ListOfClasses()
{
const long x = 2, y = 5;
var testData = _testListOfClasses!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
public void ListOfStructs()
{
const long x = 2, y = 5;
var testData = _testListOfStructs!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
public void ListOfBoxedStructs()
{
const long x = 2, y = 5;
var testData = _testListOfBoxedStructs!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
public void ListOfUnboxedStructs()
{
const long x = 2, y = 5;
var testData = TestListOfUnboxedStructs<MyComparerStruct<long>, long>().ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
public void ListOfUnboxedReadOnlyStructs()
{
const long x = 2, y = 5;
var testData = TestListOfUnboxedStructs<MyComparerReadOnlyStruct<long>, long>().ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
public void ListOfDelegates()
{
const long x = 2, y = 5;
var testData = _testListOfDelegates!.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var compareDelegate = testData[itemIdx];
_ = compareDelegate(x, y);
}
}
protected override Action? GetTestMethod(DelegateVsComparerPocBenchmarkType benchmarkType) => benchmarkType switch
{
DelegateVsComparerPocBenchmarkType.Comparer => Comparer,
DelegateVsComparerPocBenchmarkType.Delegate => Delegate,
DelegateVsComparerPocBenchmarkType.Func => Func,
DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs => ListOfBoxedStructs,
DelegateVsComparerPocBenchmarkType.ListOfClasses => ListOfClasses,
DelegateVsComparerPocBenchmarkType.ListOfComparers => ListOfComparers,
DelegateVsComparerPocBenchmarkType.ListOfDelegates => ListOfDelegates,
DelegateVsComparerPocBenchmarkType.ListOfFuncs => ListOfFuncs,
DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs => ListOfLocalFuncs,
DelegateVsComparerPocBenchmarkType.ListOfStructs => ListOfStructs,
DelegateVsComparerPocBenchmarkType.ListOfUnboxedReadOnlyStructs => ListOfUnboxedReadOnlyStructs,
DelegateVsComparerPocBenchmarkType.ListOfUnboxedStructs => ListOfUnboxedStructs,
DelegateVsComparerPocBenchmarkType.StaticFunc => StaticFunc,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
};
public override void Setup()
{
Console.WriteLine("******* SETTING UP COMMON TEST DATA *******");
_testObjects = Enumerable.Range(1, TestObjectCount).Select(x => (long)x);
base.Setup();
}
public override void Cleanup()
{
_testObjects = null;
base.Cleanup();
}
protected override void PrepareData<T>(T benchmarkType)
{
switch (benchmarkType)
{
case DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs:
_testListOfBoxedStructs = _testObjects!.Select(_ => (IComparer<long>)new MyComparerStruct<long>());
break;
case DelegateVsComparerPocBenchmarkType.ListOfClasses:
_testListOfClasses = _testObjects!.Select(_ => new MyComparer());
break;
case DelegateVsComparerPocBenchmarkType.ListOfComparers:
_testListOfComparers = _testObjects!.Select(_ => (IComparer<long>)new MyComparer());
break;
case DelegateVsComparerPocBenchmarkType.ListOfDelegates:
_testListOfDelegates = _testObjects!.Select(_ => new Compare<long>((x, y) => new MyComparer().Compare(x, y)));
break;
case DelegateVsComparerPocBenchmarkType.ListOfFuncs:
_testListOfFunctions = _testObjects!.Select<long, Func<long, long, int>>(static _ => new MyComparer().Compare);
break;
case DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs:
_testListOfLocalFunctions = GetLocalFunctions(_testObjects!);
break;
case DelegateVsComparerPocBenchmarkType.ListOfStructs:
_testListOfStructs = _testObjects!.Select(_ => new MyComparerStruct<long>());
break;
}
}
}
}
@@ -0,0 +1,40 @@
using BenchmarkDotNet.Attributes;
using FluentAssertions;
using Recyclable.CollectionsTests;
namespace Recyclable.Collections.Benchmarks.POC
{
public class EqualVsContainsInConsecutiveOrderBenchmarks : IDisposable
{
private const int TestItemsCount = 10000;
private readonly IEnumerable<long> _list1 = RecyclableLongListTestData.CreateTestData(TestItemsCount).ToRecyclableList();
private readonly IEnumerable<long> _list2 = RecyclableLongListTestData.CreateTestData(TestItemsCount).ToArray();
[Benchmark(Baseline = true)]
public void Equal()
{
_ = _list1.Should().Equal(_list2);
}
[Benchmark]
public void ContainsInConsecutiveOrder()
{
_ = _list1.Should().ContainInConsecutiveOrder(_list2);
}
public void Dispose()
{
if (_list1 is IDisposable disposable1)
{
disposable1.Dispose();
}
if (_list2 is IDisposable disposable2)
{
disposable2.Dispose();
}
}
}
}
@@ -0,0 +1,57 @@
using BenchmarkDotNet.Attributes;
using System.Runtime.CompilerServices;
#pragma warning disable CA1822
namespace Recyclable.Collections.Benchmarks.POC
{
public class GreaterOperatorVsDoesntEqualOperatorBenchmarks
{
private static readonly long num = RecyclableDefaults.MinItemsCountForParallelization;
public void Setup()
{
Console.WriteLine($"*** We're comparing if {num} is < 0 ***");
}
public void Cleanup() { }
#pragma warning disable RCS1163, IDE0060
[MethodImpl(MethodImplOptions.NoInlining)]
private static void DoNothing<T>(T item)
{
}
#pragma warning restore RCS1163
[Benchmark(Baseline = true)]
public void CompareWithGreater()
{
DoNothing(num > 0 ? 1 : 0);
}
[Benchmark]
public void CompareWithGreaterEqual()
{
DoNothing(num >= 0 ? 1 : 0);
}
[Benchmark]
public void CompareWithDoesntEqual()
{
DoNothing(num != 0 ? 1 : 0);
}
[Benchmark]
public void CompareWithLess()
{
DoNothing(num < 0 ? 1 : 0);
}
[Benchmark]
public void CompareWithEqual()
{
DoNothing(num == 0 ? 0 : 1);
}
}
}
@@ -0,0 +1,54 @@
using BenchmarkDotNet.Attributes;
using System.Runtime.CompilerServices;
#pragma warning disable CA1822
namespace Recyclable.Collections.Benchmarks.POC
{
public class LessOperatorVsAndOperatorBenchmarks
{
private const int bitShift = 1 << 31;
private static readonly Random _rnd = new(1);
private static int num = -Math.Abs(_rnd.Next());
public void Setup()
{
num = -1;
Console.WriteLine($"*** We're comparing if {num} is < 0 ***");
}
public void Cleanup() { }
#pragma warning disable RCS1163, IDE0060
[MethodImpl(MethodImplOptions.NoInlining)]
private static void DoNothing<T>(T item)
{
}
#pragma warning restore RCS1163
[Benchmark(Baseline = true)]
public void CompareWithLess()
{
DoNothing(num < 0);
}
[Benchmark]
public void CompareWithAnd()
{
DoNothing((num & bitShift) != 0);
}
[Benchmark]
public void CompareWithEqual()
{
DoNothing(num == -1);
}
[Benchmark]
public void CompareToZero()
{
DoNothing(num == 0);
}
}
}
@@ -0,0 +1,376 @@
using BenchmarkDotNet.Attributes;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#pragma warning disable xUnit1026, RCS1163, IDE0060
namespace Recyclable.Collections.Benchmarks.POC
{
[MemoryDiagnoser]
public class ModuloPocBenchmarks
{
public static void Run()
{
var benchmark = new ModuloPocBenchmarks();
benchmark.Setup();
benchmark.AddWithFor();
benchmark.AddWithForOptimized();
benchmark.DividerAndModulusWithOperator();
benchmark.MathDivRem();
benchmark.MixedApproachWithForLoopOptimized();
benchmark.MixedApproachWithSubtractWithStackalloc();
benchmark.MixedApproachWithSubtractWithWhile();
benchmark.MixedApproachWithSubtractWithWhileAsVector();
benchmark.MixedApproachWithSubtractWithWhileWithSse2();
benchmark.SubtractWithWhile();
benchmark.SubtractWithWhileWithSse2();
benchmark.XDivYMulX();
benchmark.XDivYMulXWithLocalVar();
}
private long _cutoff;
private Vector128<long> _increment;
private long _lastCaseIndex;
[GlobalSetup]
public void Setup()
{
_cutoff = checked((long)BlockSize << 4);
_increment = Vector128.Create(-BlockSize, 1L);
_lastCaseIndex = 0;
}
private static void DoNothing<T>(T result, T remainder, [CallerMemberName] string? callerName = null)
{
//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(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)]
public int BlockSize { get; set; } = int.MaxValue;
[Params(int.MaxValue, int.MaxValue / 1000, int.MaxValue / 10000)]
//[Params(int.MaxValue)]
//[Params(int.MaxValue * 100L)]
public long TestNumber { get; set; } = int.MaxValue;
private void CalculateDividerAndModulusWithOperator(long i)
{
DoNothing(i / BlockSize, i % BlockSize);
}
private void CalculateWithMathDivRem(long i)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
private void CalculateWithAddWithFor(long i)
{
int result = 0;
int remainder = 0;
int blockSize = BlockSize;
for (long iteration = 0; iteration < i; iteration++)
{
if (remainder > blockSize - 1)
{
remainder = 0;
result++;
}
remainder++;
}
DoNothing(result, remainder);
}
private void CalculateWithAddWithForOptimized(long i)
{
long total = 0;
int result = 0;
int blockSize = BlockSize;
while (total + blockSize <= i)
{
total += blockSize;
result++;
}
DoNothing(result, i - total);
}
private void CalculateWithXDivYMulX(long i)
{
DoNothing(i / BlockSize, i - (i / BlockSize * BlockSize));
}
private void CalculateWithXDivYMulXWithLocalVar(long i)
{
int blockSize = BlockSize;
int result = (int)(i / blockSize);
DoNothing(result, i - (result * blockSize));
}
private void CalculateWithSubtractWithWhile(long i)
{
int result = 0;
long remainder = i;
int blockSize = BlockSize;
while (remainder >= blockSize)
{
remainder -= blockSize;
result++;
}
DoNothing(result, remainder);
}
private void CalculateWithSubtractWithWhileWithSse2(long i)
{
var state = Vector128.Create(i, 0);
int blockSize = BlockSize;
var inc = _increment;
while (state.GetElement(0) >= blockSize)
{
state = Sse2.Add(state, inc);
}
DoNothing(state.GetElement(1), state.GetElement(0));
}
private void CalculateWithMixedApproachWithSubtractWithWhile(long i)
{
long remainder;
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out remainder), remainder);
}
else
{
int result = 0;
int blockSize = BlockSize;
remainder = i;
while (remainder >= blockSize)
{
remainder -= blockSize;
result++;
}
DoNothing(result, remainder);
}
}
private void CalculateWithMixedApproachWithSubtractWithWhileAsVector(long i)
{
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
else
{
var state = new Vector<long>(new[] { 0L, i, 0, 0 });
var increment = new Vector<long>(new[] { 1L, -BlockSize, 0, 0 });
int blockSize = BlockSize;
while (state[1] >= blockSize)
{
state += increment;
}
DoNothing(state[0], state[1]);
}
}
private void CalculateWithMixedApproachWithSubtractWithWhileWithSse2(long i)
{
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
else
{
var increment = _increment;
var state = Vector128.Create(i, 0);
int blockSize = BlockSize;
while (state.GetElement(0) >= blockSize)
{
state = Sse2.Add(state, increment);
}
DoNothing(state.GetElement(1), state.GetElement(0));
}
}
private void CalculateWithMixedApproachWithSubtractWithStackalloc(long i)
{
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
else
{
Span<long> state = stackalloc long[2] { i, 0 };// Vector128.Create(BlockSize, 0);
int blockSize = BlockSize;
while (state[0] >= blockSize)
{
state[0] -= blockSize;
state[1]++;
}
DoNothing(state[1], state[0]);
}
}
private void CalculateWithMixedApproachWithForLoopOptimized(long i)
{
long total;
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out total), total);
}
else
{
total = 0;
int result = 0;
int blockSize = BlockSize;
while (total + blockSize <= i)
{
total += blockSize;
result++;
}
DoNothing(result, i - total);
}
}
[Benchmark(Baseline = true)]
public void DividerAndModulusWithOperator()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateDividerAndModulusWithOperator(i);
}
}
[Benchmark]
public void MathDivRem()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMathDivRem(i);
}
}
[Benchmark]
public void AddWithFor()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithAddWithFor(i);
}
}
[Benchmark(Baseline = true)]
public void AddWithForOptimized()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithAddWithForOptimized(i);
}
}
[Benchmark]
public void XDivYMulX()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithXDivYMulX(i);
}
}
[Benchmark]
public void XDivYMulXWithLocalVar()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithXDivYMulXWithLocalVar(i);
}
}
[Benchmark]
public void SubtractWithWhile()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithSubtractWithWhile(i);
}
}
[Benchmark]
public void SubtractWithWhileWithSse2()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithSubtractWithWhileWithSse2(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhile()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithWhile(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhileAsVector()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithWhileAsVector(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhileWithSse2()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithWhileWithSse2(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithStackalloc()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithStackalloc(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithForLoopOptimized()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithForLoopOptimized(i);
}
}
}
}
@@ -0,0 +1,142 @@
using BenchmarkDotNet.Attributes;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Recyclable.Collections.Benchmarks.POC
{
public abstract class PocBenchmarkBase<TBenchmarkType>
{
public virtual TBenchmarkType? DataType { get; set; }
public virtual TBenchmarkType? BaseDataType { get; set; }
private Action? _testMethod;
protected Action TestMethod
{
get => _testMethod ?? throw new OperationCanceledException("Skip - No actual defined");
set => _testMethod = value;
}
private Action? _baselineMethod;
protected Action BaselineMethod
{
get => _baselineMethod ?? throw new OperationCanceledException("Skip - No baseline defined");
set => _baselineMethod = value;
}
[Benchmark(Baseline = true)]
public void Baseline() => BaselineMethod.Invoke();
[Benchmark]
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
// 1048576 2097152 4194304 8388608 16777216 33554432 67108864 134217728 268435456 536870912
// 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, 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_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(524_288, 786_432, 1_048_576)]
// [Params(262_144, 393_216, 524_288)]
// [Params(850_000_000)]
// [Params(100_000_000)]
// [Params(RecyclableDefaults.MinItemsCountForParallelization)]
// [Params(127)]
// [Params(16)]
// [Params(10, 15, 128)]
// [Params(1)]
// [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(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(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(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, 10, 1024, 10240, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinItemsCountForParallelization)]
// [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)]
// [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 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(10, 20, 50, 100)]
// [Params(1, 10)]
public int Divider = 10;
//public int BlockSize = 1_048_576;//int.MaxValue - 1;
public int BlockSize { get; private set; }
public byte BlockSizePow2BitShift => checked((byte)(31 - BitOperations.LeadingZeroCount((uint)BlockSize)));
[MethodImpl(MethodImplOptions.NoInlining)]
protected static void DoNothing<T>(in T item)
{
_ = item;
}
protected static Exception CreateUnknownBenchmarkTypeException<T>(T benchmarkType)
{
return new InvalidOperationException($"******* UNKNOWN BENCHMARK TYPE {{{benchmarkType}}} *******");
}
protected static Exception CreateMethodNotFoundException(in string methodName, in string? className)
{
return new MethodAccessException($"******* METHOD {{{methodName}}} NOT FOUND IN CLASS {{{className}}} *******");
}
protected virtual void PrepareData<T>(T benchmarkType) { }
protected virtual Action? GetTestMethod(TBenchmarkType? benchmarkType) => null;
[GlobalCleanup]
public virtual void Cleanup()
{
_baselineMethod = null;
_testMethod = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
}
[GlobalSetup]
public virtual void Setup()
{
Console.WriteLine($"******* SETTING UP TEST CASE FOR BENCHMARK {{{DataType}}} *******");
TestObjectCountForSlowMethods = (TestObjectCount / 2) + 1;
BlockSize = TestObjectCount switch
{
0 => 1,
> 0 and <= 10_240 => TestObjectCount,
_ => TestObjectCount / Divider > 0 ? TestObjectCount / Divider : TestObjectCount
};
if (BlockSize > 0)
{
BlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)BlockSize));
}
Console.WriteLine($"******* BaseDataType = {BaseDataType}; default = {default(TBenchmarkType)} *******");
if (!EqualityComparer<TBenchmarkType>.Default.Equals(BaseDataType, default))
{
Console.WriteLine("******* SETTING UP BASELINE DATA *******");
PrepareData(BaseDataType);
_baselineMethod = GetTestMethod(BaseDataType);
}
//~ We don't want to prepare data if baseline data is the same is benchmark data.
//~ One would override another - waste of time & resources.
if (!EqualityComparer<TBenchmarkType>.Default.Equals(BaseDataType, DataType))
{
Console.WriteLine("******* SETTING UP TEST CASE DATA *******");
//~ If BaseDataType == null, then we'll come here only if DataType != null.
PrepareData(DataType!);
_testMethod = GetTestMethod(DataType);
}
Console.WriteLine("******* DATA PREPARED *******");
}
}
}
@@ -0,0 +1,47 @@
using System.Reflection;
using BenchmarkDotNet.Attributes;
using Collections.Pooled;
using Recyclable.Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks.POC
{
[MemoryDiagnoser]
public partial class RecyclableLongListPocBenchmarks<T> : PocBenchmarkBase<T>
{
private long[]? _testArray;
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");
private RecyclableLongList<long>? _testRecyclableLongList;
private PooledList<long>? _testPooledList;
internal PooledList<long> TestPooledList => _testPooledList ?? throw new NullReferenceException($"Something is wrong and {nameof(PooledList<long>)} is null");
private static readonly MethodInfo _ensureCapacityNewFunc;
static RecyclableLongListPocBenchmarks()
{
_ensureCapacityNewFunc = typeof(RecyclableLongList<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic)
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongList<long>)}");
}
public override void Setup()
{
Console.WriteLine("******* SETTING UP EXPECTED RESULTS DATA *******");
_testArray = DataGenerator.EnumerateTestObjects(TestObjectCount);
base.Setup();
}
public override void Cleanup()
{
_testArray = null;
_testRecyclableLongList?.Dispose();
_testRecyclableLongList = null;
_testPooledList?.Dispose();
_testPooledList = null;
base.Cleanup();
}
}
}
@@ -0,0 +1,26 @@
BenchmarkDotNet=v0.13.3, OS=ubuntu 20.04
Intel Xeon Platinum 8272CL CPU 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK=7.0.203
[Host] : .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 |
|------------------------- |--------:|---------:|---------:|------:|--------:|
| 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_IndexOf | 2.234 s | 0.0446 s | 0.0681 s | 0.97 | 0.05 |
// * Hints *
Outliers
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)
// * Legends *
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Ratio : Mean of the ratio distribution ([Current]/[Baseline])
RatioSD : Standard deviation of the ratio distribution ([Current]/[Baseline])
1 s : 1 Second (1 sec)
@@ -0,0 +1,364 @@
using BenchmarkDotNet.Attributes;
#pragma warning disable RCS1085
namespace Recyclable.Collections.Benchmarks.POC
{
public class RefVsInstanceMemberPocBenchmarks
{
public static void Run()
{
var benchmark = new RefVsInstanceMemberPocBenchmarks();
benchmark.ArrayInstanceMember2();
benchmark.ArrayInstanceMember3();
benchmark.ArrayInstanceMember4();
benchmark.ArrayInstanceMember5();
benchmark.ArrayInstanceMember6();
benchmark.ArrayInstanceMemberLocalVar5();
benchmark.ArrayInstanceMemberLocalVar6();
benchmark.IntInstanceMember2();
benchmark.IntInstanceMember3();
benchmark.IntInstanceMember4();
benchmark.IntInstanceMember5();
benchmark.RefToArray2();
benchmark.RefToArray3();
benchmark.RefToArray4();
benchmark.RefToArray5();
benchmark.RefToArray6();
benchmark.RefToInt2();
benchmark.RefToInt3();
}
private readonly TestCase _testCase = new();
private static void DoNothing(int count)
{
_ = count;
}
private class TestCase
{
private int[] _items = new int[123];
private int _count = 123;
private static int Add(int count)
{
return count;
}
public int Count
{
get => _count;
set => _count = value;
}
public ref int GetCountAsRefCount2()
{
ref int count = ref _count;
count = 234;
return ref count;
}
public ref int GetCountAsRefCount3()
{
ref int count = ref _count;
count = 234;
_ = TestCase.Add(count);
return ref count;
}
public ref int GetCountAsRefCount4()
{
ref int count = ref _count;
count = 234;
_ = TestCase.Add(count);
_ = TestCase.Add(count);
return ref count;
}
public ref int GetCountAsRefCount5()
{
ref int count = ref _count;
count = 234;
_ = TestCase.Add(count);
_ = TestCase.Add(count);
_ = TestCase.Add(count);
return ref count;
}
public int GetCountAsInstanceMember2()
{
_count = 234;
return _count;
}
public int GetCountAsInstanceMember3()
{
_count = 234;
_ = TestCase.Add(_count);
return _count;
}
public int GetCountAsInstanceMember4()
{
_count = 234;
_ = TestCase.Add(_count);
_ = TestCase.Add(_count);
return _count;
}
public int GetCountAsInstanceMember5()
{
_count = 234;
_ = TestCase.Add(_count);
_ = TestCase.Add(_count);
_ = TestCase.Add(_count);
return _count;
}
public int GetLengthAsArray2()
{
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray3()
{
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray4()
{
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray5()
{
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray6()
{
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArrayLocalVar5()
{
var items = _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsArrayLocalVar6()
{
var items = _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray2()
{
ref int[] items = ref _items;
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray3()
{
ref int[] items = ref _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray4()
{
ref int[] items = ref _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray5()
{
ref int[] items = ref _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray6()
{
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);
return items.Length;
}
}
[Benchmark]
public void RefToInt2()
{
var count = _testCase.GetCountAsRefCount2();
DoNothing(count);
}
[Benchmark]
public void RefToInt3()
{
var count = _testCase.GetCountAsRefCount3();
DoNothing(count);
}
[Benchmark]
public void RefToInt4()
{
var count = _testCase.GetCountAsRefCount4();
DoNothing(count);
}
[Benchmark]
public void RefToInt5()
{
var count = _testCase.GetCountAsRefCount5();
DoNothing(count);
}
[Benchmark(Baseline = true)]
public void IntInstanceMember2()
{
var count = _testCase.GetCountAsInstanceMember2();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void IntInstanceMember3()
{
var count = _testCase.GetCountAsInstanceMember3();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void IntInstanceMember4()
{
var count = _testCase.GetCountAsInstanceMember4();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void IntInstanceMember5()
{
var count = _testCase.GetCountAsInstanceMember5();
DoNothing(count);
}
[Benchmark]
public void RefToArray2()
{
var count = _testCase.GetLengthAsRefArray2();
DoNothing(count);
}
[Benchmark]
public void RefToArray3()
{
var count = _testCase.GetLengthAsRefArray3();
DoNothing(count);
}
[Benchmark]
public void RefToArray4()
{
var count = _testCase.GetLengthAsRefArray4();
DoNothing(count);
}
[Benchmark]
public void RefToArray5()
{
var count = _testCase.GetLengthAsRefArray5();
DoNothing(count);
}
[Benchmark]
public void RefToArray6()
{
var count = _testCase.GetLengthAsRefArray6();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember2()
{
var count = _testCase.GetLengthAsArray2();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember3()
{
var count = _testCase.GetLengthAsArray3();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember4()
{
var count = _testCase.GetLengthAsArray4();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember5()
{
var count = _testCase.GetLengthAsArray5();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember6()
{
var count = _testCase.GetLengthAsArray6();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMemberLocalVar5()
{
var count = _testCase.GetLengthAsArrayLocalVar5();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMemberLocalVar6()
{
var count = _testCase.GetLengthAsArrayLocalVar6();
DoNothing(count);
}
}
}
@@ -0,0 +1,45 @@
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC
{
public enum RoundBenchmarkType
{
MathFloor,
MathRound,
WithTypecasting,
}
public class RoundBenchmarks : PocBenchmarkBase<RoundBenchmarkType>
{
[Params(RoundBenchmarkType.MathFloor)]
public override RoundBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
[Params(RoundBenchmarkType.MathRound, RoundBenchmarkType.WithTypecasting)]
public override RoundBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
private const double OptimalParallelSearchStep = 0.329;
public void RoundWithMathFloor()
{
DoNothing((long)Math.Floor(TestObjectCount * OptimalParallelSearchStep));
}
public void RoundWithMathRound()
{
DoNothing((long)Math.Round(TestObjectCount * OptimalParallelSearchStep, 0));
}
public void RoundWithTypeCasting()
{
DoNothing((long)(TestObjectCount * OptimalParallelSearchStep));
}
protected override Action GetTestMethod(RoundBenchmarkType benchmarkType) => benchmarkType switch
{
RoundBenchmarkType.MathFloor => RoundWithMathFloor,
RoundBenchmarkType.MathRound => RoundWithMathRound,
RoundBenchmarkType.WithTypecasting => RoundWithTypeCasting,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
};
}
}
@@ -0,0 +1,147 @@
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC
{
static class RoundUpToPowerOf2Alternatives
{
private static readonly int[] PowersOfTwo =
{
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576,
2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824
};
public static int BinarySearch(int value)
{
if (value <= 0)
{
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
int index = Array.BinarySearch(PowersOfTwo, value);
if (index < 0)
{
// If value is not a power of 2, use the next power of 2 from the table
index = ~index;
}
return PowersOfTwo[index];
}
public static int RightBitShifting(int value)
{
// Decrease value by 1 to handle the case where the input is already a power of 2
value--;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return value + 1;
}
public static int LeftBitShifting(int value)
{
if (value == 0)
{
return 1;
}
// Ensure value is a power of 2 or the next power of 2
int result = 1;
while (result < value)
{
result = checked(result << 1);
}
return result;
}
}
public enum RoundUpToPowerOf2BenchmarkType
{
Undefined,
BitOperations,
RightBitShifting,
LeftBitShifting,
BinarySearch,
IsPowOf2,
IsPowOf2_GreaterThanFirst,
IsPowOf2_WithIf,
IsPowOf2_GreaterEqual,
}
public class RoundUpToPowerOf2vsOtherBenchmarks : PocBenchmarkBase<RoundUpToPowerOf2BenchmarkType>
{
// [Params(-850_000, 850_000)]
[Params(-int.MaxValue, int.MaxValue)]
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
[Params(RoundUpToPowerOf2BenchmarkType.IsPowOf2)]
public override RoundUpToPowerOf2BenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
[Params(RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterThanFirst, RoundUpToPowerOf2BenchmarkType.IsPowOf2_WithIf, RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterEqual)]
// [Params(RoundUpToPowerOf2BenchmarkType.RightBitShifting, RoundUpToPowerOf2BenchmarkType.LeftBitShifting, RoundUpToPowerOf2BenchmarkType.BinarySearch)]
public override RoundUpToPowerOf2BenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
private void BitOperations()
{
DoNothing(System.Numerics.BitOperations.RoundUpToPowerOf2((uint)base.TestObjectCount));
}
private void RightBitShifting()
{
DoNothing(RoundUpToPowerOf2Alternatives.RightBitShifting(TestObjectCount));
}
private void LeftBitShifting()
{
DoNothing(RoundUpToPowerOf2Alternatives.LeftBitShifting(TestObjectCount));
}
private void BinarySearch()
{
DoNothing(RoundUpToPowerOf2Alternatives.BinarySearch(TestObjectCount));
}
private void IsPowOf2()
{
DoNothing((TestObjectCount & (TestObjectCount - 1)) == 0 && TestObjectCount > 0);
}
private void IsPowOf2_GreaterThanFirst()
{
DoNothing(TestObjectCount > 0 && (TestObjectCount & (TestObjectCount - 1)) == 0);
}
private void IsPowOf2_WithIf()
{
DoNothing(TestObjectCount > 0 ? (TestObjectCount & (TestObjectCount - 1)) == 0 : false);
}
private void IsPowOf2_GreaterEqual()
{
DoNothing(TestObjectCount >= 0 && TestObjectCount != 0 && (TestObjectCount & (TestObjectCount - 1)) == 0);
}
protected override Action? GetTestMethod(RoundUpToPowerOf2BenchmarkType benchmarkType) => benchmarkType switch
{
RoundUpToPowerOf2BenchmarkType.BitOperations => BitOperations,
RoundUpToPowerOf2BenchmarkType.RightBitShifting => RightBitShifting,
RoundUpToPowerOf2BenchmarkType.LeftBitShifting => LeftBitShifting,
RoundUpToPowerOf2BenchmarkType.BinarySearch => BinarySearch,
RoundUpToPowerOf2BenchmarkType.IsPowOf2 => IsPowOf2,
RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterThanFirst => IsPowOf2_GreaterThanFirst,
RoundUpToPowerOf2BenchmarkType.IsPowOf2_WithIf => IsPowOf2_WithIf,
RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterEqual => IsPowOf2_GreaterEqual,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
};
protected override void PrepareData<T>(T benchmarkType)
{
}
}
}
@@ -0,0 +1,64 @@
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC
{
public enum SpanConstructorVsTypecastingPocBenchmarkType
{
Unknown,
Constructor,
TypeCasting,
}
[MemoryDiagnoser]
public class SpanConstructorVsTypecastingPocBenchmarks : PocBenchmarkBase<SpanConstructorVsTypecastingPocBenchmarkType>
{
private int[]? TestData { get; set; }
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
private void DoNothing(ReadOnlySpan<int> span) { }
[Params(SpanConstructorVsTypecastingPocBenchmarkType.TypeCasting)]
public override SpanConstructorVsTypecastingPocBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
[Params(SpanConstructorVsTypecastingPocBenchmarkType.Constructor)]
public override SpanConstructorVsTypecastingPocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
[Params(8)]
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
public static void Run()
{
var benchmark = new SpanConstructorVsTypecastingPocBenchmarks();
benchmark.Setup();
benchmark.Constructor();
benchmark.TypeCasting();
benchmark.Cleanup();
}
public void Constructor()
{
ReadOnlySpan<int> span = new(TestData);
DoNothing(span);
}
public void TypeCasting()
{
ReadOnlySpan<int> span = TestData;
DoNothing(span);
}
public override void Setup()
{
TestData = new int[TestObjectCount];
base.Setup();
}
protected override Action? GetTestMethod(SpanConstructorVsTypecastingPocBenchmarkType benchmarkType) => benchmarkType switch
{
SpanConstructorVsTypecastingPocBenchmarkType.Constructor => Constructor,
SpanConstructorVsTypecastingPocBenchmarkType.TypeCasting => TypeCasting,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
};
}
}
@@ -0,0 +1,99 @@
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC
{
public enum SpanVsArrayBenchmarkType
{
ArrayGetWithVar,
ArrayGetWithField,
SpanGet,
ArraySetWithVar,
ArraySetWithField,
SpanSet
}
public class SpanVsArrayBenchmarks : RecyclableBenchmarkBase<SpanVsArrayBenchmarkType>
{
// [Params(SpanVsArrayBenchmarkType.ArrayGetWithVar, SpanVsArrayBenchmarkType.SpanGet)]
// [Params(SpanVsArrayBenchmarkType.ArraySetWithVar, SpanVsArrayBenchmarkType.SpanSet)]
[Params
(
SpanVsArrayBenchmarkType.ArrayGetWithVar, SpanVsArrayBenchmarkType.SpanGet,
SpanVsArrayBenchmarkType.ArraySetWithField, SpanVsArrayBenchmarkType.ArraySetWithVar, SpanVsArrayBenchmarkType.SpanSet
)]
public override SpanVsArrayBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
[Params(SpanVsArrayBenchmarkType.ArrayGetWithField)]
// [Params(SpanVsArrayBenchmarkType.ArraySetWithField)]
public override SpanVsArrayBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
protected override Action GetTestMethod(SpanVsArrayBenchmarkType benchmarkType) => benchmarkType switch
{
SpanVsArrayBenchmarkType.ArrayGetWithVar => ArrayGetWithVar,
SpanVsArrayBenchmarkType.ArrayGetWithField => ArrayGetWithField,
SpanVsArrayBenchmarkType.SpanGet => SpanGet,
SpanVsArrayBenchmarkType.ArraySetWithVar => ArraySetWithVar,
SpanVsArrayBenchmarkType.ArraySetWithField => ArraySetWithField,
SpanVsArrayBenchmarkType.SpanSet => SpanSet,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
};
public void ArrayGetWithVar()
{
var data = TestObjects;
var dataCount = TestObjectCount;
for (long i = 0; i < dataCount; i++)
{
DoNothing(data[i]);
}
}
public void ArrayGetWithField()
{
var dataCount = TestObjectCount;
for (long i = 0; i < dataCount; i++)
{
DoNothing(_testObjects![i]);
}
}
public void SpanGet()
{
var dataCount = TestObjectCount;
Span<long> data = new(_testObjects, 0, dataCount);
for (int i = 0; i < dataCount; i++)
{
DoNothing(data[i]);
}
}
public void ArraySetWithVar()
{
var data = TestObjects;
var dataCount = TestObjectCount;
for (long i = 0; i < dataCount; i++)
{
data[i] = i;
}
}
public void ArraySetWithField()
{
var dataCount = TestObjectCount;
for (long i = 0; i < dataCount; i++)
{
_testObjects![i] = i;
}
}
public void SpanSet()
{
var dataCount = TestObjectCount;
Span<long> data = new(_testObjects, 0, dataCount);
for (int i = 0; i < dataCount; i++)
{
data[i] = i;
}
}
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Benchmarks.POC
{
[MemoryDiagnoser]
public class TaskRunVsTaskFactoryStartNewBenchmarks
{
public int NumberOfTasks = 100;
private static readonly TaskFactory TaskPool = new();
[Benchmark]
public void OwnTaskFactory()
{
for (var i = 0; i < NumberOfTasks; i++)
{
_ = TaskPool.StartNew(() => {});
}
}
[Benchmark]
public void TaskRun()
{
for (var i = 0; i < NumberOfTasks; i++)
{
_ = Task.Run(() => { });
}
}
[Benchmark(Baseline = true)]
public void TaskFactoryStartNew()
{
for (var i = 0; i < NumberOfTasks; i++)
{
_ = Task.Factory.StartNew(() => { });
}
}
[Benchmark]
public void TaskConstructor()
{
for (var i = 0; i < NumberOfTasks; i++)
{
new Task(() => { }).Start();
}
}
[Benchmark]
public void TaskConstructorWithKnownScheduler()
{
TaskScheduler scheduler = TaskScheduler.Current;
for (var i = 0; i < NumberOfTasks; i++)
{
new Task(() => { }).Start(scheduler);
}
}
}
}
@@ -0,0 +1,65 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using Recyclable.Collections.Benchmarks.POC;
#pragma warning disable RCS1036, RCS1213, IDE0051
namespace Recyclable.Collections.Benchmarks
{
public static class Program
{
static IConfig BenchmarkConfig { get; } = ManualConfig.Create(DefaultConfig.Instance)
.WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary);
static void RunAssemblyBenchmarks()
{
_ = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(new[] { "-f*" });
}
private static void RunRecyclableCollectionsBenchmarks()
{
_ = BenchmarkRunner.Run<RecyclableCollectionsBenchmarks>(BenchmarkConfig);
}
static void RunSelectedBenchmarks()
{
// ****************
// *** Template ***
// ****************
var benchmark = new RecyclableCollectionsBenchmarks();
benchmark.Setup();
foreach (var _ in Enumerable.Range(1, 1000))
{
// benchmark.RecyclableList_Create_WithCapacity();
}
benchmark.Cleanup();
}
static void RunPocBenchmarks()
{
_ = BenchmarkRunner.Run<BoolOrComparePocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<DelegateVsComparerPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<ModuloPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<TaskRunVsTaskFactoryStartNewBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<RefVsInstanceMemberPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<RoundBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<SpanVsArrayBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<LessOperatorVsAndOperatorBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<EqualVsContainsInConsecutiveOrderBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<RoundUpToPowerOf2vsOtherBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<GreaterOperatorVsDoesntEqualOperatorBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<SpanConstructorVsTypecastingPocBenchmarks>(BenchmarkConfig);
// ArraySizeLimitPocBenchmarks>(BenchmarkConfig);
}
static void Main()
{
RunRecyclableCollectionsBenchmarks();
// RunPocBenchmarks();
// RunSelectedBenchmarks();
// RunAssemblyBenchmarks();
}
}
}
@@ -0,0 +1,12 @@
{
"profiles": {
"WSL": {
"commandName": "WSL2",
"distributionName": ""
},
".Net": {
"commandName": "Project",
"nativeDebugging": true
}
}
}
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64</Platforms>
<StartupObject>Recyclable.Collections.Benchmarks.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
<PackageReference Include="Collections.Pooled" Version="1.0.82" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Recyclable.CollectionsTests\Recyclable.CollectionsTests.csproj" />
<ProjectReference Include="..\Recyclable.Collections\Recyclable.Collections.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,340 @@
using BenchmarkDotNet.Attributes;
using Collections.Pooled;
using Recyclable.Collections.Benchmarks.Core;
using Recyclable.Collections.Benchmarks.POC;
namespace Recyclable.Collections.Benchmarks
{
public enum RecyclableCollectionsBenchmarkSource
{
Unknown,
Array,
List,
PooledList,
RecyclableList,
RecyclableLongList,
}
[MemoryDiagnoser]
public class RecyclableBenchmarkBase<TBenchmarkType> : PocBenchmarkBase<TBenchmarkType>
{
protected long[]? _testObjects;
protected long[] TestObjects => _testObjects ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
protected List<long>? _testObjectsAsList;
protected List<long> TestObjectsAsList => _testObjectsAsList ?? throw new NullReferenceException("Something went wrong and the field is not initialized");
protected PooledList<long>? _testObjectsAsPooledList;
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 => _testObjectsAsRecyclableList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
protected RecyclableLongList<long>? _testObjectsAsRecyclableLongList;
protected RecyclableLongList<long> TestObjectsAsRecyclableLongList => _testObjectsAsRecyclableLongList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
protected IEnumerable<long> TestObjectsAsIEnumerable
{
get
{
for (long i = 0; i < TestObjectCount; i++)
{
yield return i;
}
}
}
public override void Setup()
{
PrepareData(RecyclableCollectionsBenchmarkSource.Array);
base.Setup();
}
protected override void PrepareData<T>(T benchmarkType)
{
Console.WriteLine($"******* PREPARING DATA FOR {benchmarkType} *******");
switch (benchmarkType)
{
case RecyclableCollectionsBenchmarkSource.Unknown:
break;
case RecyclableCollectionsBenchmarkSource.Array:
_testObjects ??= DataGenerator.EnumerateTestObjects(TestObjectCount);
break;
case RecyclableCollectionsBenchmarkSource.List:
_testObjectsAsList ??= TestObjects.ToList();
break;
case RecyclableCollectionsBenchmarkSource.PooledList:
_testObjectsAsPooledList ??= new PooledList<long>(TestObjects, ClearMode.Auto);
break;
case RecyclableCollectionsBenchmarkSource.RecyclableList:
_testObjectsAsRecyclableList ??= new(TestObjects, initialCapacity: TestObjectCount);
break;
case RecyclableCollectionsBenchmarkSource.RecyclableLongList:
_testObjectsAsRecyclableLongList ??= new(TestObjects, BlockSize, initialCapacity: base.TestObjectCount);
break;
default:
throw CreateUnknownBenchmarkTypeException(benchmarkType);
}
base.PrepareData(benchmarkType);
}
public override void Cleanup()
{
Console.WriteLine("******* GLOBAL CLEANUP *******");
// Enable this if item type is changed to reference type
//if (_testObjects != null)
//{
// Array.Fill(_testObjects, 0);
//}
_testObjects = default;
_testObjectsAsList = default;
_testObjectsAsPooledList?.Dispose();
_testObjectsAsPooledList = default;
_testObjectsAsRecyclableList?.Dispose();
_testObjectsAsRecyclableList = default;
_testObjectsAsRecyclableLongList?.Dispose();
_testObjectsAsRecyclableLongList = default;
base.Cleanup();
}
}
//[MemoryDiagnoser]
//public class ListVsLinkedListBenchmarks
//{
// [Benchmark]
// public void RecyclableList()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// using var list = new RecyclableList<object>(initialCapacity: dataCount);
// for (long i = 0; i < dataCount; i++)
// {
// list.Add(data[i]);
// }
// for (long i = 0; i < list.LongCount; i++)
// {
// DoNothing(list[i]);
// }
// list.Clear();
// }
// [Benchmark(Baseline = true)]
// public void RecyclableLongList()
// {
// var data = TestObjects;
// long dataCount = TestObjectCount;
// using var list = new RecyclableLongList<object>(BlockSize, totalItemsCount: dataCount);
// for (long i = 0; i < dataCount; i++)
// {
// list.Add(data[i]);
// }
// for (long i = 0; i < list.LongCount; i++)
// {
// DoNothing(list[i]);
// }
// list.Clear();
// }
// //[Benchmark]
// public void List()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new List<object>(capacity: dataCount);
// for (var i = 0; i < dataCount; i++)
// {
// list.Add(data[i]);
// }
// for (var i = 0; i < list.Count; i++)
// {
// DoNothing(list[i]);
// }
// list.Clear();
// }
// //[Benchmark]
// public void LinkedList()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new LinkedList<object>();
// for (var i = 0; i < dataCount; i++)
// {
// _ = list.AddLast(data[i]);
// }
// foreach (var node in list)
// {
// DoNothing(node);
// }
// list.Clear();
// }
// //[Benchmark]
// public void RecyclableQueue()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new RecyclableQueue<object>(BlockSize);
// for (var i = 0; i < dataCount; i++)
// {
// list.Enqueue(data[i]);
// }
// while (list.LongCount > 0)
// {
// DoNothing(list.Dequeue());
// }
// list.Clear();
// }
// //[Benchmark]
// public void Queue()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new Queue<object>(capacity: dataCount);
// for (var i = 0; i < dataCount; i++)
// {
// list.Enqueue(data[i]);
// }
// while (list.Count > 0)
// {
// DoNothing(list.Dequeue());
// }
// list.Clear();
// }
// //[Benchmark]
// public void PriorityQueue()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new PriorityQueue<object, long>(initialCapacity: dataCount);
// for (var i = 0; i < dataCount; i++)
// {
// list.Enqueue(data[i], i);
// }
// while (list.Count > 0)
// {
// DoNothing(list.Dequeue());
// }
// list.Clear();
// }
// //[Benchmark]
// public void RecyclableSortedList_InUpdateMode()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new RecyclableSortedList<long, object>(BlockSize);
// list.BeginUpdate();
// for (var i = 0; i < dataCount; i++)
// {
// list.Add(i, data[i]);
// }
// list.EndUpdate();
// for (var i = 0; i < list.LongCount; i++)
// {
// DoNothing(list[i]);
// }
// list.Clear();
// }
// //[Benchmark]
// public void RecyclableSortedList()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new RecyclableSortedList<long, object>(BlockSize);
// for (var i = 0; i < dataCount; i++)
// {
// list.Add(i, data[i]);
// }
// for (var i = 0; i < list.LongCount; i++)
// {
// DoNothing(list[i]);
// }
// list.Clear();
// }
// //[Benchmark]
// public void SortedList()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new SortedList<long, object>();
// for (var i = 0; i < dataCount; i++)
// {
// list.Add(i, data[i]);
// }
// for (var i = 0; i < list.Count; i++)
// {
// DoNothing(list[i]);
// }
// list.Clear();
// }
// //[Benchmark]
// public void RecyclableStack()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new RecyclableStack<object>(BlockSize);
// for (var i = 0; i < dataCount; i++)
// {
// list.Push(data[i]);
// }
// while (list.LongCount > 0)
// {
// DoNothing(list.Pop());
// }
// list.Clear();
// }
// //[Benchmark]
// public void Stack()
// {
// var data = TestObjects;
// var dataCount = TestObjectCount;
// var list = new Stack<object>();
// for (var i = 0; i < dataCount; i++)
// {
// list.Push(data[i]);
// }
// while (list.Count > 0)
// {
// DoNothing(list.Pop());
// }
// list.Clear();
// }
//}
}
File diff suppressed because it is too large. Load diff
+10
View File
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections", "R
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.CollectionsTests", "Recyclable.CollectionsTests\Recyclable.CollectionsTests.csproj", "{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Benchmarks", "Recyclable.Collections.Benchmarks\Recyclable.Collections.Benchmarks.csproj", "{CDC0CF78-FC11-43E3-9059-3261DA3639D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -31,6 +33,14 @@ Global
{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.Build.0 = Release|x64
{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|x64.ActiveCfg = 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.Build.0 = Release|Any CPU
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|x64.ActiveCfg = Release|x64
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -15,7 +15,6 @@ namespace Recyclable.Collections
{
return CompareResult.XEqualY;
}
#pragma warning disable IDE0046 // This is intentional due to better performance - IL contained unnecessary local variable.
else if (x != null && y == null)
{
return CompareResult.XGreaterThenY;
@@ -28,7 +27,6 @@ namespace Recyclable.Collections
{
return _comparison(x!, y!);
}
#pragma warning restore IDE0046
}
}
}
@@ -4,7 +4,7 @@ namespace Recyclable.Collections
{
internal static class ExceptionExtensions
{
public static void CaptureAndReThrow(this Exception ex)
public static void CaptureAndRethrow(this Exception ex)
{
ExceptionDispatchInfo.Capture(ex).Throw();
}
@@ -1,5 +1,10 @@
// Ignore Spelling: Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
// The source code was taken from
// https://codereview.stackexchange.com/questions/205407/quicksort-without-recursion.
-9
View File
@@ -31,14 +31,5 @@ namespace Recyclable.Collections
return result;
}
}
public static long Min(long number1, long number2, long number3) =>
number1 <= number2
? number1 <= number3
? number1
: number3
: number2 <= number3
? number2
: number3;
}
}
+2 -2
View File
@@ -13,12 +13,12 @@ namespace Recyclable.Collections
_memory = memory;
}
#pragma warning disable IDE0032 // This is intentional due to better performance
#pragma warning disable RCS1085
private T[] _memory;
public T[] Memory { get => _memory; set => _memory = value; }
private MemoryBucket<T>? _nextBucket;
#pragma warning restore IDE0032 // Use auto property
public MemoryBucket<T>? NextBucket { get => _nextBucket; set => _nextBucket = value; }
#pragma warning restore RCS1085
}
}
@@ -0,0 +1,20 @@
namespace Recyclable.Collections.Pools
{
public sealed class BiDirectionalRecyclableArrayPoolChunk<T>
{
private static readonly T[] _empty = Array.Empty<T>();
internal static T[] Empty => _empty;
internal T[] Value;
internal int Top;
internal int Bottom;
internal BiDirectionalRecyclableArrayPoolChunk<T>? Previous;
internal BiDirectionalRecyclableArrayPoolChunk<T>? Next;
public BiDirectionalRecyclableArrayPoolChunk()
{
Value = _empty;
}
}
}
@@ -0,0 +1,37 @@
using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Pools
{
public static class BiDirectionalRecyclableArrayPoolChunkPool<T>
{
private static readonly ObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>> _pool =
new DefaultObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<BiDirectionalRecyclableArrayPoolChunk<T>>());
public static BiDirectionalRecyclableArrayPoolChunk<T> Rent() => _pool.Get();
public static void Return(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{
chunk.Top = 0;
chunk.Bottom = 0;
chunk.Previous = null;
chunk.Next = null;
_pool.Return(chunk);
}
public static void Dispose(BiDirectionalRecyclableArrayPoolChunk<T> chunk, bool needsClearing)
{
var buffer = chunk.Value;
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
}
chunk.Value = BiDirectionalRecyclableArrayPoolChunk<T>.Empty;
chunk.Top = 0;
chunk.Bottom = 0;
chunk.Previous = null;
chunk.Next = null;
_pool.Return(chunk);
}
}
}
+14 -8
View File
@@ -9,18 +9,24 @@ namespace Recyclable.Collections.Pools
internal static class MemoryUtils
{
private const double HighPressureThreshold = .90; // Percent of GC memory pressure threshold we consider "high"
private const double MediumPressureThreshold = .70; // Percent of GC memory pressure threshold we consider "medium"
public static MemoryPressure GetMemoryPressure()
{
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"
GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo();
return memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold
? MemoryPressure.High
: memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold
? MemoryPressure.Medium
: MemoryPressure.Low;
if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold)
{
return MemoryPressure.High;
}
if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold)
{
return MemoryPressure.Medium;
}
return MemoryPressure.Low;
}
}
}
@@ -0,0 +1,19 @@
namespace Recyclable.Collections.Pools
{
public sealed class RecyclableArrayPoolChunk<T>
{
private static readonly T[] _empty = Array.Empty<T>();
internal static T[] Empty => _empty;
internal T[] Value;
internal int Index;
internal RecyclableArrayPoolChunk<T>? Previous;
internal RecyclableArrayPoolChunk<T>? Next;
public RecyclableArrayPoolChunk()
{
Value = _empty;
}
}
}
@@ -0,0 +1,34 @@
using Microsoft.Extensions.ObjectPool;
namespace Recyclable.Collections.Pools
{
public static class RecyclableArrayPoolChunkPool<T>
{
private static readonly ObjectPool<RecyclableArrayPoolChunk<T>> _pool =
new DefaultObjectPool<RecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<RecyclableArrayPoolChunk<T>>());
public static RecyclableArrayPoolChunk<T> Rent() => _pool.Get();
public static void Return(RecyclableArrayPoolChunk<T> chunk)
{
chunk.Index = 0;
chunk.Previous = null;
chunk.Next = null;
_pool.Return(chunk);
}
public static void Dispose(RecyclableArrayPoolChunk<T> chunk, bool needsClearing)
{
var buffer = chunk.Value;
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
}
chunk.Value = RecyclableArrayPoolChunk<T>.Empty;
chunk.Index = 0;
chunk.Previous = null;
chunk.Next = null;
_pool.Return(chunk);
}
}
}
@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DefineConstants>$(DefineConstants);WITH_VERSIONING</DefineConstants>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>Recyclable.Collections</PackageId>
<PackageVersion>0.0.6.2</PackageVersion>
<PackageVersion>0.0.6</PackageVersion>
<Authors>Marek Lemańczyk</Authors>
<Company>N/A</Company>
<Product>Recyclable.Collections</Product>
@@ -17,15 +18,11 @@
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
<PackageReleaseNotes>Various bug fixes and improvements, primarily for .IndexOf().</PackageReleaseNotes>
<PackageReleaseNotes>List&lt;T&gt; compatibility pack added for RecyclableList&lt;T&gt;. Various performance improvements and fixes.</PackageReleaseNotes>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>logo.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<None Include="..\logo.png" Pack="true" PackagePath="" Link="logo.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
@@ -35,6 +32,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
<PackageReference Include="morelinq" Version="3.4.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VersionedCollectionsGenerator\VersionedCollectionsGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
@@ -0,0 +1,406 @@
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal sealed class RecyclableDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IDisposable
where TKey : notnull
{
internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType;
internal readonly int _blockSize;
internal readonly int _blockSizeMinus1;
internal readonly byte _blockShift;
#nullable disable
internal Entry[][] _entries;
#nullable restore
internal int[] _buckets;
internal int _count;
internal bool _disposed;
internal struct Entry
{
public int HashCode;
public int Next;
public TKey Key;
public TValue Value;
}
public RecyclableDictionary(int blockSize = RecyclableDefaults.BlockSize)
{
if (!BitOperations.IsPow2((uint)blockSize))
{
blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize);
}
_blockSize = blockSize;
_blockSizeMinus1 = blockSize - 1;
_blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize));
_entries = 4 >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(4)
: new Entry[4][];
_entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(blockSize)
: new Entry[blockSize];
_buckets = new int[4];
Array.Fill(_buckets, -1);
}
public int Count => _count;
public TValue this[TKey key]
{
get
{
var index = FindIndex(key, out _);
if (index >= 0)
{
return GetEntry(index).Value;
}
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "Key not found");
return default!;
}
set
{
var index = FindIndex(key, out _);
if (index >= 0)
{
ref var entry = ref GetEntry(index);
entry.Value = value;
return;
}
Add(key, value);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(TKey key, TValue value)
{
var hash = key.GetHashCode() & int.MaxValue;
if (_count >= (_buckets.Length * 3) / 4)
{
ResizeBuckets(_buckets.Length * 2);
}
EnsureCapacity(_count + 1);
int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket];
while (index >= 0)
{
ref var check = ref GetEntry(index);
if (check.HashCode == hash && EqualityComparer<TKey>.Default.Equals(check.Key, key))
{
throw new ArgumentException("An element with the same key already exists.", nameof(key));
}
index = check.Next;
}
int newIndex = _count++;
ref var entry = ref GetEntry(newIndex);
entry.HashCode = hash;
entry.Key = key;
entry.Value = value;
entry.Next = _buckets[bucket];
_buckets[bucket] = newIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(TKey key) => FindIndex(key, out _) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TKey key, out TValue value)
{
var index = FindIndex(key, out _);
if (index >= 0)
{
value = GetEntry(index).Value;
return true;
}
value = default!;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(TKey key)
{
if (_count == 0)
{
return false;
}
var hash = key.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1);
int prev = -1;
int index = _buckets[bucket];
while (index >= 0)
{
ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key))
{
if (prev < 0)
{
_buckets[bucket] = entry.Next;
}
else
{
GetEntry(prev).Next = entry.Next;
}
int lastIndex = _count - 1;
if (index != lastIndex)
{
MoveEntry(lastIndex, index);
}
ClearEntry(lastIndex);
_count--;
return true;
}
prev = index;
index = entry.Next;
}
return false;
}
public void Clear()
{
if (_count == 0)
{
Array.Fill(_buckets, -1);
return;
}
int blockIndex = 0;
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing);
_entries[blockIndex++] = null!;
}
}
else
{
if (_needsClearing)
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length);
_entries[blockIndex++] = null!;
}
}
else
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
_entries[blockIndex++] = null!;
}
}
}
_count = 0;
Array.Fill(_buckets, -1);
}
public TKey GetKey(int index) => GetEntry(index).Key;
public TValue GetValue(int index) => GetEntry(index).Value;
public Enumerator GetEnumerator() => new(this);
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
public void Dispose()
{
if (_disposed)
{
return;
}
Clear();
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
}
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<int>.ReturnShared(_buckets, false);
}
_entries = Array.Empty<Entry[]>();
_buckets = Array.Empty<int>();
_disposed = true;
GC.SuppressFinalize(this);
}
private ref Entry GetEntry(int index)
{
return ref _entries[index >> _blockShift][index & _blockSizeMinus1];
}
private void ClearEntry(int index)
{
ref var entry = ref GetEntry(index);
entry.HashCode = 0;
entry.Next = -1;
if (_needsClearing)
{
entry.Key = default!;
entry.Value = default!;
}
}
private void MoveEntry(int sourceIndex, int targetIndex)
{
ref var source = ref GetEntry(sourceIndex);
ref var target = ref GetEntry(targetIndex);
target = source;
int bucket = source.HashCode & (_buckets.Length - 1);
int cur = _buckets[bucket];
int prev = -1;
while (cur != sourceIndex)
{
prev = cur;
cur = GetEntry(cur).Next;
}
if (prev < 0)
{
_buckets[bucket] = targetIndex;
}
else
{
GetEntry(prev).Next = targetIndex;
}
}
private void EnsureCapacity(int min)
{
int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift;
if (_entries.Length < requiredBlocks)
{
var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks)
: new Entry[requiredBlocks][];
Array.Copy(_entries, newBlocks, _entries.Length);
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
}
_entries = newBlocks;
}
for (int i = 0; i < requiredBlocks; i++)
{
if (_entries[i] == null)
{
_entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(_blockSize)
: new Entry[_blockSize];
}
}
}
private void ResizeBuckets(int newSize)
{
var newBuckets = new int[newSize];
Array.Fill(newBuckets, -1);
for (int i = 0; i < _count; i++)
{
ref var entry = ref GetEntry(i);
int bucket = entry.HashCode & (newSize - 1);
entry.Next = newBuckets[bucket];
newBuckets[bucket] = i;
}
_buckets = newBuckets;
}
private int FindIndex(TKey key, out int hash)
{
if (_count == 0)
{
hash = 0;
return -1;
}
hash = key.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket];
while (index >= 0)
{
ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key))
{
return index;
}
index = entry.Next;
}
return -1;
}
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
{
private readonly RecyclableDictionary<TKey, TValue> _dictionary;
private int _index;
private KeyValuePair<TKey, TValue> _current;
internal Enumerator(RecyclableDictionary<TKey, TValue> dictionary)
{
_dictionary = dictionary;
_index = 0;
_current = default;
}
public KeyValuePair<TKey, TValue> Current => _current;
object IEnumerator.Current => _current;
public bool MoveNext()
{
if (_index >= _dictionary._count)
{
return false;
}
var entry = _dictionary.GetEntry(_index++);
_current = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
return true;
}
public void Reset()
{
_index = 0;
_current = default;
}
public void Dispose()
{
}
}
}
}
+362
View File
@@ -0,0 +1,362 @@
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal sealed class RecyclableHashSet<T> : IEnumerable<T>, IDisposable
where T : notnull
{
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
internal readonly int _blockSize;
internal readonly int _blockSizeMinus1;
internal readonly byte _blockShift;
#nullable disable
internal Entry[][] _entries;
#nullable restore
internal int[] _buckets;
internal int _count;
internal bool _disposed;
internal struct Entry
{
public int HashCode;
public int Next;
public T Value;
}
public RecyclableHashSet(int blockSize = RecyclableDefaults.BlockSize)
{
if (!BitOperations.IsPow2((uint)blockSize))
{
blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize);
}
_blockSize = blockSize;
_blockSizeMinus1 = blockSize - 1;
_blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize));
_entries = 4 >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(4)
: new Entry[4][];
_entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(blockSize)
: new Entry[blockSize];
_buckets = new int[4];
Array.Fill(_buckets, -1);
}
public int Count => _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add(T item)
{
var hash = item.GetHashCode() & int.MaxValue;
if (_count >= (_buckets.Length * 3) / 4)
{
ResizeBuckets(_buckets.Length * 2);
}
EnsureCapacity(_count + 1);
int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket];
while (index >= 0)
{
ref var check = ref GetEntry(index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, item))
{
return false;
}
index = check.Next;
}
int newIndex = _count++;
ref var entry = ref GetEntry(newIndex);
entry.HashCode = hash;
entry.Value = item;
entry.Next = _buckets[bucket];
_buckets[bucket] = newIndex;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(T item) => FindIndex(item, out _) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(T item)
{
if (_count == 0)
{
return false;
}
var hash = item.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1);
int prev = -1;
int index = _buckets[bucket];
while (index >= 0)
{
ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item))
{
if (prev < 0)
{
_buckets[bucket] = entry.Next;
}
else
{
GetEntry(prev).Next = entry.Next;
}
int lastIndex = _count - 1;
if (index != lastIndex)
{
MoveEntry(lastIndex, index);
}
ClearEntry(lastIndex);
_count--;
return true;
}
prev = index;
index = entry.Next;
}
return false;
}
public void Clear()
{
if (_count == 0)
{
Array.Fill(_buckets, -1);
return;
}
int blockIndex = 0;
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing);
_entries[blockIndex++] = null!;
}
}
else
{
if (_needsClearing)
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length);
_entries[blockIndex++] = null!;
}
}
else
{
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
{
_entries[blockIndex++] = null!;
}
}
}
_count = 0;
Array.Fill(_buckets, -1);
}
public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
public void Dispose()
{
if (_disposed)
{
return;
}
Clear();
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
}
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<int>.ReturnShared(_buckets, false);
}
_entries = Array.Empty<Entry[]>();
_buckets = Array.Empty<int>();
_disposed = true;
GC.SuppressFinalize(this);
}
private ref Entry GetEntry(int index)
{
return ref _entries[index >> _blockShift][index & _blockSizeMinus1];
}
private void ClearEntry(int index)
{
ref var entry = ref GetEntry(index);
entry.HashCode = 0;
entry.Next = -1;
if (_needsClearing)
{
entry.Value = default!;
}
}
private void MoveEntry(int sourceIndex, int targetIndex)
{
ref var source = ref GetEntry(sourceIndex);
ref var target = ref GetEntry(targetIndex);
target = source;
int bucket = source.HashCode & (_buckets.Length - 1);
int cur = _buckets[bucket];
int prev = -1;
while (cur != sourceIndex)
{
prev = cur;
cur = GetEntry(cur).Next;
}
if (prev < 0)
{
_buckets[bucket] = targetIndex;
}
else
{
GetEntry(prev).Next = targetIndex;
}
}
private void EnsureCapacity(int min)
{
int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift;
if (_entries.Length < requiredBlocks)
{
var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks)
: new Entry[requiredBlocks][];
Array.Copy(_entries, newBlocks, _entries.Length);
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
}
_entries = newBlocks;
}
for (int i = 0; i < requiredBlocks; i++)
{
if (_entries[i] == null)
{
_entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<Entry>.RentShared(_blockSize)
: new Entry[_blockSize];
}
}
}
private void ResizeBuckets(int newSize)
{
int[] newBuckets = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<int>.RentShared(newSize)
: new int[newSize];
Array.Fill(newBuckets, -1);
for (int i = 0; i < _count; i++)
{
ref var entry = ref GetEntry(i);
int bucket = entry.HashCode & (newSize - 1);
entry.Next = newBuckets[bucket];
newBuckets[bucket] = i;
}
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<int>.ReturnShared(_buckets, false);
}
_buckets = newBuckets;
}
private int FindIndex(T item, out int hash)
{
if (_count == 0)
{
hash = 0;
return -1;
}
hash = item.GetHashCode() & int.MaxValue;
int bucket = hash & (_buckets.Length - 1);
int index = _buckets[bucket];
while (index >= 0)
{
ref var entry = ref GetEntry(index);
if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item))
{
return index;
}
index = entry.Next;
}
return -1;
}
public struct Enumerator : IEnumerator<T>
{
private readonly RecyclableHashSet<T> _set;
private int _index;
private T? _current;
internal Enumerator(RecyclableHashSet<T> set)
{
_set = set;
_index = 0;
_current = default;
}
public T Current => _current!;
object IEnumerator.Current => _current!;
public bool MoveNext()
{
if (_index >= _set._count)
{
return false;
}
_current = _set.GetEntry(_index++).Value;
return true;
}
public void Reset()
{
_index = 0;
_current = default;
}
public void Dispose()
{
}
}
}
}
@@ -0,0 +1,323 @@
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal sealed class RecyclableLinkedList<T> : IEnumerable<T>, IDisposable
{
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous, bool forHead)
{
var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent();
if (chunk.Value.Length < size)
{
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
}
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(size)
: new T[size];
}
if (forHead)
{
chunk.Top = size;
chunk.Bottom = size;
}
else
{
chunk.Top = 0;
chunk.Bottom = 0;
}
chunk.Previous = previous;
chunk.Next = null;
return chunk;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReturnChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{
BiDirectionalRecyclableArrayPoolChunkPool<T>.Return(chunk);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DisposeChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{
BiDirectionalRecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing);
}
internal BiDirectionalRecyclableArrayPoolChunk<T> _head;
internal BiDirectionalRecyclableArrayPoolChunk<T> _tail;
internal bool _disposed;
internal long _capacity;
internal long _count;
public RecyclableLinkedList(int initialCapacity = RecyclableDefaults.InitialCapacity)
{
if (initialCapacity < 1)
{
initialCapacity = 1;
}
if (!BitOperations.IsPow2((uint)initialCapacity))
{
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
}
_head = RentChunk(initialCapacity, null, false);
_tail = _head;
_capacity = initialCapacity;
_count = 0;
}
public RecyclableLinkedList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity)
{
foreach (var item in source)
{
AddLast(item);
}
}
public int Count => checked((int)_count);
public long LongCount => _count;
public void AddLast(T item)
{
if (_tail.Top == _tail.Value.Length)
{
GrowTail();
}
_tail.Value[_tail.Top++] = item;
_count++;
}
public void AddFirst(T item)
{
if (_head.Bottom == 0)
{
GrowHead();
}
_head.Value[--_head.Bottom] = item;
_count++;
}
public T RemoveFirst()
{
if (_count == 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
}
_count--;
var value = _head.Value[_head.Bottom];
if (_needsClearing)
{
_head.Value[_head.Bottom] = default!;
}
_head.Bottom++;
if (_head.Bottom == _head.Top && _head.Next != null)
{
ReleaseHeadChunk();
}
else if (_head.Bottom == _head.Top)
{
_head.Bottom = _head.Top;
}
return value;
}
public T RemoveLast()
{
if (_count == 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
}
_count--;
_tail.Top--;
var value = _tail.Value[_tail.Top];
if (_needsClearing)
{
_tail.Value[_tail.Top] = default!;
}
if (_tail.Top == _tail.Bottom && _tail.Previous != null)
{
ReleaseTailChunk();
}
else if (_tail.Top == _tail.Bottom)
{
_tail.Top = _tail.Bottom;
}
return value;
}
public void Clear()
{
while (_head.Next != null)
{
ReleaseHeadChunk();
}
while (_tail.Previous != null)
{
ReleaseTailChunk();
}
if (_needsClearing && _head.Top > _head.Bottom)
{
Array.Clear(_head.Value, _head.Bottom, _head.Top - _head.Bottom);
}
_capacity = _head.Value.Length;
_head.Bottom = _head.Top;
_tail = _head;
_count = 0;
}
public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
private void GrowTail()
{
int newSize;
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
{
newSize = RecyclableDefaults.MaxPooledBlockSize;
}
else
{
var doubled = _capacity << 1;
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
}
var newChunk = RentChunk(newSize, _tail, false);
_tail.Next = newChunk;
_tail = newChunk;
_capacity += newSize;
}
private void GrowHead()
{
int newSize;
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
{
newSize = RecyclableDefaults.MaxPooledBlockSize;
}
else
{
var doubled = _capacity << 1;
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
}
var newChunk = RentChunk(newSize, null, true);
newChunk.Next = _head;
_head.Previous = newChunk;
_head = newChunk;
_capacity += newSize;
}
private void ReleaseHeadChunk()
{
var toReturn = _head;
_head = toReturn.Next!;
_head.Previous = null;
_capacity -= toReturn.Value.Length;
ReturnChunk(toReturn);
}
private void ReleaseTailChunk()
{
var toReturn = _tail;
_tail = toReturn.Previous!;
_tail.Next = null;
_capacity -= toReturn.Value.Length;
ReturnChunk(toReturn);
}
public void Dispose()
{
if (_disposed)
{
return;
}
var chunk = _head;
while (chunk != null)
{
var next = chunk.Next;
DisposeChunk(chunk);
chunk = next;
}
_disposed = true;
GC.SuppressFinalize(this);
}
public struct Enumerator : IEnumerator<T>
{
private readonly BiDirectionalRecyclableArrayPoolChunk<T>? _start;
private BiDirectionalRecyclableArrayPoolChunk<T>? _chunk;
private int _index;
private T? _current;
internal Enumerator(RecyclableLinkedList<T> list)
{
_start = list._head;
_chunk = list._head;
_index = _chunk != null ? _chunk.Bottom : 0;
_current = default;
}
public T Current => _current!;
object IEnumerator.Current => _current!;
public bool MoveNext()
{
if (_chunk == null)
{
return false;
}
if (_index >= _chunk.Top)
{
_chunk = _chunk.Next;
if (_chunk == null)
{
return false;
}
_index = _chunk.Bottom;
}
_current = _chunk.Value[_index++];
return true;
}
public void Reset()
{
_chunk = _start;
_index = _chunk != null ? _chunk.Bottom : 0;
}
public void Dispose()
{
_chunk = null;
_current = default;
}
}
}
}
@@ -1,13 +1,80 @@
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Linq.Expressions;
using System.Reflection;
namespace Recyclable.Collections
{
#pragma warning disable IDE1006 // This is intentional naming for the class to show up at the end of IntelliSense list.
public static class zRecyclableListAddRange
#pragma warning restore IDE1006 // This is intentional naming for the class to show up at the end of IntelliSense list.
{
private static class AddRangeHelper<T>
{
private static readonly Action<RecyclableList<T>, object>? _dictionaryAdder;
private static readonly Action<RecyclableList<T>, object>? _sortedListAdder;
private static readonly Type? _dictionaryType;
private static readonly Type? _sortedListType;
static AddRangeHelper()
{
Type elementType = typeof(T);
if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
{
Type[] args = elementType.GetGenericArguments();
_dictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args);
MethodInfo? method = typeof(zRecyclableListAddRange).GetMethod(
nameof(AddRange),
BindingFlags.NonPublic | BindingFlags.Static,
binder: null,
types: new[] { typeof(RecyclableList<>).MakeGenericType(elementType), _dictionaryType },
modifiers: null);
if (method != null)
{
var listParam = Expression.Parameter(typeof(RecyclableList<T>));
var objParam = Expression.Parameter(typeof(object));
var call = Expression.Call(method, listParam, Expression.Convert(objParam, _dictionaryType));
_dictionaryAdder = Expression.Lambda<Action<RecyclableList<T>, object>>(call, listParam, objParam).Compile();
}
}
else if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(ValueTuple<,>))
{
Type[] args = elementType.GetGenericArguments();
_sortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args);
MethodInfo? method = typeof(zRecyclableListAddRange).GetMethod(
nameof(AddRange),
BindingFlags.NonPublic | BindingFlags.Static,
binder: null,
types: new[] { typeof(RecyclableList<>).MakeGenericType(elementType), _sortedListType },
modifiers: null);
if (method != null)
{
var listParam = Expression.Parameter(typeof(RecyclableList<T>));
var objParam = Expression.Parameter(typeof(object));
var call = Expression.Call(method, listParam, Expression.Convert(objParam, _sortedListType));
_sortedListAdder = Expression.Lambda<Action<RecyclableList<T>, object>>(call, listParam, objParam).Compile();
}
}
}
internal static bool TryAddRange(RecyclableList<T> list, IEnumerable<T> items)
{
if (_dictionaryAdder != null && _dictionaryType!.IsInstanceOfType(items))
{
_dictionaryAdder(list, items);
return true;
}
if (_sortedListAdder != null && _sortedListType!.IsInstanceOfType(items))
{
_sortedListAdder(list, items);
return true;
}
return false;
}
}
private static void AddRangeEnumerated<T>(this RecyclableList<T> list, IEnumerable<T> items, int growByCount)
{
var enumerator = items.GetEnumerator();
@@ -60,6 +127,7 @@ namespace Recyclable.Collections
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
[VersionedCollectionsGenerator.RewriteForVersioned]
public static void AddRange<T>(this RecyclableList<T> list, in Array items)
{
if (list._capacity < list._count + items.Length)
@@ -69,7 +137,9 @@ namespace Recyclable.Collections
Array.Copy(items, items.GetLowerBound(0), list._memoryBlock, list._count, items.Length);
list._count += items.Length;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -82,7 +152,9 @@ namespace Recyclable.Collections
new ReadOnlySpan<T>(items).CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
list._count += items.Length;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -95,7 +167,9 @@ namespace Recyclable.Collections
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
list._count += items.Length;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -108,7 +182,9 @@ namespace Recyclable.Collections
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
list._count += items.Length;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -121,7 +197,9 @@ namespace Recyclable.Collections
items.CopyTo(list._memoryBlock, list._count);
list._count += items.Count;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -134,7 +212,9 @@ namespace Recyclable.Collections
items.CopyTo(list._memoryBlock, list._count);
list._count += items.Count;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -147,7 +227,9 @@ namespace Recyclable.Collections
items.CopyTo(list._memoryBlock, list._count);
list._count += items.Count;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -160,7 +242,9 @@ namespace Recyclable.Collections
new ReadOnlySpan<T>(items._memoryBlock, 0, items._count).CopyTo(new Span<T>(list._memoryBlock, list._count, items._count));
list._count += items._count;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -211,7 +295,279 @@ namespace Recyclable.Collections
}
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableHashSet<T> items)
{
if (items._count == 0)
{
return;
}
int oldCount = list._count,
sourceCount = items._count,
targetCapacity = oldCount + sourceCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
Span<T> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
var entries = items._entries;
int shift = items._blockShift,
mask = items._blockSizeMinus1;
for (int i = 0; i < sourceCount; i++)
{
targetSpan[i] = entries[i >> shift][i & mask].Value;
}
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableStack<T> items)
{
if (items._count == 0)
{
return;
}
if (items._count > int.MaxValue - list._count)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), "The total number of items exceeds the capacity of RecyclableList.");
}
int oldCount = list._count,
sourceCount = checked((int)items._count),
targetCapacity = oldCount + sourceCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
Span<T> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
int index = 0;
var chunk = items._current;
while (chunk != null)
{
var array = chunk.Value;
for (int i = chunk.Index - 1; i >= 0; i--)
{
targetSpan[index++] = array[i];
}
chunk = chunk.Previous;
}
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableSortedSet<T> items)
{
if (items._count == 0)
{
return;
}
int oldCount = list._count,
sourceCount = items._count,
targetCapacity = oldCount + sourceCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
new ReadOnlySpan<T>(items._items, 0, sourceCount).CopyTo(new Span<T>(list._memoryBlock, oldCount, sourceCount));
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableLinkedList<T> items)
{
if (items._count == 0)
{
return;
}
if (items._count > int.MaxValue - list._count)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), "The total number of items exceeds the capacity of RecyclableList.");
}
int oldCount = list._count,
sourceCount = checked((int)items._count),
targetCapacity = oldCount + sourceCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
Span<T> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
int index = 0;
var chunk = items._head;
while (chunk != null)
{
for (int i = chunk.Bottom; i < chunk.Top; i++)
{
targetSpan[index++] = chunk.Value[i];
}
chunk = chunk.Next;
}
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableList<T> list, RecyclablePriorityQueue<T> items)
{
if (items._size == 0)
{
return;
}
int oldCount = list._count,
sourceCount = items._size,
targetCapacity = oldCount + sourceCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
new ReadOnlySpan<T>(items._heap, 0, sourceCount).CopyTo(new Span<T>(list._memoryBlock, oldCount, sourceCount));
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableQueue<T> items)
{
if (items._count == 0)
{
return;
}
if (items._count > int.MaxValue - list._count)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), "The total number of items exceeds the capacity of RecyclableList.");
}
int oldCount = list._count,
sourceCount = checked((int)items._count),
targetCapacity = oldCount + sourceCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
Span<T> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
int index = 0;
var chunk = items._head;
while (chunk != null)
{
for (int i = chunk.Bottom; i < chunk.Top; i++)
{
targetSpan[index++] = chunk.Value[i];
}
chunk = chunk.Next;
}
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<TKey, TValue>(this RecyclableList<KeyValuePair<TKey, TValue>> list, RecyclableDictionary<TKey, TValue> items)
{
if (items._count == 0)
{
return;
}
int oldCount = list._count,
sourceCount = items._count,
targetCapacity = oldCount + sourceCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<KeyValuePair<TKey, TValue>>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
Span<KeyValuePair<TKey, TValue>> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
var entries = items._entries;
int shift = items._blockShift,
mask = items._blockSizeMinus1;
for (int i = 0; i < sourceCount; i++)
{
ref var entry = ref entries[i >> shift][i & mask];
targetSpan[i] = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
}
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<TKey, TValue>(this RecyclableList<(TKey Key, TValue Value)> list, RecyclableSortedList<TKey, TValue> items)
{
if (items._count == 0)
{
return;
}
int oldCount = list._count,
sourceCount = items._count,
targetCapacity = oldCount + sourceCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<(TKey Key, TValue Value)>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
Span<(TKey Key, TValue Value)> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
for (int i = 0; i < sourceCount; i++)
{
targetSpan[i] = (items._keys[i], items._values[i]);
}
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
public static IEnumerator AddRange<T>(this RecyclableList<T> list, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
@@ -301,6 +657,33 @@ namespace Recyclable.Collections
{
AddRange(list, sourceICollectionWithObjects);
}
else if (items is RecyclableHashSet<T> sourceHashSet)
{
AddRange(list, sourceHashSet);
}
else if (items is RecyclableStack<T> sourceStack)
{
AddRange(list, sourceStack);
}
else if (items is RecyclableSortedSet<T> sourceSortedSet)
{
AddRange(list, sourceSortedSet);
}
else if (items is RecyclableLinkedList<T> sourceLinkedList)
{
AddRange(list, sourceLinkedList);
}
else if (items is RecyclablePriorityQueue<T> sourcePriorityQueue)
{
AddRange(list, sourcePriorityQueue);
}
else if (items is RecyclableQueue<T> sourceQueue)
{
AddRange(list, sourceQueue);
}
else if (AddRangeHelper<T>.TryAddRange(list, items))
{
}
else if (items is IReadOnlyList<T> sourceIReadOnlyList)
{
AddRange(list, sourceIReadOnlyList);
@@ -308,12 +691,16 @@ namespace Recyclable.Collections
else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0)
{
AddRangeWithKnownCount(list, items, list._count, requiredAdditionalCapacity);
#if WITH_VERSIONING
list._version++;
#endif
}
else
{
AddRangeEnumerated(list, items, growByCount);
#if WITH_VERSIONING
list._version++;
#endif
}
}
}
@@ -0,0 +1,272 @@
using System.Numerics;
namespace Recyclable.Collections
{
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
public static class zRecyclableListCompatibilityListFind
{
public static bool Exists<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return false;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return true;
}
}
return false;
}
public static T? Find<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return default;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return sourceSpan[itemIndex];
}
}
return default;
}
public static RecyclableList<T> FindAll<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return new();
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
RecyclableList<T> result = new(sourceItemsCount >> 3);
Span<T> resultSpan = result._memoryBlock;
int capacity = result._capacity,
resultItemsCount = 0;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
if (resultItemsCount + 1 > capacity)
{
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1));
_ = RecyclableListHelpers<T>.EnsureCapacity(result, resultItemsCount, capacity);
resultSpan = result._memoryBlock;
}
resultSpan[resultItemsCount++] = sourceSpan[itemIndex];
}
}
result._capacity = capacity;
result._count = resultItemsCount;
return result;
}
public static RecyclableList<int> FindAllIndexes<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return new();
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
RecyclableList<int> result = new(sourceItemsCount >> 3);
Span<int> resultSpan = result._memoryBlock;
int capacity = result._capacity,
resultItemsCount = 0;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
if (resultItemsCount + 1 > capacity)
{
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1));
_ = RecyclableListHelpers<int>.EnsureCapacity(result, resultItemsCount, capacity);
resultSpan = result._memoryBlock;
}
resultSpan[resultItemsCount++] = itemIndex;
}
}
result._capacity = capacity;
result._count = resultItemsCount;
return result;
}
public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0 || startIndex >= sourceItemsCount)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount);
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0 || startIndex >= sourceItemsCount)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount);
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static int FindIndex<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static T? FindLast<T>(this RecyclableList<T> list, Predicate<T> match)
{
if (list._count == 0)
{
return default;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--)
{
if (match(sourceSpan[itemIndex]))
{
return sourceSpan[itemIndex];
}
}
return default;
}
public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match)
{
if (count == 0)
{
if (startIndex != -1)
{
ThrowHelper.ThrowArgumentOutOfRangeException_Index();
}
}
else if (count < 0 || startIndex - count + 1 < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException_Count();
}
if (startIndex < 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
int sourceItemsCount = count; // startIndex - count + 1 >= 0 ? count : startIndex + 1;
if (sourceItemsCount == 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
int lastItemIndex = startIndex - sourceItemsCount;
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex > lastItemIndex; itemIndex--)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match)
{
if (list._count == 0 || startIndex < 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex >= 0; itemIndex--)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static int FindLastIndex<T>(this RecyclableList<T> list, Predicate<T> match)
{
if (list._count == 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
}
}
@@ -0,0 +1,26 @@
namespace Recyclable.Collections
{
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
public static class zRecyclableListCompatibilityListIndexOf
{
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)
: RecyclableDefaults.ItemNotFoundIndex;
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)
: RecyclableDefaults.ItemNotFoundIndex;
public static int LastIndexOf<T>(this RecyclableList<T> list, T item) => list._count != 0
? Array.LastIndexOf(list._memoryBlock, item, list._count - 1)
: RecyclableDefaults.ItemNotFoundIndex;
public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0
? Array.LastIndexOf(list._memoryBlock, item, index)
: RecyclableDefaults.ItemNotFoundIndex;
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)
: RecyclableDefaults.ItemNotFoundIndex;
}
}
@@ -0,0 +1,378 @@
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Recyclable.Collections
{
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
public static class zRecyclableListCompatibilityListInsertRange
{
private static void InsertRangeEnumerated<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int growByCount)
{
var enumerator = items.GetEnumerator();
if (!enumerator.MoveNext())
{
return;
}
int capacity = list._capacity,
added = 0,
available = capacity - list._count,
i,
movingBlockLength = list._count - index;
T[] memoryBlock = list._memoryBlock;
if (available > 0)
{
Array.Copy(memoryBlock, index, memoryBlock, capacity - movingBlockLength, movingBlockLength);
}
Span<T> memorySpan = memoryBlock;
do
{
if (index + available + movingBlockLength > capacity)
{
var capacityBeforeIncrease = capacity;
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)(capacity + growByCount)));
_ = RecyclableListHelpers<T>.EnsureCapacity(list, memoryBlock.Length, capacity);
memorySpan = memoryBlock = list._memoryBlock;
available = capacity - index - movingBlockLength;
Array.Copy(memoryBlock, capacityBeforeIncrease - movingBlockLength, memoryBlock, capacity - movingBlockLength, movingBlockLength);
}
for (i = 0; i < available; i++)
{
memorySpan[index++] = enumerator.Current;
if (!enumerator.MoveNext())
{
added++;
break;
}
}
added += i;
}
while (i == available);
if (i != available)
{
Array.Copy(memoryBlock, capacity - movingBlockLength, memoryBlock, index, movingBlockLength);
if (RecyclableList<T>._needsClearing)
{
Array.Clear(memoryBlock, capacity - movingBlockLength, movingBlockLength);
}
}
list._count += added;
list._capacity = capacity;
}
private static void InsertRangeWithKnownCount<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int currentItemsCount, int requiredAdditionalCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity))));
Array.Copy(list._memoryBlock, index, list._memoryBlock, index + requiredAdditionalCapacity, list._count - index);
Span<T> memorySpan = list._memoryBlock;
foreach (var item in items)
{
memorySpan[index++] = item;
}
list._count += requiredAdditionalCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, in Array items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
Array.Copy(items, items.GetLowerBound(0), memoryBlock, index, items.Length);
list._count += items.Length;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, in T[] items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
new ReadOnlySpan<T>(items).CopyTo(new Span<T>(memoryBlock, index, items.Length));
list._count += items.Length;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, ReadOnlySpan<T> items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
items.CopyTo(new Span<T>(memoryBlock, index, items.Length));
list._count += items.Length;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, Span<T> items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
items.CopyTo(new Span<T>(memoryBlock, index, items.Length));
list._count += items.Length;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, List<T> items)
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
items.CopyTo(memoryBlock, index);
list._count += items.Count;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, ICollection items)
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
items.CopyTo(list._memoryBlock, index);
list._count += items.Count;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, ICollection<T> items)
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
items.CopyTo(memoryBlock, index);
list._count += items.Count;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, RecyclableList<T> items)
{
if (list._capacity < list._count + items._count)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items._count))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
new ReadOnlySpan<T>(items._memoryBlock, 0, items._count).CopyTo(new(list._memoryBlock, index, items._count));
list._count += items._count;
#if WITH_VERSIONING
list._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, RecyclableLongList<T> items)
{
if (items._longCount == 0)
{
return;
}
int oldCount = list._count;
long sourceItemsCount = items._longCount;
if (oldCount + sourceItemsCount > int.MaxValue)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items in source and target table exceeds the maximum capacity of {nameof(RecyclableList<T>)}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList<T>)}, instead");
}
int targetCapacity = oldCount + (int)sourceItemsCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, oldCount - index);
Span<T> targetSpan = new(list._memoryBlock, index, list._capacity - index),
itemsSpan;
int blockIndex,
sourceBlockSize = items._blockSize,
lastBlockIndex = (int)(sourceItemsCount >> items._blockSizePow2BitShift) - 1;
for (blockIndex = 0; blockIndex <= lastBlockIndex; blockIndex++)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, sourceBlockSize);
itemsSpan.CopyTo(targetSpan);
targetSpan = targetSpan[sourceBlockSize..];
}
if (blockIndex == 0)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, (int)sourceItemsCount);
itemsSpan.CopyTo(targetSpan);
}
else if (blockIndex <= items._lastBlockWithData)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, items._nextItemIndex > 0 ? items._nextItemIndex : sourceBlockSize);
itemsSpan.CopyTo(targetSpan);
}
list._count = targetCapacity;
#if WITH_VERSIONING
list._version++;
#endif
}
public static IEnumerator InsertRange<T>(this RecyclableList<T> list, int index, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
{
int targetItemIndex = list._count;
Span<T> memorySpan;
int i;
var enumerator = source.GetEnumerator();
int capacity = list._capacity;
memorySpan = list._memoryBlock;
if (enumerator.MoveNext())
{
int available = capacity - targetItemIndex;
do
{
if (targetItemIndex + growByCount > capacity)
{
capacity = RecyclableListHelpers<T>.EnsureCapacity(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount))));
memorySpan = list._memoryBlock;
available = capacity - targetItemIndex;
}
for (i = 0; i < available; i++)
{
memorySpan[targetItemIndex++] = (T)enumerator.Current;
if (!enumerator.MoveNext())
{
break;
}
}
}
while (i >= available);
}
list._capacity = capacity;
list._count = targetItemIndex;
return enumerator;
}
public static void InsertRange<T>(this RecyclableList<T> list, int index, IReadOnlyList<T> items)
{
int sourceItemsCount = items.Count;
if (list._capacity < list._count + sourceItemsCount)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + sourceItemsCount))));
}
Span<T> memorySpan = new(list._memoryBlock, list._count, sourceItemsCount);
for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++)
{
memorySpan[sourceItemIndex] = items[sourceItemIndex];
}
list._count += sourceItemsCount;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int growByCount = RecyclableDefaults.MinPooledArrayLength)
{
if (items is RecyclableList<T> sourceRecyclableList)
{
InsertRange(list, index, sourceRecyclableList);
}
else if (items is RecyclableLongList<T> sourceRecyclableLongList)
{
InsertRange(list, index, sourceRecyclableLongList);
}
else if (items is T[] sourceArray)
{
InsertRange(list, index, sourceArray);
}
else if (items is Array sourceArrayWithObjects)
{
InsertRange(list, index, sourceArrayWithObjects);
}
else if (items is List<T> sourceList)
{
InsertRange(list, index, sourceList);
}
else if (items is ICollection<T> sourceICollection)
{
InsertRange(list, index, sourceICollection);
}
else if (items is ICollection sourceICollectionWithObjects)
{
InsertRange(list, index, sourceICollectionWithObjects);
}
else if (items is IReadOnlyList<T> sourceIReadOnlyList)
{
InsertRange(list, index, sourceIReadOnlyList);
}
else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0)
{
InsertRangeWithKnownCount(list, index, items, list._count, requiredAdditionalCapacity);
}
else
{
InsertRangeEnumerated(list, index, items, growByCount);
}
}
}
}
@@ -0,0 +1,230 @@
using System.Collections.ObjectModel;
using System.Numerics;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
public static class zRecyclableListCompatibilityList
{
public static ReadOnlyCollection<T> AsReadOnly<T>(this RecyclableList<T> list) => new(list);
public static int BinarySearch<T>(this RecyclableList<T> list, T item)
=> Array.BinarySearch(list._memoryBlock, 0, list._count, item);
public static int BinarySearch<T>(this RecyclableList<T> list, T item, IComparer<T>? comparer)
=> Array.BinarySearch(list._memoryBlock, 0, list._count, item, comparer);
public static int BinarySearch<T>(this RecyclableList<T> list, int index, int count, T item, IComparer<T>? comparer)
=> Array.BinarySearch(list._memoryBlock, index, count, item, comparer);
public static RecyclableList<TOutput> ConvertAll<T, TOutput>(this RecyclableList<T> list, Converter<T, TOutput> converter)
{
int itemsCount = list._count;
if (itemsCount == 0)
{
return new();
}
RecyclableList<TOutput> result = new(itemsCount);
TOutput[] resultSpan = result._memoryBlock;
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (int bufferIndex = 0; bufferIndex < itemsCount; bufferIndex++)
{
resultSpan[bufferIndex] = converter(sourceSpan[bufferIndex]);
}
result._count = itemsCount;
return result;
}
public static void CopyTo<T>(this RecyclableList<T> list, T[] array) => Array.Copy(list._memoryBlock, array, list._count);
public static void CopyTo<T>(this RecyclableList<T> list, int index, T[] array, int arrayIndex, int count) => Array.Copy(list._memoryBlock, index, array, arrayIndex, count);
public static void ForEach<T>(this RecyclableList<T> list, Action<T> action)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
action(sourceSpan[itemIndex]);
}
}
public static RecyclableList<T> GetRange<T>(this RecyclableList<T> list, int index, int count)
{
int sourceItemsCount = index + count <= list._count ? count : list._count - index;
if (sourceItemsCount <= 0)
{
return new();
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
RecyclableList<T> result = new(sourceItemsCount);
Span<T> targetSpan = result._memoryBlock;
int resultItemsCount = 0;
sourceItemsCount += index;
for (var itemIndex = index; itemIndex < sourceItemsCount; itemIndex++)
{
targetSpan[resultItemsCount++] = sourceSpan[itemIndex];
}
result._count = resultItemsCount;
return result;
}
public static int RemoveAll<T>(this RecyclableList<T> list, Predicate<T> match)
{
using var allIndexes = list.FindAllIndexes(match);
int foundItemsCount = allIndexes._count;
if (foundItemsCount == 0)
{
return foundItemsCount;
}
for (int removed = 0; removed < foundItemsCount; removed++)
{
var indexToRemove = allIndexes[removed] - removed;
Array.Copy(list._memoryBlock, indexToRemove + 1, list._memoryBlock, indexToRemove, list._count - indexToRemove - removed - 1);
}
if (RecyclableList<T>._needsClearing)
{
Array.Clear(list._memoryBlock, list._count - foundItemsCount, foundItemsCount);
}
list._count -= foundItemsCount;
#if WITH_VERSIONING
list._version++;
#endif
return foundItemsCount;
}
public static void RemoveRange<T>(this RecyclableList<T> list, int index, int count)
{
int oldCount = list._count;
count = count - index + 1 <= oldCount ? count : oldCount - index + 1;
if (count == 0)
{
return;
}
if (index + count < oldCount)
{
Array.Copy(list._memoryBlock, index + count, list._memoryBlock, index, oldCount - count - index);
}
if (RecyclableList<T>._needsClearing)
{
Array.Clear(list._memoryBlock, oldCount - count, count);
}
list._count -= count;
#if WITH_VERSIONING
list._version++;
#endif
}
public static void Reverse<T>(this RecyclableList<T> list)
{
Array.Reverse(list._memoryBlock, 0, list._count);
#if WITH_VERSIONING
list._version++;
#endif
}
public static void Reverse<T>(this RecyclableList<T> list, int index, int count)
{
Array.Reverse(list._memoryBlock, index, count);
#if WITH_VERSIONING
list._version++;
#endif
}
public static void Sort<T>(this RecyclableList<T> list)
{
Array.Sort(list._memoryBlock, 0, list._count);
#if WITH_VERSIONING
list._version++;
#endif
}
public static void Sort<T>(this RecyclableList<T> list, Comparison<T> comparison)
{
Array.Sort(list._memoryBlock, 0, list._count, new ComparisonToComparerAdapter<T>(comparison));
#if WITH_VERSIONING
list._version++;
#endif
}
public static void Sort<T>(this RecyclableList<T> list, IComparer<T>? comparer)
{
Array.Sort(list._memoryBlock, 0, list._count, comparer);
#if WITH_VERSIONING
list._version++;
#endif
}
public static void Sort<T>(this RecyclableList<T> list, int index, int count, IComparer<T>? comparer)
{
Array.Sort(list._memoryBlock, index, count, comparer);
#if WITH_VERSIONING
list._version++;
#endif
}
public static T[] ToArray<T>(this RecyclableList<T> list)
{
T[] result = new T[list._count];
new ReadOnlySpan<T>(list._memoryBlock, 0, list._count).CopyTo(result);
return result;
}
public static void TrimExcess<T>(this RecyclableList<T> list)
{
var requiredCapacity = list._count >= RecyclableDefaults.InitialCapacity ? list._count : RecyclableDefaults.InitialCapacity;
T[] oldMemoryBlock = list._memoryBlock;
T[] result = requiredCapacity >= RecyclableDefaults.MinPooledArrayLength && BitOperations.IsPow2(requiredCapacity)
? RecyclableArrayPool<T>.RentShared(requiredCapacity)
: new T[requiredCapacity];
new ReadOnlySpan<T>(oldMemoryBlock, 0, list._count).CopyTo(result);
if (oldMemoryBlock.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, RecyclableList<T>._needsClearing);
}
list._memoryBlock = result;
list._capacity = requiredCapacity;
}
public static bool TrueForAll<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return false;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (!match(sourceSpan[itemIndex]))
{
return false;
}
}
return true;
}
}
}
@@ -7,6 +7,8 @@ namespace Recyclable.Collections
public partial class RecyclableList<T>
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: confirm if enumerator should implement a versioned interface for generated types
public struct Enumerator : IEnumerator<T>
{
private static readonly bool _isReferenceType = !typeof(T).IsValueType;
@@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
namespace Recyclable.Collections
{
[VersionedCollectionsGenerator.GenerateVersioned]
public static class RecyclableListExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -5,7 +5,7 @@ namespace Recyclable.Collections
{
internal static class RecyclableListHelpers<T>
{
private static readonly bool _needsClearing = !typeof(T).IsValueType;
private static readonly bool NeedsClearing = !typeof(T).IsValueType;
public static int CalculateIndex(int index, int lowerBound) => index >= 0 ? index + lowerBound : RecyclableDefaults.ItemNotFoundIndex;
@@ -30,7 +30,7 @@ namespace Recyclable.Collections
// Array.Clear(source);
//}
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, _needsClearing);
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, NeedsClearing);
}
}
@@ -54,7 +54,7 @@ namespace Recyclable.Collections
//}
// If anything, it has been already cleared above, so we don't need to repeat it.
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, _needsClearing);
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, NeedsClearing);
}
return newSize;
@@ -80,7 +80,7 @@ namespace Recyclable.Collections
//}
// If anything, it has been already cleared above, so we don't need to repeat it.
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, _needsClearing);
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, NeedsClearing);
}
return newSize;
@@ -6,9 +6,12 @@ namespace Recyclable.Collections
{
public partial class RecyclableList<T> : IRecyclableVersionedList<T>
{
[VersionedCollectionsGenerator.CloneForVersioned]
VersionedEnumerator IRecyclableVersionedList<T>.GetEnumerator() => new(this);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: confirm if enumerator should implement a versioned interface for generated types
public struct VersionedEnumerator : IEnumerator<T>
{
private int _currentItemIndex;
+58 -10
View File
@@ -6,6 +6,7 @@ using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
[Serializable]
[VersionedCollectionsGenerator.GenerateVersioned]
public sealed partial class RecyclableList<T> : IList<T>, IReadOnlyList<T>, IDisposable
{
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
@@ -17,6 +18,7 @@ namespace Recyclable.Collections
#nullable restore
internal ulong _version;
[VersionedCollectionsGenerator.CloneForVersioned]
public ulong Version => _version;
public RecyclableList()
@@ -41,6 +43,22 @@ namespace Recyclable.Collections
}
}
public RecyclableList(int initialCapacity, int lowerBound)
{
if (initialCapacity >= RecyclableDefaults.InitialCapacity)
{
_memoryBlock = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity)))
: new T[initialCapacity];
_capacity = _memoryBlock.Length;
}
else
{
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
_capacity = RecyclableDefaults.InitialCapacity;
}
}
public RecyclableList(in Array source)
{
_capacity = RecyclableDefaults.InitialCapacity;
@@ -156,7 +174,9 @@ namespace Recyclable.Collections
set
{
_memoryBlock[index] = value;
#if WITH_VERSIONING
_version++;
#endif
}
}
@@ -169,7 +189,9 @@ namespace Recyclable.Collections
if (_capacity != value)
{
_capacity = RecyclableListHelpers<T>.EnsureCapacity(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)value)));
#if WITH_VERSIONING
_version++;
#endif
}
}
}
@@ -188,11 +210,29 @@ namespace Recyclable.Collections
}
_count = value;
#if WITH_VERSIONING
_version++;
#endif
}
}
}
/// <summary>
/// Ensures that the capacity of this list is at least the specified value.
/// </summary>
/// <param name="capacity">The minimum capacity to ensure.</param>
/// <returns>The new capacity of the list.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int EnsureCapacity(int capacity)
{
if (capacity > _capacity)
{
_capacity = RecyclableListHelpers<T>.EnsureCapacity(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)capacity)));
}
return _capacity;
}
public bool IsReadOnly => false;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -211,7 +251,9 @@ namespace Recyclable.Collections
}
_memoryBlock[_count++] = item;
#if WITH_VERSIONING
_version++;
#endif
}
public T[] AsArray => _memoryBlock;
@@ -230,7 +272,9 @@ namespace Recyclable.Collections
}
_count = 0;
#if WITH_VERSIONING
_version++;
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -244,16 +288,6 @@ namespace Recyclable.Collections
? Array.IndexOf(_memoryBlock, itemToFind, 0, _count)
: RecyclableDefaults.ItemNotFoundIndex;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(T item, int index) => _count != 0
? Array.IndexOf(_memoryBlock, item, index, _count - index)
: RecyclableDefaults.ItemNotFoundIndex;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(T item, int index, int count) => _count != 0
? Array.IndexOf(_memoryBlock, item, index, count)
: RecyclableDefaults.ItemNotFoundIndex;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Insert(int index, T item)
{
@@ -273,7 +307,9 @@ namespace Recyclable.Collections
_memoryBlock[index] = item;
_count++;
#if WITH_VERSIONING
_version++;
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -293,7 +329,9 @@ namespace Recyclable.Collections
_memoryBlock[_count] = default;
}
#if WITH_VERSIONING
_version++;
#endif
return true;
}
@@ -320,21 +358,31 @@ namespace Recyclable.Collections
_memoryBlock[_count] = default;
}
#if WITH_VERSIONING
_version++;
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: review if returned enumerator type should change for versioned list
public Enumerator GetEnumerator() => new(this);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: review if returned enumerator type should change for versioned list
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: review if returned enumerator type should change for versioned list
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
public void Dispose()
{
#if WITH_VERSIONING
_version++;
#endif
if (_count != 0)
{
if (_needsClearing)
@@ -1,13 +1,81 @@
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Linq.Expressions;
using System.Reflection;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
#pragma warning disable IDE1006 // This is intentional naming for the class to show up at the end of IntelliSense list.
public static class zRecyclableLongListAddRange
#pragma warning restore IDE1006
{
private static class AddRangeHelper<T>
{
private static readonly Action<RecyclableLongList<T>, object>? _dictionaryAdder;
private static readonly Action<RecyclableLongList<T>, object>? _sortedListAdder;
private static readonly Type? _dictionaryType;
private static readonly Type? _sortedListType;
static AddRangeHelper()
{
Type elementType = typeof(T);
if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
{
Type[] args = elementType.GetGenericArguments();
_dictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args);
MethodInfo? method = typeof(zRecyclableLongListAddRange).GetMethod(
nameof(AddRange),
BindingFlags.NonPublic | BindingFlags.Static,
binder: null,
types: new[] { typeof(RecyclableLongList<>).MakeGenericType(elementType), _dictionaryType },
modifiers: null);
if (method != null)
{
var listParam = Expression.Parameter(typeof(RecyclableLongList<T>));
var objParam = Expression.Parameter(typeof(object));
var call = Expression.Call(method, listParam, Expression.Convert(objParam, _dictionaryType));
_dictionaryAdder = Expression.Lambda<Action<RecyclableLongList<T>, object>>(call, listParam, objParam).Compile();
}
}
else if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(ValueTuple<,>))
{
Type[] args = elementType.GetGenericArguments();
_sortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args);
MethodInfo? method = typeof(zRecyclableLongListAddRange).GetMethod(
nameof(AddRange),
BindingFlags.NonPublic | BindingFlags.Static,
binder: null,
types: new[] { typeof(RecyclableLongList<>).MakeGenericType(elementType), _sortedListType },
modifiers: null);
if (method != null)
{
var listParam = Expression.Parameter(typeof(RecyclableLongList<T>));
var objParam = Expression.Parameter(typeof(object));
var call = Expression.Call(method, listParam, Expression.Convert(objParam, _sortedListType));
_sortedListAdder = Expression.Lambda<Action<RecyclableLongList<T>, object>>(call, listParam, objParam).Compile();
}
}
}
internal static bool TryAddRange(RecyclableLongList<T> list, IEnumerable<T> items)
{
if (_dictionaryAdder != null && _dictionaryType!.IsInstanceOfType(items))
{
_dictionaryAdder(list, items);
return true;
}
if (_sortedListAdder != null && _sortedListType!.IsInstanceOfType(items))
{
_sortedListAdder(list, items);
return true;
}
return false;
}
}
private static void AddRangeEnumerated<T>(this RecyclableLongList<T> targetList, IEnumerable<T> items)
{
using IEnumerator<T> enumerator = items.GetEnumerator();
@@ -32,7 +100,7 @@ namespace Recyclable.Collections
{
if (copied + blockSize > capacity)
{
capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
memoryBlocksSpan = targetList._memoryBlocks;
available = capacity - copied;
}
@@ -45,7 +113,7 @@ namespace Recyclable.Collections
if (!enumerator.MoveNext())
{
targetList._lastBlockWithData = targetBlockIdx;
targetList._longCount = copied + i;
targetList._longCount += copied + i - targetList._longCount;
if (targetItemIdx < blockSize)
{
targetList._nextItemBlockIndex = targetBlockIdx;
@@ -58,7 +126,9 @@ namespace Recyclable.Collections
}
targetList._capacity = capacity;
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
return;
}
@@ -93,7 +163,7 @@ namespace Recyclable.Collections
if (targetList._capacity < requiredCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
}
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
@@ -118,9 +188,12 @@ namespace Recyclable.Collections
targetList._nextItemBlockIndex = targetBlockIdx;
targetList._nextItemIndex = targetItemIdx;
targetList._lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1);
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
[VersionedCollectionsGenerator.RewriteForVersioned]
public static void AddRange<T>(this RecyclableLongList<T> targetList, in Array items)
{
if (items.LongLength == 0)
@@ -137,7 +210,7 @@ namespace Recyclable.Collections
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
@@ -172,7 +245,9 @@ namespace Recyclable.Collections
}
targetList._longCount = targetCapacity;
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, ICollection items)
@@ -186,7 +261,7 @@ namespace Recyclable.Collections
long targetCapacity = targetList._longCount + items.Count;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
// We're better off to temporarily copy it to a fixed array,
@@ -237,7 +312,9 @@ namespace Recyclable.Collections
RecyclableArrayPool<T>.ReturnShared(itemsBuffer, RecyclableLongList<T>._needsClearing);
}
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, ICollection<T> items)
@@ -252,7 +329,7 @@ namespace Recyclable.Collections
long targetCapacity = targetList._longCount + items.Count;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
// We're better off to temporarily copy it to a fixed array,
@@ -305,7 +382,9 @@ namespace Recyclable.Collections
RecyclableArrayPool<T>.ReturnShared(itemsBuffer, RecyclableLongList<T>._needsClearing);
}
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, IEnumerable source)
@@ -332,7 +411,7 @@ namespace Recyclable.Collections
{
if (copied + blockSize > capacity)
{
capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
memoryBlocksSpan = targetList._memoryBlocks;
available = capacity - copied;
}
@@ -345,7 +424,7 @@ namespace Recyclable.Collections
if (!enumerator.MoveNext())
{
targetList._lastBlockWithData = targetBlockIdx;
targetList._longCount = copied + i;
targetList._longCount += copied + i - targetList._longCount;
if (targetItemIdx < blockSize)
{
targetList._nextItemBlockIndex = targetBlockIdx;
@@ -415,7 +494,36 @@ namespace Recyclable.Collections
targetList.AddRange(sourceIReadOnlyList);
return;
case RecyclableHashSet<T> sourceHashSet:
targetList.AddRange(sourceHashSet);
return;
case RecyclableStack<T> sourceStack:
targetList.AddRange(sourceStack);
return;
case RecyclableSortedSet<T> sourceSortedSet:
targetList.AddRange(sourceSortedSet);
return;
case RecyclableLinkedList<T> sourceLinkedList:
targetList.AddRange(sourceLinkedList);
return;
case RecyclablePriorityQueue<T> sourcePriorityQueue:
targetList.AddRange(sourcePriorityQueue);
return;
case RecyclableQueue<T> sourceQueue:
targetList.AddRange(sourceQueue);
return;
default:
if (AddRangeHelper<T>.TryAddRange(targetList, items))
{
return;
}
if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
{
targetList.AddRangeWithKnownCount(items, requiredAdditionalCapacity);
@@ -445,7 +553,7 @@ namespace Recyclable.Collections
if (targetList._capacity < requiredCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
}
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
@@ -488,7 +596,7 @@ namespace Recyclable.Collections
long targetCapacity = targetList._longCount + items.Count;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
@@ -519,7 +627,9 @@ namespace Recyclable.Collections
targetList._nextItemBlockIndex = targetBlockIndex;
targetList._longCount = targetCapacity;
targetList._lastBlockWithData = targetBlockIndex - (targetList._nextItemIndex > 0 ? 0 : 1);
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, ReadOnlySpan<T> items)
@@ -534,7 +644,7 @@ namespace Recyclable.Collections
long targetCapacity = targetList._longCount + items.Length;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
@@ -574,7 +684,9 @@ namespace Recyclable.Collections
targetList._longCount = targetCapacity;
targetList._lastBlockWithData = targetBlockIndex;
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableList<T> items)
@@ -589,7 +701,7 @@ namespace Recyclable.Collections
long targetCapacity = targetList._longCount + items._count;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
ReadOnlySpan<T> itemsSpan = new(items._memoryBlock, 0, items._count);
@@ -631,7 +743,9 @@ namespace Recyclable.Collections
}
targetList._longCount = targetCapacity;
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableLongList<T> items)
@@ -651,7 +765,7 @@ namespace Recyclable.Collections
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
ReadOnlySpan<T[]> sourceMemoryBlocksSpan = items._memoryBlocks;
@@ -704,7 +818,9 @@ namespace Recyclable.Collections
}
targetList._longCount = targetCapacity;
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, Span<T> items)
@@ -719,7 +835,7 @@ namespace Recyclable.Collections
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
@@ -759,7 +875,9 @@ namespace Recyclable.Collections
targetList._longCount = targetCapacity;
targetList._lastBlockWithData = targetBlockIndex;
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, in T[] items)
@@ -774,7 +892,7 @@ namespace Recyclable.Collections
long targetCapacity = targetList._longCount + items.LongLength;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
ReadOnlySpan<T> itemsSpan = items;
@@ -815,7 +933,361 @@ namespace Recyclable.Collections
targetList._longCount = targetCapacity;
targetList._lastBlockWithData = targetBlockIndex;
targetList._version++;
#if WITH_VERSIONING
targetList._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableHashSet<T> items)
{
if (items._count == 0)
{
return;
}
long sourceCount = items._count,
targetCapacity = targetList._longCount + sourceCount;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
var blocks = targetList._memoryBlocks;
int blockIndex = targetList._nextItemBlockIndex,
itemIndex = targetList._nextItemIndex,
blockSize = targetList._blockSize;
var entries = items._entries;
int shift = items._blockShift,
mask = items._blockSizeMinus1;
for (long i = 0; i < sourceCount; i++)
{
blocks[blockIndex][itemIndex++] = entries[i >> shift][(int)(i & mask)].Value;
if (itemIndex == blockSize)
{
itemIndex = 0;
blockIndex++;
}
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = blockIndex;
targetList._nextItemIndex = itemIndex;
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
#if WITH_VERSIONING
targetList._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableStack<T> items)
{
if (items._count == 0)
{
return;
}
long sourceCount = items._count,
targetCapacity = targetList._longCount + sourceCount;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
var blocks = targetList._memoryBlocks;
int blockIndex = targetList._nextItemBlockIndex,
itemIndex = targetList._nextItemIndex,
blockSize = targetList._blockSize;
var chunk = items._current;
while (chunk != null)
{
var array = chunk.Value;
for (int i = chunk.Index - 1; i >= 0; i--)
{
blocks[blockIndex][itemIndex++] = array[i];
if (itemIndex == blockSize)
{
itemIndex = 0;
blockIndex++;
}
}
chunk = chunk.Previous;
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = blockIndex;
targetList._nextItemIndex = itemIndex;
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
#if WITH_VERSIONING
targetList._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableSortedSet<T> items)
{
if (items._count == 0)
{
return;
}
long sourceCount = items._count,
targetCapacity = targetList._longCount + sourceCount;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
var blocks = targetList._memoryBlocks;
int blockIndex = targetList._nextItemBlockIndex,
itemIndex = targetList._nextItemIndex,
blockSize = targetList._blockSize;
int idx = 0;
while (idx < items._count)
{
blocks[blockIndex][itemIndex++] = items._items[idx++];
if (itemIndex == blockSize)
{
itemIndex = 0;
blockIndex++;
}
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = blockIndex;
targetList._nextItemIndex = itemIndex;
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
#if WITH_VERSIONING
targetList._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableLinkedList<T> items)
{
if (items._count == 0)
{
return;
}
long sourceCount = items._count,
targetCapacity = targetList._longCount + sourceCount;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
var blocks = targetList._memoryBlocks;
int blockIndex = targetList._nextItemBlockIndex,
itemIndex = targetList._nextItemIndex,
blockSize = targetList._blockSize;
var chunk = items._head;
while (chunk != null)
{
for (int i = chunk.Bottom; i < chunk.Top; i++)
{
blocks[blockIndex][itemIndex++] = chunk.Value[i];
if (itemIndex == blockSize)
{
itemIndex = 0;
blockIndex++;
}
}
chunk = chunk.Next;
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = blockIndex;
targetList._nextItemIndex = itemIndex;
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
#if WITH_VERSIONING
targetList._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclablePriorityQueue<T> items)
{
if (items._size == 0)
{
return;
}
long sourceCount = items._size,
targetCapacity = targetList._longCount + sourceCount;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
var blocks = targetList._memoryBlocks;
int blockIndex = targetList._nextItemBlockIndex,
itemIndex = targetList._nextItemIndex,
blockSize = targetList._blockSize;
int idx = 0;
while (idx < items._size)
{
blocks[blockIndex][itemIndex++] = items._heap[idx++];
if (itemIndex == blockSize)
{
itemIndex = 0;
blockIndex++;
}
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = blockIndex;
targetList._nextItemIndex = itemIndex;
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
#if WITH_VERSIONING
targetList._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableQueue<T> items)
{
if (items._count == 0)
{
return;
}
long sourceCount = items._count,
targetCapacity = targetList._longCount + sourceCount;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
var blocks = targetList._memoryBlocks;
int blockIndex = targetList._nextItemBlockIndex,
itemIndex = targetList._nextItemIndex,
blockSize = targetList._blockSize;
var chunkQ = items._head;
while (chunkQ != null)
{
for (int i = chunkQ.Bottom; i < chunkQ.Top; i++)
{
blocks[blockIndex][itemIndex++] = chunkQ.Value[i];
if (itemIndex == blockSize)
{
itemIndex = 0;
blockIndex++;
}
}
chunkQ = chunkQ.Next;
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = blockIndex;
targetList._nextItemIndex = itemIndex;
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
#if WITH_VERSIONING
targetList._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<TKey, TValue>(this RecyclableLongList<KeyValuePair<TKey, TValue>> targetList, RecyclableDictionary<TKey, TValue> items)
{
if (items._count == 0)
{
return;
}
long sourceCount = items._count,
targetCapacity = targetList._longCount + sourceCount;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<KeyValuePair<TKey, TValue>>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
var blocks = targetList._memoryBlocks;
int blockIndex = targetList._nextItemBlockIndex,
itemIndex = targetList._nextItemIndex,
blockSize = targetList._blockSize;
var entries = items._entries;
int shift = items._blockShift,
mask = items._blockSizeMinus1;
for (long i = 0; i < sourceCount; i++)
{
ref var entry = ref entries[i >> shift][(int)(i & mask)];
blocks[blockIndex][itemIndex++] = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
if (itemIndex == blockSize)
{
itemIndex = 0;
blockIndex++;
}
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = blockIndex;
targetList._nextItemIndex = itemIndex;
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
#if WITH_VERSIONING
targetList._version++;
#endif
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<TKey, TValue>(this RecyclableLongList<(TKey Key, TValue Value)> targetList, RecyclableSortedList<TKey, TValue> items)
{
if (items._count == 0)
{
return;
}
long sourceCount = items._count,
targetCapacity = targetList._longCount + sourceCount;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<(TKey Key, TValue Value)>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
var blocks = targetList._memoryBlocks;
int blockIndex = targetList._nextItemBlockIndex,
itemIndex = targetList._nextItemIndex,
blockSize = targetList._blockSize;
int idx2 = 0;
while (idx2 < items._count)
{
blocks[blockIndex][itemIndex++] = (items._keys[idx2], items._values[idx2]);
idx2++;
if (itemIndex == blockSize)
{
itemIndex = 0;
blockIndex++;
}
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = blockIndex;
targetList._nextItemIndex = itemIndex;
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
#if WITH_VERSIONING
targetList._version++;
#endif
}
}
}
@@ -0,0 +1,47 @@
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using Recyclable.Collections.Linq;
namespace Recyclable.Collections
{
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
public static class zRecyclableLongListCompatibilityListSorted
{
public static void Sort<T>(this RecyclableLongList<T> list)
{
QuickSortExtensions<T>.QuickSort(list);
#if WITH_VERSIONING
list._version++;
#endif
}
public static void Sort<T>(this RecyclableLongList<T> list, Comparison<T> comparison)
{
QuickSortExtensions<T>.QuickSort(list, new ComparisonToComparerAdapter<T>(comparison), new SystemRandomNumberGenerator());
#if WITH_VERSIONING
list._version++;
#endif
}
public static void Sort<T>(this RecyclableLongList<T> list, IComparer<T>? comparer)
{
QuickSortExtensions<T>.QuickSort(list, comparer ?? Comparer<T>.Default, new SystemRandomNumberGenerator());
#if WITH_VERSIONING
list._version++;
#endif
}
public static void Sort<T>(this RecyclableLongList<T> list, long index, long count, IComparer<T>? comparer)
{
if (count == 0)
{
return;
}
QuickSortExtensions<T>.QuickSort(list, (int)index, (int)(index + count - 1), comparer ?? Comparer<T>.Default, new SystemRandomNumberGenerator());
#if WITH_VERSIONING
list._version++;
#endif
}
}
}
@@ -5,6 +5,8 @@ namespace Recyclable.Collections
{
public partial class RecyclableLongList<T>
{
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: confirm if enumerator should implement a versioned interface for generated types
public struct Enumerator : IEnumerator<T>
{
#nullable disable
@@ -16,9 +18,7 @@ namespace Recyclable.Collections
private readonly RecyclableLongList<T> _list;
#nullable disable
#pragma warning disable IDE0032 // This is intentional due to better performance
private T _current;
#pragma warning restore IDE0032
#nullable restore
[Pure]
@@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
namespace Recyclable.Collections
{
[VersionedCollectionsGenerator.GenerateVersioned]
public static class RecyclableLongListExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -142,9 +142,9 @@ namespace Recyclable.Collections
/// </remarks>
/// <returns>The maximum no. of items <paramref name="list"/> can store.</returns>
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static long Resize(ref T[][] memoryBlocks, int minBlockSize, byte minBlockSizePow2Shift, long newCapacity)
public static long Resize(RecyclableLongList<T> list, int minBlockSize, byte minBlockSizePow2Shift, long newCapacity)
{
int sourceBlockCount = memoryBlocks?.Length ?? 0;
int sourceBlockCount = list._memoryBlocks?.Length ?? 0;
int requiredBlockCount = checked((int)(newCapacity >> minBlockSizePow2Shift) + ((newCapacity & (minBlockSize - 1)) != 0 ? 1 : 0));
int blockIndex;
@@ -157,19 +157,19 @@ namespace Recyclable.Collections
// Copy arrays from the old memory block for all arrays
if (sourceBlockCount > 0)
{
Array.Copy(memoryBlocks!, newMemoryBlocks, sourceBlockCount);
Array.Copy(list._memoryBlocks!, newMemoryBlocks, sourceBlockCount);
// We can now return the old memory block for all arrays itself
if (sourceBlockCount >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T[]>.ReturnShared(memoryBlocks!, true);
RecyclableArrayPool<T[]>.ReturnShared(list._memoryBlocks!, true);
}
}
memoryBlocks = newMemoryBlocks;
list._memoryBlocks = newMemoryBlocks;
}
else
{
newMemoryBlocks = memoryBlocks!;
newMemoryBlocks = list._memoryBlocks!;
}
// Allocate arrays for any new blocks
@@ -284,184 +284,7 @@ namespace Recyclable.Collections
try
{
context.Exception.CaptureAndReThrow();
}
finally
{
context.Dispose();
}
return RecyclableDefaults.ItemNotFoundIndexLong;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static long IndexOfParallel(RecyclableLongList<T> list, T item, int firstBlockIndex)
{
IndexOfSynchronizationContext context = IndexOfSynchronizationContextPool.GetWithOneParticipant();
int blockIndex = firstBlockIndex,
lastBlockWithData = list._lastBlockWithData,
lastFullBlockWithData = lastBlockWithData - BlocksForEachTask;
while (blockIndex < lastFullBlockWithData)
{
// At this point itemIndex is limited to blockSize range - int.
// & WAS SLOWER
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
// Task.Factory.StartNew(() =>
context.AddParticipant();
var startingBlock = blockIndex;
_ = Task.Run(() => IndexOfInConsecutiveBlocks(context, list, startingBlock, item));
// & WAS SLOWER
// ScheduleIndexOfTask(context, list, blockIndex, item);
if (context._isItemFound)
{
break;
}
blockIndex += BlocksForEachTask;
}
if (!context._isItemFound)
{
while (blockIndex < lastBlockWithData)
{
// & WAS SLOWER
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
// Task.Factory.StartNew(() =>
var startingBlock = blockIndex;
context.AddParticipant();
_ = Task.Run(() => IndexOfIn1Block(context, list, startingBlock, item));
// & WAS SLOWER
// ScheduleIndexOfTaskSingle(context, list, blockIndex, item);
if (context._isItemFound)
{
break;
}
blockIndex++;
}
}
if (!context._isItemFound)
{
int itemIndex = list._nextItemIndex != 0
? Array.IndexOf(list._memoryBlocks[lastBlockWithData], item, 0, list._nextItemIndex)
: Array.IndexOf(list._memoryBlocks[lastBlockWithData], item, 0, list._blockSize);
if (!context._isItemFound && itemIndex >= 0)
{
ExchangeFoundItemIndexIfLower(context, lastBlockWithData, ((long)lastBlockWithData << list._blockSizePow2BitShift) + itemIndex);
}
}
context.SignalAndWait();
if (context.Exception == null)
{
try
{
return context.FoundItemIndex;
}
finally
{
context.Dispose();
}
}
try
{
context.Exception.CaptureAndReThrow();
}
finally
{
context.Dispose();
}
return RecyclableDefaults.ItemNotFoundIndexLong;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static long IndexOfParallel(RecyclableLongList<T> list, T item, int firstBlockIndex, long count)
{
IndexOfSynchronizationContext context = IndexOfSynchronizationContextPool.GetWithOneParticipant();
int blockIndex = firstBlockIndex,
lastBlockWithData = (int)Math.Min((count >> list._blockSizePow2BitShift) + firstBlockIndex + ((count & list._blockSizeMinus1) > 0 ? 1 : 0) - 1, list._lastBlockWithData),
lastFullBlockWithData = lastBlockWithData - BlocksForEachTask;
while (blockIndex < lastFullBlockWithData)
{
// At this point itemIndex is limited to blockSize range - int.
// & WAS SLOWER
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
// Task.Factory.StartNew(() =>
context.AddParticipant();
var startingBlock = blockIndex;
_ = Task.Run(() => IndexOfInConsecutiveBlocks(context, list, startingBlock, item));
// & WAS SLOWER
// ScheduleIndexOfTask(context, list, blockIndex, item);
if (context._isItemFound)
{
break;
}
blockIndex += BlocksForEachTask;
}
if (!context._isItemFound)
{
count -= (blockIndex - firstBlockIndex) << list._blockSizePow2BitShift;
while (blockIndex < lastBlockWithData)
{
// & WAS SLOWER
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
// Task.Factory.StartNew(() =>
var startingBlock = blockIndex;
context.AddParticipant();
_ = Task.Run(() => IndexOfIn1Block(context, list, startingBlock, item));
// & WAS SLOWER
// ScheduleIndexOfTaskSingle(context, list, blockIndex, item);
if (context._isItemFound)
{
break;
}
blockIndex++;
count -= list._blockSize;
}
}
if (!context._isItemFound && count > 0)
{
int itemIndex = Array.IndexOf(list._memoryBlocks[lastBlockWithData], item, 0, (int)count);
if (!context._isItemFound && itemIndex >= 0)
{
ExchangeFoundItemIndexIfLower(context, lastBlockWithData, ((long)lastBlockWithData << list._blockSizePow2BitShift) + itemIndex);
}
}
context.SignalAndWait();
if (context.Exception == null)
{
try
{
return context.FoundItemIndex;
}
finally
{
context.Dispose();
}
}
try
{
context.Exception.CaptureAndReThrow();
context.Exception.CaptureAndRethrow();
}
finally
{
@@ -521,119 +344,6 @@ namespace Recyclable.Collections
return itemIndex >= 0 ? itemIndex + ((long)list._lastBlockWithData << list._blockSizePow2BitShift) : RecyclableDefaults.ItemNotFoundIndexLong;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static long IndexOfSequential(RecyclableLongList<T> list, in T item, int firstBlockIndex, long count)
{
int blockIndex,
blockSize = list._blockSize,
itemIndex,
lastBlockWithData = (int)Math.Min((count >> list._blockSizePow2BitShift) + firstBlockIndex + ((count & list._blockSizeMinus1) > 0 ? 1 : 0) - 1, list._lastBlockWithData);
byte blockSizePow2BitShift = list._blockSizePow2BitShift;
ReadOnlySpan<T[]> memoryBlocksSpan = new(list._memoryBlocks, 0, lastBlockWithData + 1);
for (blockIndex = firstBlockIndex; blockIndex < lastBlockWithData && count > blockSize; blockIndex += 4)
{
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize);
if (itemIndex >= 0)
{
return itemIndex + ((long)blockIndex << blockSizePow2BitShift);
}
count -= blockSize;
if (blockIndex + 1 < lastBlockWithData && count > blockSize)
{
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 1], item, 0, blockSize);
if (itemIndex >= 0)
{
return itemIndex + ((long)(blockIndex + 1) << blockSizePow2BitShift);
}
count -= blockSize;
if (blockIndex + 2 < lastBlockWithData && count > blockSize)
{
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 2], item, 0, blockSize);
if (itemIndex >= 0)
{
return itemIndex + ((long)(blockIndex + 2) << blockSizePow2BitShift);
}
count -= blockSize;
if (blockIndex + 3 < lastBlockWithData && count > blockSize)
{
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 3], item, 0, blockSize);
if (itemIndex >= 0)
{
return itemIndex + ((long)(blockIndex + 3) << blockSizePow2BitShift);
}
count -= blockSize;
}
}
}
}
itemIndex = Array.IndexOf(memoryBlocksSpan[lastBlockWithData], item, 0, (int)count);
return itemIndex >= 0 ? itemIndex + ((long)lastBlockWithData << blockSizePow2BitShift) : RecyclableDefaults.ItemNotFoundIndexLong;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static long IndexOfSequential(RecyclableLongList<T> list, in T item, int firstBlockIndex)
{
int blockIndex,
blockSize = list._blockSize,
itemIndex,
lastBlockWithData = list._lastBlockWithData;
byte blockSizePow2BitShift = list._blockSizePow2BitShift;
ReadOnlySpan<T[]> memoryBlocksSpan = new(list._memoryBlocks, 0, lastBlockWithData + 1);
for (blockIndex = firstBlockIndex; blockIndex < lastBlockWithData; blockIndex += 4)
{
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize);
if (itemIndex >= 0)
{
return itemIndex + ((long)blockIndex << blockSizePow2BitShift);
}
if (blockIndex + 1 < lastBlockWithData)
{
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 1], item, 0, blockSize);
if (itemIndex >= 0)
{
return itemIndex + ((long)(blockIndex + 1) << blockSizePow2BitShift);
}
if (blockIndex + 2 < lastBlockWithData)
{
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 2], item, 0, blockSize);
if (itemIndex >= 0)
{
return itemIndex + ((long)(blockIndex + 2) << blockSizePow2BitShift);
}
if (blockIndex + 3 < lastBlockWithData)
{
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 3], item, 0, blockSize);
if (itemIndex >= 0)
{
return itemIndex + ((long)(blockIndex + 3) << blockSizePow2BitShift);
}
}
}
}
}
itemIndex = list._nextItemIndex != 0
? Array.IndexOf(memoryBlocksSpan[lastBlockWithData], item, 0, list._nextItemIndex)
: Array.IndexOf(memoryBlocksSpan[lastBlockWithData], item, 0, list._blockSize);
return itemIndex >= 0 ? itemIndex + ((long)lastBlockWithData << blockSizePow2BitShift) : RecyclableDefaults.ItemNotFoundIndexLong;
}
}
}
}
File diff suppressed because it is too large. Load diff
@@ -5,9 +5,13 @@ namespace Recyclable.Collections
{
public partial class RecyclableLongList<T> : IRecyclableVersionedLongList<T>
{
[VersionedCollectionsGenerator.CloneForVersioned]
public VersionedEnumerator GetVersionedEnumerator() => new(this);
[VersionedCollectionsGenerator.CloneForVersioned]
VersionedEnumerator IRecyclableVersionedLongList<T>.GetEnumerator() => new(this);
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: confirm if enumerator should implement a versioned interface for generated types
public struct VersionedEnumerator : IEnumerator<T>
{
#nullable disable
+52 -117
View File
@@ -1,5 +1,4 @@
using System.Collections;
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
@@ -10,6 +9,7 @@ using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
[VersionedCollectionsGenerator.GenerateVersioned]
public partial class RecyclableLongList<T> : IList<T>, IReadOnlyList<T>, IDisposable
{
private static readonly bool _defaultIsNull = default(T) == null;
@@ -37,11 +37,15 @@ namespace Recyclable.Collections
private set
{
_capacity = value;
#if WITH_VERSIONING
_version++;
#endif
if (_capacity != value)
{
_capacity = Helpers.Resize(ref _memoryBlocks, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)value)));
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)value)));
#if WITH_VERSIONING
_version++;
#endif
}
}
}
@@ -52,6 +56,7 @@ namespace Recyclable.Collections
public long LongCount => _longCount;
public int NextItemBlockIndex => _nextItemBlockIndex;
public int NextItemIndex => _nextItemIndex;
[VersionedCollectionsGenerator.CloneForVersioned]
public ulong Version => _version;
public RecyclableLongList(int minBlockSize = RecyclableDefaults.BlockSize, long initialCapacity = RecyclableDefaults.InitialCapacity)
@@ -62,7 +67,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
}
@@ -75,7 +80,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -90,7 +95,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -105,7 +110,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -120,7 +125,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -135,7 +140,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -150,7 +155,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -165,7 +170,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -180,7 +185,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -195,7 +200,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -210,7 +215,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -225,7 +230,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -240,7 +245,7 @@ namespace Recyclable.Collections
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
@@ -253,7 +258,9 @@ namespace Recyclable.Collections
set
{
new Span<T>(_memoryBlocks[(int)(index >> _blockSizePow2BitShift)])[(int)(index & _blockSizeMinus1)] = value;
#if WITH_VERSIONING
_version++;
#endif
}
}
@@ -263,7 +270,9 @@ namespace Recyclable.Collections
set
{
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = value;
#if WITH_VERSIONING
_version++;
#endif
}
}
@@ -274,7 +283,7 @@ namespace Recyclable.Collections
{
if (_capacity < _longCount + 1)
{
_capacity = Helpers.Resize(ref _memoryBlocks, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(_longCount + 1))));
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(_longCount + 1))));
}
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex++] = item;
@@ -294,7 +303,9 @@ namespace Recyclable.Collections
}
_longCount++;
#if WITH_VERSIONING
_version++;
#endif
}
public void Clear()
@@ -328,7 +339,9 @@ namespace Recyclable.Collections
_nextItemIndex = 0;
_lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
_longCount = 0;
#if WITH_VERSIONING
_version++;
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -359,8 +372,14 @@ namespace Recyclable.Collections
public void CopyTo(T[] array, int arrayIndex) => Helpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: review if returned enumerator type should change for versioned list
public Enumerator GetEnumerator() => new(this);
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: review if returned enumerator type should change for versioned list
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
[VersionedCollectionsGenerator.CloneForVersioned]
// TODO: review if returned enumerator type should change for versioned list
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -375,7 +394,7 @@ namespace Recyclable.Collections
return RecyclableDefaults.ItemNotFoundIndex;
}
int itemIndex = Array.IndexOf(_memoryBlocks[0], item, 0, _blockSize);
int itemIndex = Array.IndexOf(_memoryBlocks[0], item, 0, _longCount < _blockSize ? _nextItemIndex : _blockSize);
if (itemIndex >= 0)
{
return itemIndex;
@@ -398,104 +417,6 @@ namespace Recyclable.Collections
}
}
public int IndexOf(T item, int index)
{
if (index < 0 || index >= _longCount)
{
ThrowHelper.ThrowArgumentOutOfRangeException_Index();
}
Contract.Requires(index >= 0);
Contract.Requires(index < _longCount);
Contract.EndContractBlock();
if (_lastBlockWithData == 0)
{
return Array.IndexOf(_memoryBlocks[0], item, index, (_longCount < _blockSize ? _nextItemIndex : _blockSize) - index);
}
else if (_longCount == 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
int firstBlockIndex = index >> _blockSizePow2BitShift;
int firstItemIndex = index & _blockSizeMinus1;
int itemIndex = Array.IndexOf(_memoryBlocks[firstBlockIndex], item, firstItemIndex, (firstBlockIndex == _lastBlockWithData && _nextItemIndex > 0 ? _nextItemIndex : _blockSize) - firstItemIndex);
if (itemIndex >= 0)
{
return (firstBlockIndex << _blockSizePow2BitShift) + itemIndex;
}
else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization && firstBlockIndex + 1 <= _lastBlockWithData)
{
return (int)RecyclableLongList<T>.IndexOfHelpers.IndexOfSequential(this, item, firstBlockIndex + 1);
}
else
{
if (firstBlockIndex + 1 <= _lastBlockWithData)
{
itemIndex = Array.IndexOf(_memoryBlocks[firstBlockIndex + 1], item, 0, (int)Math.Min(firstBlockIndex + 1 == _lastBlockWithData ? _nextItemIndex : _blockSize, _longCount - _blockSize));
return itemIndex >= 0 ? ((firstBlockIndex + 1) << _blockSizePow2BitShift) + itemIndex : checked((int)RecyclableLongList<T>.IndexOfHelpers.IndexOfParallel(this, item, firstBlockIndex + 2));
}
else
{
return RecyclableDefaults.ItemNotFoundIndex;
}
}
}
public int IndexOf(T item, int index, int count)
{
if (index < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException_Index();
}
if (count + index > _longCount)
{
ThrowHelper.ThrowArgumentOutOfRangeException_Count();
}
Contract.Requires(index >= 0);
Contract.Requires(count + index <= _longCount);
Contract.EndContractBlock();
if (_lastBlockWithData == 0)
{
return Array.IndexOf(_memoryBlocks[0], item, index, count);
}
else if (_longCount == 0 || count == 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
int blockSize = _blockSize,
firstBlockIndex = index >> _blockSizePow2BitShift,
firstItemIndex = index & _blockSizeMinus1;
int firstBlockItemsCount = Math.Min(blockSize - firstItemIndex, count);
int itemIndex = Array.IndexOf(_memoryBlocks[firstBlockIndex], item, firstItemIndex, firstBlockItemsCount);
if (itemIndex >= 0)
{
return (firstBlockIndex << _blockSizePow2BitShift) + itemIndex;
}
else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization && firstBlockIndex + 1 <= _lastBlockWithData)
{
return (int)RecyclableLongList<T>.IndexOfHelpers.IndexOfSequential(this, item, firstBlockIndex + 1, count - firstBlockItemsCount);
}
else
{
if (firstBlockIndex + 1 <= _lastBlockWithData)
{
itemIndex = Array.IndexOf(_memoryBlocks[firstBlockIndex + 1], item, 0, (int)MathUtils.Min(blockSize, _longCount - blockSize, count - firstBlockItemsCount));
return itemIndex >= 0 ? ((firstBlockIndex + 1) << _blockSizePow2BitShift) + itemIndex : checked((int)RecyclableLongList<T>.IndexOfHelpers.IndexOfParallel(this, item, firstBlockIndex + 2, count - firstBlockItemsCount - blockSize));
}
else
{
return RecyclableDefaults.ItemNotFoundIndex;
}
}
}
public void Insert(int index, T item)
{
if (index > _longCount || index < 0)
@@ -505,7 +426,7 @@ namespace Recyclable.Collections
if (_capacity < ++_longCount)
{
_capacity = Helpers.Resize(ref _memoryBlocks, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)_longCount)));
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)_longCount)));
}
if (index + 1 < _longCount)
@@ -532,7 +453,9 @@ namespace Recyclable.Collections
_lastBlockWithData++;
}
#if WITH_VERSIONING
_version++;
#endif
}
public void Insert(long index, T item)
@@ -544,7 +467,7 @@ namespace Recyclable.Collections
if (_capacity < ++_longCount)
{
_capacity = Helpers.Resize(ref _memoryBlocks, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)_longCount)));
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)_longCount)));
}
if (index + 1 < _longCount)
@@ -571,7 +494,9 @@ namespace Recyclable.Collections
_lastBlockWithData++;
}
#if WITH_VERSIONING
_version++;
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -642,7 +567,9 @@ namespace Recyclable.Collections
#nullable restore
}
#if WITH_VERSIONING
_version++;
#endif
return true;
}
@@ -684,7 +611,9 @@ namespace Recyclable.Collections
#nullable restore
}
#if WITH_VERSIONING
_version++;
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -722,7 +651,9 @@ namespace Recyclable.Collections
#nullable restore
}
#if WITH_VERSIONING
_version++;
#endif
}
public void RemoveBlock(int index)
@@ -744,12 +675,16 @@ namespace Recyclable.Collections
}
_lastBlockWithData--;
#if WITH_VERSIONING
_version++;
#endif
}
public void Dispose()
{
#if WITH_VERSIONING
_version++;
#endif
if (_capacity > 0)
{
Clear();
@@ -0,0 +1,244 @@
using System.Collections;
using System.Runtime.CompilerServices;
using System.Numerics;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal sealed class RecyclablePriorityQueue<T> : IEnumerable<T>, IDisposable
{
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
internal readonly IComparer<T> _comparer;
#nullable disable
internal T[] _heap;
#nullable restore
internal int _size;
internal bool _disposed;
public RecyclablePriorityQueue(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer<T>? comparer = null)
{
if (initialCapacity < 1)
{
initialCapacity = 1;
}
if (!BitOperations.IsPow2((uint)initialCapacity))
{
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
}
_heap = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(initialCapacity)
: new T[initialCapacity];
_size = 0;
_comparer = comparer ?? Comparer<T>.Default;
}
public int Count => _size;
public long LongCount => _size;
public void Enqueue(T item)
{
if (_size == _heap.Length)
{
Grow();
}
int index = _size++;
_heap[index] = item;
MoveUp(index);
}
public T Dequeue()
{
if (_size == 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_size), "Queue is empty");
}
var root = _heap[0];
if (_needsClearing)
{
_heap[0] = default!;
}
_size--;
if (_size > 0)
{
_heap[0] = _heap[_size];
if (_needsClearing)
{
_heap[_size] = default!;
}
MoveDown(0);
}
return root;
}
public bool TryDequeue(out T? item)
{
if (_size > 0)
{
item = Dequeue();
return true;
}
item = default;
return false;
}
public T Peek()
{
if (_size == 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_size), "Queue is empty");
}
return _heap[0];
}
public void Clear()
{
if (_needsClearing)
{
Array.Clear(_heap, 0, _size);
}
_size = 0;
}
public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
private void Grow()
{
int newSize;
if (_heap.Length >= RecyclableDefaults.MaxPooledBlockSize)
{
newSize = RecyclableDefaults.MaxPooledBlockSize;
}
else
{
var doubled = _heap.Length << 1;
newSize = (int)Math.Min(doubled, RecyclableDefaults.MaxPooledBlockSize);
}
var newHeap = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(newSize)
: new T[newSize];
Array.Copy(_heap, newHeap, _size);
if (_heap.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(_heap, _needsClearing);
}
_heap = newHeap;
}
private void MoveUp(int index)
{
var item = _heap[index];
while (index > 0)
{
int parent = (index - 1) >> 1;
if (_comparer.Compare(item, _heap[parent]) >= 0)
{
break;
}
_heap[index] = _heap[parent];
index = parent;
}
_heap[index] = item;
}
private void MoveDown(int index)
{
var item = _heap[index];
int half = _size >> 1;
while (index < half)
{
int child = (index << 1) + 1;
int right = child + 1;
if (right < _size && _comparer.Compare(_heap[right], _heap[child]) < 0)
{
child = right;
}
if (_comparer.Compare(_heap[child], item) >= 0)
{
break;
}
_heap[index] = _heap[child];
index = child;
}
_heap[index] = item;
}
public void Dispose()
{
if (_disposed)
{
return;
}
if (_heap.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(_heap, _needsClearing);
}
_heap = Array.Empty<T>();
_size = 0;
_disposed = true;
GC.SuppressFinalize(this);
}
public struct Enumerator : IEnumerator<T>
{
private readonly RecyclablePriorityQueue<T> _queue;
private int _index;
private T? _current;
internal Enumerator(RecyclablePriorityQueue<T> queue)
{
_queue = queue;
_index = 0;
_current = default;
}
public T Current => _current!;
object IEnumerator.Current => _current!;
public bool MoveNext()
{
if (_index >= _queue._size)
{
return false;
}
_current = _queue._heap[_index++];
return true;
}
public void Reset()
{
_index = 0;
_current = default;
}
public void Dispose()
{
_current = default;
}
}
}
}
+209 -161
View File
@@ -1,43 +1,130 @@
using System.Buffers;
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal class RecyclableQueue<T> : IRecyclableOwner<T>, IList<T>, IDisposable
internal sealed class RecyclableQueue<T> : IEnumerable<T>, IDisposable
{
private static readonly ArrayPool<T> _arrayPool = ArrayPool<T>.Create();
private static readonly IEqualityComparer<T> _equalityComparer = EqualityComparer<T>.Default;
private bool _disposedValue;
private RecyclableList<T[]> Memory { get; }
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static T Dequeue(RecyclableQueue<T> queue)
private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous)
{
if (queue.LongCount <= 0)
var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent();
if (chunk.Value.Length < size)
{
throw new ArgumentOutOfRangeException(nameof(queue), "There are no items in the queue");
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
}
var toRemove = queue[0];
queue.RemovedCount++;
queue.LongCount--;
if (queue.Capacity - queue.LongCount == queue.BlockSize)
{
queue.RemoveBlock(0);
queue.RemovedCount -= queue.BlockSize;
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(size)
: new T[size];
}
return toRemove;
chunk.Top = 0;
chunk.Bottom = 0;
chunk.Previous = previous;
chunk.Next = null;
return chunk;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryDequeue(RecyclableQueue<T> queue, out T? item)
private static void ReturnChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{
if (queue.LongCount > 0)
BiDirectionalRecyclableArrayPoolChunkPool<T>.Return(chunk);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DisposeChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
{
item = queue.Dequeue();
BiDirectionalRecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing);
}
internal BiDirectionalRecyclableArrayPoolChunk<T> _head;
internal BiDirectionalRecyclableArrayPoolChunk<T> _tail;
internal bool _disposed;
internal long _capacity;
internal long _count;
public RecyclableQueue(int initialCapacity = RecyclableDefaults.InitialCapacity)
{
if (initialCapacity < 1)
{
initialCapacity = 1;
}
if (!BitOperations.IsPow2((uint)initialCapacity))
{
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
}
_head = RentChunk(initialCapacity, null);
_tail = _head;
_capacity = initialCapacity;
_count = 0;
}
public RecyclableQueue(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity)
{
foreach (var item in source)
{
Enqueue(item);
}
}
public int Count => checked((int)_count);
public long LongCount => _count;
public void Enqueue(T item)
{
if (_tail.Top == _tail.Value.Length)
{
Grow();
}
_tail.Value[_tail.Top++] = item;
_count++;
}
public void Add(T item) => Enqueue(item);
public T Dequeue()
{
if (_count == 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "Queue is empty");
}
_count--;
var value = _head.Value[_head.Bottom];
if (_needsClearing)
{
_head.Value[_head.Bottom] = default!;
}
_head.Bottom++;
if (_head.Bottom == _head.Top && _head.Next != null)
{
ReleaseHeadChunk();
}
else if (_head.Bottom == _head.Top)
{
_head.Bottom = 0;
_head.Top = 0;
}
return value;
}
public bool TryDequeue(out T? item)
{
if (_count > 0)
{
item = Dequeue();
return true;
}
@@ -45,165 +132,126 @@ namespace Recyclable.Collections
return false;
}
protected static readonly bool NeedsClearing = !typeof(T).IsValueType;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static long GetAbsoluteIndex(long index, long removedCount) => index + removedCount;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected long GetRelativeIndex(long index) => index - RemovedCount;
protected long RemovedCount { get; set; }
public RecyclableQueue(IEnumerable<T> source, int blockSize = RecyclableDefaults.BlockSize)
{
BlockSize = blockSize;
Memory = new(1);
foreach (var item in source)
{
Enqueue(item);
}
}
public RecyclableQueue(int blockSize = RecyclableDefaults.BlockSize)
{
BlockSize = blockSize;
Memory = new(1);
}
public int BlockSize { get; protected set; }
public long Capacity { get; set; }
public int Count
{
get => checked((int)LongCount);
set => LongCount = value;
}
public long LongCount { get; set; }
public bool IsReadOnly { get; }
public T this[int index]
{
get => GetItem(index);
set => SetItem(index, value);
}
public T this[long index]
{
get => GetItem(index);
set => SetItem(index, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetItem(long index, T value)
{
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
int arrayIndex = RecyclableQueueHelpers<T>.ToArrayIndex(absoluteIndex, BlockSize);
long itemIndex = RecyclableQueueHelpers<T>.ToItemIndex(absoluteIndex, BlockSize);
Memory[arrayIndex][itemIndex] = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private T GetItem(long index)
{
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
int arrayIndex = RecyclableQueueHelpers<T>.ToArrayIndex(absoluteIndex, BlockSize);
long itemIndex = RecyclableQueueHelpers<T>.ToItemIndex(absoluteIndex, BlockSize);
return Memory[arrayIndex][itemIndex];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enqueue(T item)
{
if (LongCount == Capacity)
{
AddBlock();
}
SetItem(LongCount, item);
LongCount++;
}
public void AddBlock()
{
T[] newArray = (BlockSize >= RecyclableDefaults.MinPooledArrayLength)
? _arrayPool.Rent(BlockSize)
: new T[BlockSize];
Memory.Add(newArray);
Capacity += BlockSize;
}
public T Dequeue() => Dequeue(this);
public void Add(T item) => Enqueue(item);
public void Clear()
{
try
while (_head.Next != null)
{
// Remove in reversed order for performance savings
while (Memory._count > 0)
{
try
{
RemoveBlock(Memory._count - 1);
}
catch (Exception)
{
}
}
}
finally
{
LongCount = 0;
RemovedCount = 0;
Memory.Clear();
}
ReleaseHeadChunk();
}
#pragma warning disable CS0618
public bool Contains(T item) => RecyclableQueueHelpers<T>.Contains(Memory, item);
public void CopyTo(T[] array, int arrayIndex) => RecyclableQueueHelpers<T>.CopyTo(Memory, RemovedCount, BlockSize, checked((int)(LongCount % BlockSize)), array, arrayIndex);
public IEnumerable<T> Enumerate() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount);
public IEnumerator<T> GetEnumerator() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
public int IndexOf(T item) => checked((int)GetRelativeIndex(RecyclableQueueHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer)));
public long LongIndexOf(T item) => GetRelativeIndex(RecyclableQueueHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
#pragma warning restore CS0618
public void Insert(int index, T item) => throw new NotSupportedException();
public void RemoveAt(int index) => throw new NotSupportedException();
public bool TryDequeue(out T? item) => TryDequeue(this, out item);
public bool Remove(T item) => TryDequeue(out var _);
public void RemoveBlock(int index)
if (_needsClearing && _head.Top > _head.Bottom)
{
try
{
if (Memory[index].LongLength is > RecyclableDefaults.MinPooledArrayLength and <= int.MaxValue)
{
_arrayPool.Return(Memory[index], NeedsClearing);
Array.Clear(_head.Value, _head.Bottom, _head.Top - _head.Bottom);
}
Memory.RemoveAt(index);
_capacity = _head.Value.Length;
_head.Top = 0;
_head.Bottom = 0;
_tail = _head;
_count = 0;
}
finally
public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
private void Grow()
{
Capacity -= BlockSize;
//LongCount -= BlockSize;
int newSize;
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
{
newSize = RecyclableDefaults.MaxPooledBlockSize;
}
else
{
var doubled = _capacity << 1;
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
}
var newChunk = RentChunk(newSize, _tail);
_tail.Next = newChunk;
_tail = newChunk;
_capacity += newSize;
}
private void ReleaseHeadChunk()
{
var toReturn = _head;
_head = toReturn.Next!;
_head.Previous = null;
_capacity -= toReturn.Value.Length;
ReturnChunk(toReturn);
}
public void Dispose()
{
if (!_disposedValue)
if (_disposed)
{
Clear();
_disposedValue = true;
return;
}
var chunk = _head;
while (chunk != null)
{
var next = chunk.Next;
DisposeChunk(chunk);
chunk = next;
}
_disposed = true;
GC.SuppressFinalize(this);
}
public struct Enumerator : IEnumerator<T>
{
private readonly BiDirectionalRecyclableArrayPoolChunk<T>? _start;
private BiDirectionalRecyclableArrayPoolChunk<T>? _chunk;
private int _index;
private T? _current;
internal Enumerator(RecyclableQueue<T> queue)
{
_start = queue._head;
_chunk = queue._head;
_index = _chunk != null ? _chunk.Bottom : 0;
_current = default;
}
public T Current => _current!;
object IEnumerator.Current => _current!;
public bool MoveNext()
{
if (_chunk == null)
{
return false;
}
if (_index >= _chunk.Top)
{
_chunk = _chunk.Next;
if (_chunk == null)
{
return false;
}
_index = _chunk.Bottom;
}
_current = _chunk.Value[_index++];
return true;
}
public void Reset()
{
_chunk = _start;
_index = _chunk != null ? _chunk.Bottom : 0;
}
public void Dispose()
{
_chunk = null;
_current = default;
}
}
}
}
@@ -0,0 +1,287 @@
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal sealed class RecyclableSortedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IDisposable
where TKey : notnull
{
private static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType;
private readonly IComparer<TKey> _comparer;
private TKey[] _keys;
private TValue[] _values;
private int _count;
private bool _disposed;
public RecyclableSortedDictionary(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer<TKey>? comparer = null)
{
if (initialCapacity < 1)
{
initialCapacity = 1;
}
if (!BitOperations.IsPow2((uint)initialCapacity))
{
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
}
_keys = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<TKey>.RentShared(initialCapacity)
: new TKey[initialCapacity];
_values = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<TValue>.RentShared(initialCapacity)
: new TValue[initialCapacity];
_count = 0;
_comparer = comparer ?? Comparer<TKey>.Default;
}
public int Count => _count;
public TValue this[TKey key]
{
get
{
int index = BinarySearch(key);
if (index < _count && _comparer.Compare(_keys[index], key) == 0)
{
return _values[index];
}
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "Key not found");
return default!;
}
set
{
int index = BinarySearch(key);
if (index < _count && _comparer.Compare(_keys[index], key) == 0)
{
_values[index] = value;
return;
}
InsertAt(index, key, value);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(TKey key, TValue value)
{
int index = BinarySearch(key);
if (index < _count && _comparer.Compare(_keys[index], key) == 0)
{
throw new ArgumentException("An element with the same key already exists.", nameof(key));
}
InsertAt(index, key, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(TKey key)
{
int index = BinarySearch(key);
return index < _count && _comparer.Compare(_keys[index], key) == 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(TKey key)
{
int index = BinarySearch(key);
if (index >= _count || _comparer.Compare(_keys[index], key) != 0)
{
return false;
}
if (index < _count - 1)
{
Array.Copy(_keys, index + 1, _keys, index, _count - index - 1);
Array.Copy(_values, index + 1, _values, index, _count - index - 1);
}
_count--;
if (_needsClearing)
{
_keys[_count] = default!;
_values[_count] = default!;
}
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TKey key, out TValue value)
{
int index = BinarySearch(key);
if (index < _count && _comparer.Compare(_keys[index], key) == 0)
{
value = _values[index];
return true;
}
value = default!;
return false;
}
public void Clear()
{
if (_needsClearing)
{
Array.Clear(_keys, 0, _count);
Array.Clear(_values, 0, _count);
}
_count = 0;
}
public TKey GetKey(int index) => _keys[index];
public TValue GetValue(int index) => _values[index];
public Enumerator GetEnumerator() => new(this);
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
private void InsertAt(int index, TKey key, TValue value)
{
if (_count == _keys.Length)
{
Grow();
}
if (index < _count)
{
Array.Copy(_keys, index, _keys, index + 1, _count - index);
Array.Copy(_values, index, _values, index + 1, _count - index);
}
_keys[index] = key;
_values[index] = value;
_count++;
}
private void Grow()
{
int newSize;
if (_keys.Length >= RecyclableDefaults.MaxPooledBlockSize)
{
newSize = RecyclableDefaults.MaxPooledBlockSize;
}
else
{
int doubled = _keys.Length << 1;
newSize = (int)Math.Min(doubled, RecyclableDefaults.MaxPooledBlockSize);
}
var newKeys = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<TKey>.RentShared(newSize)
: new TKey[newSize];
var newValues = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<TValue>.RentShared(newSize)
: new TValue[newSize];
Array.Copy(_keys, newKeys, _count);
Array.Copy(_values, newValues, _count);
if (_keys.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<TKey>.ReturnShared(_keys, _needsClearing);
}
if (_values.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<TValue>.ReturnShared(_values, _needsClearing);
}
_keys = newKeys;
_values = newValues;
}
private int BinarySearch(TKey key)
{
int low = 0;
int high = _count - 1;
while (low <= high)
{
int mid = (low + high) >> 1;
int cmp = _comparer.Compare(_keys[mid], key);
if (cmp == 0)
{
return mid;
}
if (cmp < 0)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
return low;
}
public void Dispose()
{
if (_disposed)
{
return;
}
Clear();
if (_keys.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<TKey>.ReturnShared(_keys, _needsClearing);
}
if (_values.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<TValue>.ReturnShared(_values, _needsClearing);
}
_keys = Array.Empty<TKey>();
_values = Array.Empty<TValue>();
_disposed = true;
GC.SuppressFinalize(this);
}
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
{
private readonly RecyclableSortedDictionary<TKey, TValue> _dictionary;
private int _index;
private KeyValuePair<TKey, TValue> _current;
internal Enumerator(RecyclableSortedDictionary<TKey, TValue> dictionary)
{
_dictionary = dictionary;
_index = 0;
_current = default;
}
public KeyValuePair<TKey, TValue> Current => _current;
object IEnumerator.Current => _current;
public bool MoveNext()
{
if (_index >= _dictionary._count)
{
return false;
}
_current = new KeyValuePair<TKey, TValue>(_dictionary._keys[_index], _dictionary._values[_index]);
_index++;
return true;
}
public void Reset()
{
_index = 0;
_current = default;
}
public void Dispose()
{
}
}
}
}
+246 -46
View File
@@ -1,81 +1,281 @@
using Recyclable.Collections.Linq;
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal class RecyclableSortedList<TKey, TValue> : RecyclableLongList<(TKey Key, TValue Value)>
internal sealed class RecyclableSortedList<TKey, TValue> : IEnumerable<(TKey Key, TValue Value)>, IDisposable
where TKey : notnull
{
private static readonly IComparer<(TKey, TValue)> _comparer = new FrequencyTupleComparer<TKey, TValue>();
private uint _isUpdating;
private readonly SystemRandomNumberGenerator _randomNumberGenerator = new();
internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType;
protected void ListUpdated()
internal TKey[] _keys;
internal TValue[] _values;
internal int _count;
internal readonly IComparer<TKey> _comparer;
internal bool _disposed;
public RecyclableSortedList(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer<TKey>? comparer = null)
{
QuickSort();
if (initialCapacity < 1)
{
initialCapacity = 1;
}
public RecyclableSortedList() : base()
if (!BitOperations.IsPow2((uint)initialCapacity))
{
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
}
public RecyclableSortedList(Dictionary<TKey, TValue> source, int blockSize = RecyclableDefaults.BlockSize)
: this(source.Select(x => (x.Key, x.Value)), blockSize)
{
_keys = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<TKey>.RentShared(initialCapacity)
: new TKey[initialCapacity];
_values = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<TValue>.RentShared(initialCapacity)
: new TValue[initialCapacity];
_count = 0;
_comparer = comparer ?? Comparer<TKey>.Default;
}
public RecyclableSortedList(IEnumerable<(TKey Key, TValue Value)> source, int blockSize = RecyclableDefaults.BlockSize)
: base(source, blockSize)
public int Count => _count;
public TValue this[TKey key]
{
QuickSort();
get
{
int index = BinarySearch(key);
if (index >= 0)
{
return _values[index];
}
public RecyclableSortedList(int blockSize = RecyclableDefaults.BlockSize)
: base(blockSize)
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "Key not found");
return default!;
}
set
{
int index = BinarySearch(key);
if (index >= 0)
{
_values[index] = value;
return;
}
public bool IsUpdating => _isUpdating > 0;
public void Add(TKey key, TValue value) => Add((key, value));
public void BeginUpdate() => _isUpdating++;
public new void Clear()
{
BeginUpdate();
try
{
base.Clear();
}
finally
{
EndUpdate();
Insert(~index, key, value);
}
}
public void EndUpdate(bool raiseListUpdated = true)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(TKey key, TValue value)
{
if (_isUpdating > 0)
int index = BinarySearch(key);
if (index >= 0)
{
_isUpdating--;
if (raiseListUpdated && _isUpdating == 0)
{
ListUpdated();
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "An element with the same key already exists.");
}
Insert(~index, key, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(TKey key) => BinarySearch(key) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TKey key, out TValue value)
{
int index = BinarySearch(key);
if (index >= 0)
{
value = _values[index];
return true;
}
value = default!;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(TKey key)
{
int index = BinarySearch(key);
if (index < 0)
{
return false;
}
if (index < _count - 1)
{
Array.Copy(_keys, index + 1, _keys, index, _count - index - 1);
Array.Copy(_values, index + 1, _values, index, _count - index - 1);
}
_count--;
if (_needsClearing)
{
_keys[_count] = default!;
_values[_count] = default!;
}
return true;
}
public void Clear()
{
if (_needsClearing && _count > 0)
{
Array.Clear(_keys, 0, _count);
Array.Clear(_values, 0, _count);
}
_count = 0;
}
public TKey GetKey(int index) => _keys[index];
public TValue GetValue(int index) => _values[index];
public Enumerator GetEnumerator() => new(this);
IEnumerator<(TKey Key, TValue Value)> IEnumerable<(TKey Key, TValue Value)>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
public void Dispose()
{
if (_disposed)
{
return;
}
if (_keys.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<TKey>.ReturnShared(_keys, _needsClearing);
}
if (_values.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<TValue>.ReturnShared(_values, _needsClearing);
}
_keys = Array.Empty<TKey>();
_values = Array.Empty<TValue>();
_count = 0;
_disposed = true;
GC.SuppressFinalize(this);
}
private void Insert(int index, TKey key, TValue value)
{
if (_count == _keys.Length)
{
Grow(_count + 1);
}
if (index < _count)
{
Array.Copy(_keys, index, _keys, index + 1, _count - index);
Array.Copy(_values, index, _values, index + 1, _count - index);
}
_keys[index] = key;
_values[index] = value;
_count++;
}
private void Grow(int min)
{
int newSize = _keys.Length << 1;
if (newSize < min)
{
newSize = min;
}
var newKeys = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<TKey>.RentShared(newSize)
: new TKey[newSize];
var newValues = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<TValue>.RentShared(newSize)
: new TValue[newSize];
Array.Copy(_keys, newKeys, _count);
Array.Copy(_values, newValues, _count);
if (_keys.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<TKey>.ReturnShared(_keys, _needsClearing);
}
if (_values.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<TValue>.ReturnShared(_values, _needsClearing);
}
_keys = newKeys;
_values = newValues;
}
private int BinarySearch(TKey key)
{
int low = 0;
int high = _count - 1;
while (low <= high)
{
int mid = (low + high) >> 1;
int cmp = _comparer.Compare(_keys[mid], key);
if (cmp == 0)
{
return mid;
}
if (cmp < 0)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
public void QuickSort()
{
BeginUpdate();
try
{
QuickSortExtensions<(TKey, TValue)>.QuickSort(this, _comparer, _randomNumberGenerator);
return ~low;
}
finally
public struct Enumerator : IEnumerator<(TKey Key, TValue Value)>
{
private readonly RecyclableSortedList<TKey, TValue> _list;
private int _index;
private (TKey, TValue) _current;
internal Enumerator(RecyclableSortedList<TKey, TValue> list)
{
_list = list;
_index = 0;
_current = default!;
}
public (TKey Key, TValue Value) Current => _current;
object IEnumerator.Current => _current;
public bool MoveNext()
{
if (_index >= _list._count)
{
return false;
}
_current = (_list._keys[_index], _list._values[_index]);
_index++;
return true;
}
public void Reset()
{
_index = 0;
_current = default!;
}
public void Dispose()
{
EndUpdate(false);
}
}
}
}
@@ -0,0 +1,215 @@
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal sealed class RecyclableSortedSet<T> : IEnumerable<T>, IDisposable where T : notnull
{
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
internal readonly IComparer<T> _comparer;
internal T[] _items;
internal int _count;
internal bool _disposed;
public RecyclableSortedSet(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer<T>? comparer = null)
{
if (initialCapacity < 1)
{
initialCapacity = 1;
}
if (!BitOperations.IsPow2((uint)initialCapacity))
{
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
}
_items = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(initialCapacity)
: new T[initialCapacity];
_count = 0;
_comparer = comparer ?? Comparer<T>.Default;
}
public int Count => _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add(T item)
{
int index = BinarySearch(item);
if (index < _count && _comparer.Compare(_items[index], item) == 0)
{
return false;
}
if (_count == _items.Length)
{
Grow();
}
if (index < _count)
{
Array.Copy(_items, index, _items, index + 1, _count - index);
}
_items[index] = item;
_count++;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(T item)
{
int index = BinarySearch(item);
return index < _count && _comparer.Compare(_items[index], item) == 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(T item)
{
int index = BinarySearch(item);
if (index >= _count || _comparer.Compare(_items[index], item) != 0)
{
return false;
}
if (index < _count - 1)
{
Array.Copy(_items, index + 1, _items, index, _count - index - 1);
}
_count--;
if (_needsClearing)
{
_items[_count] = default!;
}
return true;
}
public void Clear()
{
if (_needsClearing)
{
Array.Clear(_items, 0, _count);
}
_count = 0;
}
public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
private void Grow()
{
int newSize;
if (_items.Length >= RecyclableDefaults.MaxPooledBlockSize)
{
newSize = RecyclableDefaults.MaxPooledBlockSize;
}
else
{
var doubled = _items.Length << 1;
newSize = (int)Math.Min(doubled, RecyclableDefaults.MaxPooledBlockSize);
}
var newItems = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(newSize)
: new T[newSize];
Array.Copy(_items, newItems, _count);
if (_items.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(_items, _needsClearing);
}
_items = newItems;
}
private int BinarySearch(T item)
{
int low = 0;
int high = _count - 1;
while (low <= high)
{
int mid = (low + high) >> 1;
int cmp = _comparer.Compare(_items[mid], item);
if (cmp == 0)
{
return mid;
}
if (cmp < 0)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
return low;
}
public void Dispose()
{
if (_disposed)
{
return;
}
Clear();
if (_items.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(_items, _needsClearing);
}
_items = Array.Empty<T>();
_disposed = true;
GC.SuppressFinalize(this);
}
public struct Enumerator : IEnumerator<T>
{
private readonly RecyclableSortedSet<T> _set;
private int _index;
private T? _current;
internal Enumerator(RecyclableSortedSet<T> set)
{
_set = set;
_index = 0;
_current = default;
}
public T Current => _current!;
object IEnumerator.Current => _current!;
public bool MoveNext()
{
if (_index >= _set._count)
{
return false;
}
_current = _set._items[_index++];
return true;
}
public void Reset()
{
_index = 0;
_current = default;
}
public void Dispose()
{
}
}
}
}
+193 -53
View File
@@ -1,94 +1,234 @@
using System.Collections;
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal class RecyclableStack<T> : IList<T>, IDisposable
internal sealed class RecyclableStack<T> : IEnumerable<T>, IDisposable
{
private static readonly bool _needsClearing = !typeof(T).IsValueType;
private bool _disposedValue;
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
protected RecyclableLongList<T> List { get; }
public int Count => checked((int)List._longCount);
public long LongCount
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static RecyclableArrayPoolChunk<T> RentChunk(int size, RecyclableArrayPoolChunk<T>? previous)
{
get => List._longCount;
set => List._longCount = value;
}
public bool IsReadOnly => false;
public T this[int index] { get => this[(long)index]; set => this[(long)index] = value; }
public T this[long index] { get => List[index]; set => List[index] = value; }
public RecyclableStack(int blockSize = RecyclableDefaults.BlockSize)
var chunk = RecyclableArrayPoolChunkPool<T>.Rent();
if (chunk.Value.Length < size)
{
List = new(blockSize);
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
}
public RecyclableStack(IEnumerable<T> list, int blockSize = RecyclableDefaults.BlockSize)
{
List = new(list, blockSize);
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(size)
: new T[size];
}
chunk.Index = 0;
chunk.Previous = previous;
chunk.Next = null;
return chunk;
}
public RecyclableStack(RecyclableLongList<T> list, int blockSize = RecyclableDefaults.BlockSize)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReturnChunk(RecyclableArrayPoolChunk<T> chunk)
{
List = new(list, blockSize);
RecyclableArrayPoolChunkPool<T>.Return(chunk);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DisposeChunk(RecyclableArrayPoolChunk<T> chunk)
{
RecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing);
}
internal RecyclableArrayPoolChunk<T> _current;
internal bool _disposed;
internal long _capacity;
internal long _count;
public RecyclableStack(int initialCapacity = RecyclableDefaults.InitialCapacity)
{
if (initialCapacity < 1)
{
initialCapacity = 1;
}
if (!BitOperations.IsPow2((uint)initialCapacity))
{
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
}
_current = RentChunk(initialCapacity, null);
_capacity = initialCapacity;
_count = 0;
}
public RecyclableStack(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity)
{
foreach (var item in source)
{
Push(item);
}
}
public int Count => checked((int)_count);
public long LongCount => _count;
public void Push(T item)
{
List.Add(item);
if (_current.Index == _current.Value.Length)
{
Grow();
}
_current.Value[_current.Index++] = item;
_count++;
}
public void Add(T item) => Push(item);
public T Pop()
{
RecyclableLongList<T> list = List;
var itemIndex = (int)(list.LongCount & list._blockSizeMinus1) - 1;
var blockIndex = (int)(LongCount >> list._blockSizePow2BitShift);
var toRemove = list._memoryBlocks[blockIndex][itemIndex];
LongCount--;
if (_count == 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "Stack is empty");
}
_count--;
_current.Index--;
var value = _current.Value[_current.Index];
if (_needsClearing)
{
#nullable disable
new Span<T>(list._memoryBlocks[blockIndex])[itemIndex] = default;
#nullable restore
_current.Value[_current.Index] = default!;
}
if (itemIndex == 0)
if (_current.Index == 0 && _current.Previous != null)
{
list.RemoveBlock(blockIndex);
ReleaseCurrentChunk();
}
return toRemove;
return value;
}
public int IndexOf(T item) => List.IndexOf(item);
public long LongIndexOf(T item) => List.LongIndexOf(item);
public void Insert(int index, T item) => List.Insert(index, item);
public void RemoveAt(int index) => List.RemoveAt(index);
public void Add(T item) => List.Add(item);
public void Clear() => List.Clear();
public bool Contains(T item) => List.Contains(item);
public void CopyTo(T[] array, int arrayIndex) => List.CopyTo(array, arrayIndex);
public bool Remove(T item) => List.Remove(item);
public IEnumerator<T> GetEnumerator() => List.Reverse().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
protected virtual void Dispose(bool disposing)
public void Clear()
{
if (!_disposedValue)
while (_current.Previous != null)
{
Clear();
_disposedValue = true;
ReleaseCurrentChunk();
}
if (_needsClearing && _current.Index > 0)
{
Array.Clear(_current.Value, 0, _current.Index);
}
_capacity = _current.Value.Length;
_current.Index = 0;
_count = 0;
}
public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
private void Grow()
{
int newSize;
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
{
newSize = RecyclableDefaults.MaxPooledBlockSize;
}
else
{
var doubled = _capacity << 1;
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
}
var newChunk = RentChunk(newSize, _current);
_current.Next = newChunk;
_current = newChunk;
_capacity += newSize;
}
private void ReleaseCurrentChunk()
{
var toReturn = _current;
_current = toReturn.Previous!;
_current.Next = null;
_capacity -= toReturn.Value.Length;
ReturnChunk(toReturn);
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
if (_disposed)
{
return;
}
var chunk = _current;
while (chunk != null)
{
var previous = chunk.Previous;
DisposeChunk(chunk);
chunk = previous;
}
_disposed = true;
GC.SuppressFinalize(this);
}
public struct Enumerator : IEnumerator<T>
{
private readonly RecyclableArrayPoolChunk<T>? _start;
private RecyclableArrayPoolChunk<T>? _chunk;
private int _index;
private T? _current;
internal Enumerator(RecyclableStack<T> stack)
{
_start = stack._current;
_chunk = stack._current;
_index = _chunk != null ? _chunk.Index - 1 : -1;
_current = default;
}
public T Current => _current!;
object IEnumerator.Current => _current!;
public bool MoveNext()
{
if (_chunk == null)
{
return false;
}
if (_index < 0)
{
_chunk = _chunk.Previous;
if (_chunk == null)
{
return false;
}
_index = _chunk.Index - 1;
}
_current = _chunk.Value[_index--];
return true;
}
public void Reset()
{
_chunk = _start;
_index = _chunk != null ? _chunk.Index - 1 : -1;
}
public void Dispose()
{
_chunk = null;
_current = default;
}
}
}
}
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff. Show more