Merge branch 'recyclable-block-list-added'
This commit is contained in:
commit
1c8d1998f5
82 files changed
+17135
-1057
No files matched your search
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||
"name": ".NET Core Launch (console)",
|
||||
"type": "coreclr",
|
||||
"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",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/Recyclable.Collections.Benchmarks",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"isBuildCommand": true,
|
||||
"label": "build debug",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Recyclable.Collections.Benchmarks/Recyclable.Collections.Benchmarks.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary",
|
||||
"-f", "net7.0"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build release",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Recyclable.Collections.Benchmarks/Recyclable.Collections.Benchmarks.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary",
|
||||
"-c", "Release",
|
||||
"-f", "net7.0"
|
||||
],
|
||||
"problemMatcher": "$msCompile",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/Recyclable.Collections.Benchmarks/Recyclable.Collections.Benchmarks.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/Recyclable.Collections.Benchmarks/Recyclable.Collections.Benchmarks.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,35 +1,338 @@
|
||||
# Recyclable.Collections
|
||||
`Recyclable.Collections` project is an open source framework for operating dynamic lists at performance close to raw arrays, but fairly unlimited in size. It aims at providing minimal memory footprint. It implements `IList<T>`'s interface and is targeted as direct replacements of `List<T>`, `SortableList<T>`, `PriorityQueue<T>` & similar.
|
||||
|
||||
# Characteristicts of the classes
|
||||
## Included
|
||||
* `RecyclableList<T>`
|
||||
* `RecyclableLongList<T>`
|
||||
|
||||
# Milestones
|
||||
1. ✅ Create basic classes
|
||||
1. ✅ `RecyclableList<T>`
|
||||
1. ✅ `RecyclableLongList<T>`
|
||||
1. 🅿️ `RecyclableQueue<T>`
|
||||
1. 🅿️ `RecyclableSortedList<T>`
|
||||
1. 🅿️ `RecyclableStack<T>`
|
||||
1. 🅿️ `RecyclableUnorderedList<T>`
|
||||
1. ✅ Create basic unit tests
|
||||
1. ✅ `RecyclableList<T>`
|
||||
1. ✅ `RecyclableLongList<T>`
|
||||
1. 🅿️ `RecyclableQueue<T>`
|
||||
1. 🅿️ `RecyclableSortedList<T>`
|
||||
1. 🅿️ `RecyclableStack<T>`
|
||||
1. 🅿️ `RecyclableUnorderedList<T>`
|
||||
1. ✅ Optimize `RecyclableList<T>`
|
||||
1. ✅ `Add`
|
||||
1. ✅ `AddRange`
|
||||
1. ✅ when source is `array<T>`
|
||||
1. ✅ when source is `List<T>`
|
||||
1. ✅ when source is `IList<T>`
|
||||
1. ✅ when source is `RecyclableList<T>`
|
||||
1. ✅ when source is `RecyclableLongList<T>`
|
||||
1. ✅ when source is `IEnumerable<T>`
|
||||
1. ✅ when source has non-enumerated count
|
||||
1. ✅ `Clear`
|
||||
1. ✅ `Contains`
|
||||
1. ✅ `CopyTo`
|
||||
1. ✅ `EnsureCapacity`
|
||||
1. 🅿️ `GetEnumerator`
|
||||
1. ✅ `IndexOf`
|
||||
1. ✅ `Insert`
|
||||
1. ✅ `Remove`
|
||||
1. ✅ `RemoveAt`
|
||||
1. ✅ `Resize`
|
||||
1. ✅ `this[int index]`
|
||||
1. ✅ Port `RecyclableList<T>` implementation to `RecyclableLongList<T>`
|
||||
1. 👉 Optimize `RecyclableLongList<T>`
|
||||
1. ✅ `Add`
|
||||
1. ✅ `AddRange`
|
||||
1. ✅ when source is `array<T>`
|
||||
1. ✅ when source is `List<T>`
|
||||
1. ✅ when source is `IList<T>`
|
||||
1. ✅ when source is `RecyclableList<T>`
|
||||
1. ✅ when source is `RecyclableLongList<T>`
|
||||
1. ✅ when source is `IEnumerable<T>`
|
||||
1. ✅ when source has non-enumerated count
|
||||
1. ✅ `Clear`
|
||||
1. ✅ `Contains`
|
||||
1. 🅿️ `CopyTo`
|
||||
1. ✅ `EnsureCapacity`
|
||||
1. 🅿️ `GetEnumerator`
|
||||
1. ✅ `IndexOf`
|
||||
1. ✅ `LongIndexOf`
|
||||
1. ✅ `Insert`
|
||||
1. ✅ `Remove`
|
||||
1. ✅ `RemoveAt(int index)`
|
||||
1. ✅ `RemoveAt(long index)`
|
||||
1. ✅ `RemoveAt`
|
||||
1. ✅ `Resize`
|
||||
1. ✅ `this[int index]`
|
||||
1. ✅ `this[long index]`
|
||||
1. ✅ Rename `RecyclableList<T>` to `RecyclableLongList<T>`
|
||||
1. ✅ Rename `RecyclableArrayList<T>` to `RecyclableList<T>`
|
||||
1. ✅ Fix failing tests
|
||||
1. ✅ Hide not ready classes
|
||||
1. ✅ `RecyclableQueue<T>`
|
||||
1. ✅ `RecyclableSortedList<T>`
|
||||
1. ✅ `RecyclableStack<T>`
|
||||
1. ✅ `RecyclableUnorderedList<T>`
|
||||
1. 👉 Add support for `ReadOnlySpan<T>`
|
||||
1. 🅿️ Implement list versioning to allow data change identification
|
||||
1. ✅ Release 0.0.3-alpha
|
||||
1. 🅿️ Implement `List<T>` interfaces
|
||||
1. 🅿️ `ICollection<T>`
|
||||
1. 🅿️ `IEnumerable<T>`
|
||||
1. 🅿️ `IEnumerable`
|
||||
1. 🅿️ `IList<T>`
|
||||
1. 🅿️ `IReadOnlyCollection<T>`
|
||||
1. 🅿️ `IReadOnlyList<T>`
|
||||
1. 🅿️ `ICollection`
|
||||
1. 🅿️ `IList`
|
||||
1. 🅿️ Make sure that `NeedsClearing` is used & items are cleared in
|
||||
1. 🅿️ `Clear`
|
||||
1. 🅿️ `Dispose`
|
||||
1. 🅿️ `Remove`
|
||||
1. 🅿️ `RemoveAt`
|
||||
1. 🅿️ `RemoveBlock`
|
||||
1. 🅿️ Release 0.0.3-beta
|
||||
1. 🅿️ Add support for `ulong` indexing
|
||||
1. 🅿️ Convert `_memoryBlocks` to `Array` to allow `ulong` lengths
|
||||
1. 🅿️ Convert block indexes from `int` to `ulong` or `long`
|
||||
1. 🅿️ Final optimizations
|
||||
1. 🅿️ Replace `Math` class usages with `if` statements
|
||||
1. 🅿️ Replace `a - b > 0` & `a - b < 0` comparisons with `a > b` & `a < b`
|
||||
1. 🅿️ Replace `a + b > 0` & `a + b < 0` comparisons with `a > b` & `a < b`
|
||||
1. 🅿️ Replace `a / b` & `a * b` calculations with equivalents, where possible
|
||||
1. 🅿️ Replace virtual calls with static calls
|
||||
1. 🅿️ Replace `blockSize` sums by powers of 2, minus 1
|
||||
1. 🅿️ Remove type castings, if possible
|
||||
1. 🅿️ Convert generic methods to non-generic
|
||||
1. 🅿️ Overflow review
|
||||
1. 🅿️ Add type casting to `long` for `<<` & `>>` operations, where required
|
||||
1. 🅿️ Make type castings `checked`
|
||||
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableList<T>`
|
||||
1. 🅿️ Release 0.0.3
|
||||
1. 🅿️ Implement `RecyclableQeueue<T>`
|
||||
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableQueue<T>`
|
||||
1. 🅿️ Release 0.0.4
|
||||
1. 🅿️ Implement `RecyclableStack<T>`
|
||||
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableStack<T>`
|
||||
1. 🅿️ Release 0.0.5
|
||||
1. 🅿️ Implement `RecyclableSortedList<T>`
|
||||
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableSortedList<T>`
|
||||
1. 🅿️ Release 0.0.6
|
||||
1. 🅿️ Implement `RecyclableUnorderedList<T>`
|
||||
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableUnorderedList<T>`
|
||||
1. 🅿️ Release 0.0.7
|
||||
1. 🅿️ Optimize `OneSizeArrayPool`
|
||||
1. 🅿️ Review locks
|
||||
1. 🅿️ Measure multi-threading performance
|
||||
1. 🅿️ Review `RecyclableArrayPool`
|
||||
1. 🅿️ Review locks
|
||||
1. 🅿️ Measure multi-threading performance
|
||||
1. 🅿️ Optimize `MemoryBucket<T>`
|
||||
1. 🅿️ Convert to `struct`, if possible
|
||||
1. 🅿️ Find out if there are better replacements
|
||||
1. 🅿️ Release 0.0.8
|
||||
1. 🅿️ Optimize
|
||||
1. 🅿️ `IndexOfSynchronizationContext`
|
||||
1. 🅿️ `IndexOfSynchronizationContextPool`
|
||||
1. 🅿️ `ManualResetEventSlimmer`
|
||||
1. 🅿️ `ManualResetEventSlimmerPool`
|
||||
1. 🅿️ `SpinLockSlimmer`
|
||||
1. 🅿️ Release 0.0.9-beta
|
||||
1. 🅿️ Cleanup
|
||||
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
||||
1. 🅿️ Cleanup `RecyclableLongListExtensions`
|
||||
1. 🅿️ `ListExtensions`
|
||||
1. 🅿️ `MathUtils`
|
||||
1. 🅿️ Optimize
|
||||
1. 🅿️ `ListExtensions`
|
||||
1. 🅿️ `MathUtils`
|
||||
1. 🅿️ `SystemRandomNumberGenerator`
|
||||
1. 🅿️ Release 1.0.0
|
||||
|
||||
# Characteristics of the classes
|
||||
|
||||
## Common
|
||||
* All classes implement `IDisposable` interface & SHOULD be disposed after use. That's to return block arrays taken from the shared pool. It may be foreseen as an issue for replacement in existing code, which obviously is missing `using` clause. But considering that `Dispose` will be called by `GC` anyway, it should cause issues in specific scenarios, only. In either case the fix is one-word addition of `using`.
|
||||
* Memory blocks are created in 2 ways, depending on `blockSize` value
|
||||
* when `blockSize < 128` ⇨ blocks are created as regular `arrays`.
|
||||
* when `blockSize >= 128` ⇨ blocks are taken from and returned to `ArrayPool<T>.Shared`, when blocks are removed.
|
||||
* maximum `blockSize` value is `2_147_483_591`, which corresponds more or less to the maximum array size.
|
||||
* `blockSize` MUST be a power of 2. It will be rounded up to the closest power of 2, if needed. That is due to high performance gain on some operations, like the calculation of item index in a block.
|
||||
* Array pools are shared between the same `T` type. I.e. `List<int>` will use a different pool from `List<short>` and so on. For high concurrency environments you may want to provide your own pools, when this feature becomes available in the upcoming releases.
|
||||
* Trying to access `this[int index]`, `this[long index]`, `Count`, `LongCount` etc. when `Capacity == 0` will raise `NullReferenceException`. This is by design to remove all non-critical code from the constructor.
|
||||
|
||||
## `RecyclableList<T>`
|
||||
* Range: `int`
|
||||
* Interfaces: `IList<T>`, `IEnumerable<T>`, `IDisposable`
|
||||
This is the direct equivalent of `List<T>` class, except that the arrays are taken from the shared pool, instead of directly being allocated. It proves to out perform `List<T>` & the standard arrays in certain operations and is fairly close in others. But they may perform worse in certain scenarios. If you're interested in details, please refer to benchmarks.
|
||||
|
||||
## `RecyclableLongList<T>`, `RecyclableSortableList<T>`, `RecyclableQueue<T>`, `RecyclableStack<T>`
|
||||
* Range: `long`
|
||||
* Interfaces: `IList<T>`, `ILongList<T>`, `IEnumerable<T>`, `IDisposable`
|
||||
* All classes provide large storage capabilities, in practice limited only by the memory available in your system.
|
||||
* All data is stored in blocks, with the provided `int blockSize` or the default, currently being `10,240` items in each block. It's recommended to use smaller numbers, if you foresee storing low no. of items on the list.
|
||||
* Memory blocks are created in 2 ways, depending on `blockSize` value
|
||||
* when `blockSize < 100` ⇨ blocks are created as regular `arrays`
|
||||
* when `blockSize >= 100` ⇨ blocks are taken from and returned to `ArrayPool<T>.Shared`, when blocks are removed
|
||||
* All classes implement `IDisposable` interface & SHOULD be disposed after use. That's to return block arrays taken from the shared pool. It may be foreseen as an issue for replacement in existing code, which obviously is missing `using` clause. But considering that `Dispose` will be called by `GC` anyway, it should cause issues in specific scenarios, only. In either case the fix is one-word addition of `using`.
|
||||
* There is no memory copying to increase the capacity of the recyclable classes, anymore. If more capacity is needed, a new memory block is allocate & added to the internal list of blocks. This is the only list that may re-allocate memory block & copy their content to grow. That shouldn't be an issue considering limited no. of blocks stored by each class in practical scenarios. Currently I'm not planning any works around that. Shall there be a need to eliminate it, you're welcomed to file a PR with the proposed changes.
|
||||
* `Insert`, `Remove` & `RemoveAt` methods raise `NotSupportedException`. This is by design in the current version, because it involves performance degradadion. I'm planning works around this in the upcoming weeks. If you need that sooner, you're welcomed to file a PR with the proposed changes.
|
||||
* There is no memory copying to increase the capacity of the recyclable classes, unless the no. of items exceeds the no. of allocated blocks. In that case, a new array of blocks is created to accommodate . If more capacity is needed, a new memory block is allocate & added to the internal list of blocks. This is the only list that may re-allocate memory block & copy their content to grow. That shouldn't be an issue considering limited no. of blocks stored by each class in practical scenarios. Currently I'm not planning any works around that. Shall there be a need to eliminate it, you're welcomed to file a PR with the proposed changes.
|
||||
* ⚠️ `IndexOf` will automatically switch to parallel search mode. Collections smaller than (currently) 850_000 items are guaranteed to find the 1st occurrence of the `item` parameter. Collections bigger than (currently) 850_000 items will be scanned using parallelization. If either case it'll return the index of the first matching item. This note also applies to `Contains`, because it internally uses `IndexOf`. The value of 850_000 may become configurable in the future, unless it proves to result in much worse performance.
|
||||
|
||||
The parallelization utilizes default `Task` scheduling, resulting in non-deterministic order in which search tasks are executed. In most cases it resulted in significant performance improvements. Below you can find the benchmarks for various item counts.
|
||||
|
||||
```csharp
|
||||
BenchmarkDotNet=v0.13.3, OS=Windows 11 (10.0.22621.1702)
|
||||
AMD Ryzen 7 4700U with Radeon Graphics, 1 CPU, 8 logical and 8 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 | TestCase | BaselineBenchmarkType | BenchmarkType | TestObjectCount | Mean | Error | StdDev | Median | Ratio | RatioSD | Allocated | Alloc Ratio |
|
||||
|--------- |--------------------- |---------------------- |------------------- |---------------- |----------------------:|---------------------:|-------------------:|----------------------:|------:|------------:|-----------:|------------:|
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 1 | 16.60 ns | 0.060 ns | 0.056 ns | 16.57 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 1 | 19.20 ns | 0.137 ns | 0.121 ns | 19.16 ns | 1.16 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 2 | 27.64 ns | 0.188 ns | 0.176 ns | 27.65 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 2 | 31.90 ns | 0.102 ns | 0.095 ns | 31.89 ns | 1.15 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 3 | 28.35 ns | 0.146 ns | 0.137 ns | 28.42 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 3 | 32.63 ns | 0.177 ns | 0.166 ns | 32.55 ns | 1.15 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 4 | 39.05 ns | 0.112 ns | 0.105 ns | 38.99 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 4 | 44.01 ns | 0.168 ns | 0.149 ns | 43.98 ns | 1.13 | 0.00 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 5 | 39.77 ns | 0.163 ns | 0.152 ns | 39.69 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 5 | 46.72 ns | 0.943 ns | 1.258 ns | 46.87 ns | 1.17 | 0.03 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 6 | 51.14 ns | 0.202 ns | 0.189 ns | 51.10 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 6 | 59.75 ns | 1.076 ns | 1.105 ns | 59.75 ns | 1.17 | 0.02 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 7 | 51.60 ns | 0.126 ns | 0.105 ns | 51.58 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 7 | 58.40 ns | 1.125 ns | 0.997 ns | 58.38 ns | 1.13 | 0.02 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 8 | 61.22 ns | 0.301 ns | 0.281 ns | 61.22 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 8 | 73.96 ns | 0.397 ns | 0.372 ns | 73.98 ns | 1.21 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 9 | 62.21 ns | 0.074 ns | 0.062 ns | 62.22 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 9 | 96.22 ns | 0.712 ns | 0.631 ns | 96.16 ns | 1.55 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 10 | 75.18 ns | 0.244 ns | 0.228 ns | 75.07 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 10 | 83.85 ns | 0.224 ns | 0.209 ns | 83.74 ns | 1.12 | 0.00 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 11 | 74.93 ns | 0.214 ns | 0.200 ns | 74.86 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 11 | 83.78 ns | 0.484 ns | 0.453 ns | 83.53 ns | 1.12 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 12 | 86.86 ns | 0.316 ns | 0.295 ns | 86.88 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 12 | 106.61 ns | 1.246 ns | 1.224 ns | 106.88 ns | 1.23 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 13 | 88.02 ns | 0.273 ns | 0.255 ns | 87.90 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 13 | 96.74 ns | 0.074 ns | 0.066 ns | 96.73 ns | 1.10 | 0.00 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 14 | 100.74 ns | 0.344 ns | 0.322 ns | 100.79 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 14 | 110.46 ns | 0.359 ns | 0.336 ns | 110.27 ns | 1.10 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 15 | 100.67 ns | 0.405 ns | 0.359 ns | 100.55 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 15 | 127.81 ns | 1.472 ns | 1.305 ns | 128.04 ns | 1.27 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 16 | 114.05 ns | 0.520 ns | 0.486 ns | 113.75 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 16 | 141.99 ns | 0.510 ns | 0.477 ns | 141.82 ns | 1.24 | 0.00 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 17 | 114.63 ns | 0.432 ns | 0.404 ns | 114.41 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 17 | 125.91 ns | 0.363 ns | 0.322 ns | 125.79 ns | 1.10 | 0.00 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 18 | 127.90 ns | 0.404 ns | 0.378 ns | 128.07 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 18 | 140.47 ns | 0.452 ns | 0.423 ns | 140.24 ns | 1.10 | 0.00 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 19 | 128.56 ns | 0.375 ns | 0.350 ns | 128.41 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 19 | 152.84 ns | 1.948 ns | 1.822 ns | 152.48 ns | 1.19 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 20 | 150.88 ns | 0.538 ns | 0.477 ns | 150.97 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 20 | 184.59 ns | 0.871 ns | 0.772 ns | 184.61 ns | 1.22 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 30 | 217.63 ns | 0.967 ns | 0.905 ns | 217.71 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 30 | 244.89 ns | 1.417 ns | 1.326 ns | 244.56 ns | 1.13 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 32 | 266.32 ns | 2.378 ns | 2.224 ns | 266.71 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 32 | 310.60 ns | 1.946 ns | 1.820 ns | 310.93 ns | 1.17 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 40 | 362.07 ns | 2.741 ns | 2.564 ns | 362.60 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 40 | 401.02 ns | 1.604 ns | 1.500 ns | 400.31 ns | 1.11 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 50 | 400.35 ns | 3.445 ns | 3.223 ns | 400.41 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 50 | 485.05 ns | 3.031 ns | 2.531 ns | 485.54 ns | 1.21 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 60 | 582.48 ns | 4.689 ns | 4.386 ns | 581.95 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 60 | 664.89 ns | 3.413 ns | 3.192 ns | 663.75 ns | 1.14 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 80 | 845.29 ns | 7.416 ns | 6.937 ns | 843.90 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 80 | 915.71 ns | 5.696 ns | 5.328 ns | 916.91 ns | 1.08 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 90 | 994.91 ns | 15.228 ns | 14.244 ns | 998.73 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 90 | 1,074.04 ns | 12.721 ns | 11.277 ns | 1,077.75 ns | 1.08 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 100 | 1,130.91 ns | 20.393 ns | 19.076 ns | 1,130.99 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 100 | 1,185.56 ns | 23.173 ns | 21.676 ns | 1,184.10 ns | 1.05 | 0.02 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 128 | 1,610.08 ns | 16.899 ns | 15.807 ns | 1,604.90 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 128 | 1,753.55 ns | 17.254 ns | 16.140 ns | 1,752.63 ns | 1.09 | 0.02 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 256 | 5,750.75 ns | 28.609 ns | 26.760 ns | 5,758.04 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 256 | 5,893.43 ns | 29.453 ns | 27.551 ns | 5,887.99 ns | 1.02 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 512 | 21,669.95 ns | 427.575 ns | 399.954 ns | 21,713.08 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 512 | 21,674.73 ns | 68.941 ns | 64.487 ns | 21,668.86 ns | 1.00 | 0.02 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 1024 | 69,740.31 ns | 838.846 ns | 932.375 ns | 69,475.32 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 1024 | 68,980.90 ns | 450.707 ns | 421.591 ns | 68,923.79 ns | 0.99 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 2048 | 244,266.95 ns | 2,449.277 ns | 2,045.257 ns | 243,462.06 ns | 1.00 | 0.00 | - | NA |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 2048 | 239,887.40 ns | 854.306 ns | 799.118 ns | 240,015.48 ns | 0.98 | 0.01 | - | NA |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 4096 | 904,644.52 ns | 3,379.464 ns | 2,638.463 ns | 904,380.27 ns | 1.00 | 0.00 | 1 B | 1.00 |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 4096 | 915,459.26 ns | 6,529.766 ns | 6,107.947 ns | 917,327.34 ns | 1.01 | 0.01 | 1 B | 1.00 |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 8192 | 3,513,716.89 ns | 62,893.465 ns | 90,199.928 ns | 3,474,476.56 ns | 1.00 | 0.00 | 2 B | 1.00 |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 8192 | 3,486,690.89 ns | 14,705.600 ns | 13,755.627 ns | 3,481,303.91 ns | 0.99 | 0.02 | 2 B | 1.00 |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 16384 | 13,736,865.85 ns | 120,702.128 ns | 106,999.292 ns | 13,714,744.53 ns | 1.00 | 0.00 | 9 B | 1.00 |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 16384 | 14,167,800.31 ns | 43,856.428 ns | 41,023.330 ns | 14,149,225.00 ns | 1.03 | 0.01 | 9 B | 1.00 |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 32768 | 53,875,332.00 ns | 245,220.180 ns | 229,379.108 ns | 53,809,450.00 ns | 1.00 | 0.00 | 60 B | 1.00 |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 32768 | 55,149,804.00 ns | 273,037.835 ns | 255,399.760 ns | 55,076,500.00 ns | 1.02 | 0.01 | 60 B | 1.00 |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 65536 | 218,431,663.33 ns | 4,265,864.273 ns | 4,912,575.640 ns | 214,920,550.00 ns | 1.00 | 0.00 | 200 B | 1.00 |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 65536 | 217,449,610.26 ns | 493,374.080 ns | 411,989.702 ns | 217,368,133.33 ns | 1.01 | 0.02 | 200 B | 1.00 |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 131072 | 936,350,615.38 ns | 2,290,617.990 ns | 1,912,769.764 ns | 936,031,100.00 ns | 1.00 | 0.00 | 600 B | 1.00 |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 131072 | 928,423,416.67 ns | 1,034,931.818 ns | 808,006.689 ns | 928,198,750.00 ns | 0.99 | 0.00 | 600 B | 1.00 |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 849999 | 99,588,892,028.57 ns | 998,348,445.797 ns | 885,009,893.256 ns | 99,218,977,100.00 ns | 1.00 | 0.00 | 600 B | 1.00 |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 849999 | 102,323,847,080.00 ns | 1,028,672,431.300 ns | 962,220,830.472 ns | 101,982,058,200.00 ns | 1.03 | 0.01 | 600 B | 1.00 |
|
||||
| | | | | | | | | | | | | |
|
||||
| Baseline | Index(...)Cases [25] | List | RecyclableLongList | 850000 | 113.8 s | 2.26 s | 2.69 s | | 1.00 | 0.00 | 600 B | 1.00 |
|
||||
| Actual | Index(...)Cases [25] | List | RecyclableLongList | 850000 | 110.3 s | 2.11 s | 2.07 s | | 0.97 | 0.03 | 399750776 B | 666,251.29 |
|
||||
```
|
||||
As `RecyclableLongList<T>` is foreseen for use-cases with big data, you should automatically benefit from it in many use-cases.
|
||||
|
||||
* `Insert`, `Remove` & `RemoveAt` methods raise `NotSupportedException`. This is by design in the current version, because it involves performance degradation. I'm planning works around this in the upcoming weeks. If you need that sooner, you're welcomed to file a PR with the proposed changes.
|
||||
|
||||
# Architecture Decisions
|
||||
The architecture of the classes included in this project is driven primarily by performance & memory footprints. The following decisions were made to support it.
|
||||
|
||||
## Code duplication
|
||||
There is some level of code duplication in each of the classes. When you look at them, you will see they are alike & some of the code could be extracted to extension methods. It has been done & proved to perform worse. For this reason, properties & methods expected to be on the hotpath duplicate some code.
|
||||
There is high level of code duplication in each of the classes. When you look at them, you will see they are alike & some of the code could be extracted to extension methods. It has been done & has proven to perform worse. For this reason, properties & methods expected to be on the hot-path, duplicate much of the code to get the most out of it.
|
||||
|
||||
## Common base `ILongList<T>`
|
||||
Having a common base class for all the classes looks very attractive. But it is also proved to perform worse, if you start making properties & methods virtual & there are subsequent calls to `base`. For this reason, each of the classes is the root class & there are hardly any virtual methods & properties. All classes are meant to be `final`, but I've not marked them as `sealed`. If you feel that you'd benefit from it, you're welcomed to inherit from them.
|
||||
## Common base - `ILongList<T>`
|
||||
Having a common base class for all the classes looks very attractive. But it is also proved to perform worse, if you start making properties & methods virtual & there are subsequent calls to `base`. For this reason, each of the classes is the root class & there are hardly any virtual methods & properties. All classes are meant to be `final`, but I've not marked them as `sealed`. If you feel that you'd benefit from it, you're welcomed to inherit from them. The protected stuff is there for your use.
|
||||
|
||||
If you desire to have a common base for calls, each of the classes implements `IList<T>` & `ILongList<T>` interfaces. `ILongList<T>` is a new interface, made alike `IList<T>`, but unlocking full support for `long` indexing & use. Considering the characteristics of the classes I feel that the generic name is justified, instead of `IRecyclableList<T>` what you could expect.
|
||||
If you desire to have a common base for calls, each of the classes implements `IList<T>` & `ILongList<T>` interfaces. `ILongList<T>` is a new interface, made alike `IList<T>`, but unlocking full support for `long` indexing & use. Considering the characteristics of the classes I feel that the generic name is justified, instead of `IRecyclableList<T>` what you could expect.
|
||||
|
||||
## Protection against inccorrect use & state
|
||||
## Protection against incorrect use & state
|
||||
Having protection against invalid state of the object is an important part of software engineering. The classes included in this project provide the same level of protection as the regular `array` & `List` class. They will raise exceptions when invalid index is given. But there is no additional protection that the content & state of the objects internally used by these classes are valid. You can break them, if you want to.
|
||||
|
||||
This decision is driven by performance measures. Considering wide use of the lists on the hot-paths, each additional check & operation starts counting. I've decided to eliminate all the checks for the sake of performance. If you feel that you need additional protection, feel free to inherit or reuse any of the provided classes to provide that.
|
||||
|
||||
## `this[int index]` & `Count` may overflow, but...
|
||||
All the classes included in this package support `long` indexing and addressing. The `LongCount` property will always provide the total no. of items stored on the list. Since `Count` is just `LongCount` typecasted to `int`, its value may overflow.
|
||||
## `this[int index]`, `Count`, `IndexOf(T item)` etc. may overflow, but...
|
||||
All the classes included in this package support `long` indexing and addressing. The `LongCount` property will always provide the total no. of items stored on the list. Since `Count` is just `LongCount` type-casted to `int`, its value may overflow.
|
||||
|
||||
But `List<T>` addressing is kind of currently limited to `int` ranges anyway in practical scenarios. As such `Count` overflowing shouldn't be an issue when using the classes with existing code. If you use `Count` in your loops, it's recommended to replace it with `LongCount`, tough. You don't need to make any changes in `foreach` or other iterator loops. They are expected to transparently use the new classes & yield all items, beyond `int.MaxValue` range.
|
||||
|
||||
@@ -37,20 +340,37 @@ This is by design, for the best performance. Initially there have been checks in
|
||||
|
||||
If you foresee the overflowing as an issue, you're welcomed to propose an improvement & file a PR with proposed solution.
|
||||
|
||||
# Auto-properties, new language features & code style
|
||||
When you look at the code you may note, that it looks like in early c# days. Where the new language features yield the same or better performance, they are & will be used.
|
||||
|
||||
However, many of the new language features have proved to provide worse performance at some point in my testing. It includes auto-properties, for which I could see getters & setters generated in IL, instead of fields. Because all the classes are expected to be on the hot-paths, I've eliminated most of them.
|
||||
|
||||
Similarly, you will find places in code with hints or warnings disabled. The decision was always driven by performance measures. The use case & business logic was carefully reviewed to ensure it's safe to take shortcuts, before making the change. If you run into failing scenario, please feel free to create a work item against it and/or file a PR to fix it.
|
||||
|
||||
Finally, you'll find places with code commented out. That's to easily track what has been tried & what proved to perform worse.
|
||||
|
||||
At any time, if you know & can show in your benchmarks that the code can be simplified without impacting the performance, feel free to file a PR with proposed changes. Please include benchmark results from your testing to speed up things.
|
||||
|
||||
# Thread Safety
|
||||
The `public static` members of the classes are thread safe. Any instance members are not guaraneteed to be thread safe. You need to implement locking mechanism to safely use the classes in a multi-threading environment.
|
||||
The `public static` members of the classes are thread safe. Any instance members are not guaranteed to be thread safe. You need to implement locking mechanism to safely use the classes in a multi-threading environment.
|
||||
|
||||
# Use
|
||||
All the classes included in this package are meant to be direct replacements of their corresponding system classes.
|
||||
* `List<T>` ⇨ `RecyclableList<T>`
|
||||
* `SortedList<T>` ⇨ `RecyclableSortedList<T>`
|
||||
* `IList<T>` ⇨ `ILongList<T>`
|
||||
* `List<T>` ⇨ `RecyclableLongList<T>`
|
||||
* `Queue<T>` ⇨ `RecyclableQueue<T>`
|
||||
* `PriorityQueue<T>` ⇨ `RecyclableSortedList<T>`
|
||||
* `SortedList<T>` ⇨ `RecyclableSortedList<T>`
|
||||
* `Stack<T>` ⇨ `RecyclableStack<T>`
|
||||
* `IList<T>` ⇨ `ILongList<T>`
|
||||
|
||||
Examples of each of the above classes are provided below. Please refer automated tests for more.
|
||||
|
||||
## `RecyclableList<T>`
|
||||
It's the closest equivalent to `List<T>` in regards to functionality. It supports items' removal & delivers the best performance in most scenarios. But it's limited to `int` range, tough. If you find that acceptable, my recommendation is to use this class as the direct replacement for `List<T>`.
|
||||
|
||||
Do note, that it implements `ILongList<T>` interface, too. This is to allow having common code base regardless, if you use the basic `RecyclableList<T>` or it's `long`-range version - `RecyclableLongList<T>`. By doing that you can switch between them with highly limited changes in code.
|
||||
|
||||
## `RecyclableLongList<T>`
|
||||
|
||||
```CSharp
|
||||
public void RecyclableListExample()
|
||||
@@ -58,7 +378,7 @@ public void RecyclableListExample()
|
||||
int[] testNumbers = new[] { 1, 2, 2, 3 };
|
||||
|
||||
// Create
|
||||
using var list = new RecyclableList<int>();
|
||||
using var list = new RecyclableLongList<int>();
|
||||
|
||||
// Add items
|
||||
foreach (var index in testNumbers)
|
||||
@@ -85,4 +405,10 @@ public void RecyclableListExample()
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
```
|
||||
```
|
||||
## `RecyclableList<T>`
|
||||
|
||||
## `RecyclableList<T>`
|
||||
|
||||
## `RecyclableList<T>`
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using Collections.Pooled;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks
|
||||
{
|
||||
public partial class RecyclableCollectionsBenchmarks
|
||||
{
|
||||
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,35 @@
|
||||
using Collections.Pooled;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks
|
||||
{
|
||||
public partial class RecyclableCollectionsBenchmarks
|
||||
{
|
||||
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>(capacity: TestObjectCount, 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, expectedItemsCount: TestObjectCount);
|
||||
list.AddRange(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Collections.Pooled;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks
|
||||
{
|
||||
public partial class RecyclableCollectionsBenchmarks
|
||||
{
|
||||
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>(capacity: TestObjectCount, 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, expectedItemsCount: TestObjectCount);
|
||||
list.AddRange(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Collections.Pooled;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks
|
||||
{
|
||||
public partial class RecyclableCollectionsBenchmarks
|
||||
{
|
||||
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>)TestObjectsAsPooledList;
|
||||
using var list = new PooledList<long>(capacity: TestObjectCount, 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, expectedItemsCount: TestObjectCount);
|
||||
list.AddRange(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Collections.Pooled;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks
|
||||
{
|
||||
public partial class RecyclableCollectionsBenchmarks
|
||||
{
|
||||
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>(capacity: TestObjectCount, 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, expectedItemsCount: TestObjectCount);
|
||||
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>();
|
||||
list.AddRange(data);
|
||||
}
|
||||
|
||||
public void RecyclableLongList_AddRangeWhenSourceIsRecyclableList()
|
||||
{
|
||||
var data = TestObjectsAsRecyclableList;
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, expectedItemsCount: TestObjectCount);
|
||||
list.AddRange(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Collections.Pooled;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks
|
||||
{
|
||||
public partial class RecyclableCollectionsBenchmarks
|
||||
{
|
||||
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, expectedItemsCount: 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, expectedItemsCount: dataCount);
|
||||
for (var i = 0; i < dataCount; i++)
|
||||
{
|
||||
list.Add(data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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,31 @@
|
||||
using Collections.Pooled;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks
|
||||
{
|
||||
public partial class RecyclableCollectionsBenchmarks
|
||||
{
|
||||
public static void List_Create()
|
||||
{
|
||||
var list = new List<long>();
|
||||
DoNothing(list);
|
||||
}
|
||||
|
||||
public static void PooledList_Create()
|
||||
{
|
||||
using var list = new PooledList<long>(ClearMode.Auto);
|
||||
DoNothing(list);
|
||||
}
|
||||
|
||||
public static void RecyclableList_Create()
|
||||
{
|
||||
using var list = new RecyclableList<long>();
|
||||
DoNothing(list);
|
||||
}
|
||||
|
||||
public static 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, expectedItemsCount: TestObjectCount);
|
||||
DoNothing(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,35 +17,35 @@ namespace Recyclable.Collections.Benchmarks
|
||||
public RecyclableCollectionsBenchmarks() : base()
|
||||
{
|
||||
TestObjectCount = 1;
|
||||
// BaselineBenchmarkType = RecyclableCollectionsBenchmarkSource.PooledList;
|
||||
BaselineBenchmarkType = RecyclableCollectionsBenchmarkSource.List;
|
||||
BenchmarkType = RecyclableCollectionsBenchmarkSource.RecyclableLongList;
|
||||
}
|
||||
|
||||
[Params
|
||||
(
|
||||
//RecyclableCollectionsBenchmarkType.Add_WithCapacity,
|
||||
//RecyclableCollectionsBenchmarkType.Add,
|
||||
//RecyclableCollectionsBenchmarkType.AddRange_WithCapacity,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsArray,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIEnumerable,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
|
||||
//RecyclableCollectionsBenchmarkType.Contains_FirstItems,
|
||||
//RecyclableCollectionsBenchmarkType.Contains_LastItems,
|
||||
//RecyclableCollectionsBenchmarkType.Count,
|
||||
//RecyclableCollectionsBenchmarkType.Create_WithCapacity,
|
||||
//RecyclableCollectionsBenchmarkType.Create,
|
||||
//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.Add_WithCapacity,
|
||||
RecyclableCollectionsBenchmarkType.Add,
|
||||
RecyclableCollectionsBenchmarkType.AddRange_WithCapacity,
|
||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsArray,
|
||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIEnumerable,
|
||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
|
||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
|
||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
|
||||
RecyclableCollectionsBenchmarkType.Contains_FirstItems,
|
||||
RecyclableCollectionsBenchmarkType.Contains_LastItems,
|
||||
RecyclableCollectionsBenchmarkType.Count,
|
||||
RecyclableCollectionsBenchmarkType.Create_WithCapacity,
|
||||
RecyclableCollectionsBenchmarkType.Create,
|
||||
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
|
||||
)]
|
||||
|
||||
public RecyclableCollectionsBenchmarkType TestCase { get; set; } = RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases;
|
||||
@@ -58,10 +58,10 @@ namespace Recyclable.Collections.Benchmarks
|
||||
|
||||
[Params
|
||||
(
|
||||
//RecyclableCollectionsBenchmarkSource.Array,
|
||||
//RecyclableCollectionsBenchmarkSource.List,
|
||||
//RecyclableCollectionsBenchmarkSource.PooledList
|
||||
//RecyclableCollectionsBenchmarkSource.RecyclableList,
|
||||
RecyclableCollectionsBenchmarkSource.Array,
|
||||
RecyclableCollectionsBenchmarkSource.List,
|
||||
RecyclableCollectionsBenchmarkSource.PooledList,
|
||||
RecyclableCollectionsBenchmarkSource.RecyclableList,
|
||||
RecyclableCollectionsBenchmarkSource.RecyclableLongList
|
||||
)]
|
||||
public override RecyclableCollectionsBenchmarkSource BenchmarkType { get => base.BenchmarkType; set => base.BenchmarkType = value; }
|
||||
|
||||
@@ -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(i);
|
||||
}
|
||||
}
|
||||
|
||||
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(i);
|
||||
}
|
||||
}
|
||||
|
||||
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(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void RecyclableLongList_RemoveAt_FirstItems()
|
||||
{
|
||||
var data = TestObjects;
|
||||
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, expectedItemsCount: TestObjectCount);
|
||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
||||
for (var i = 0; i < dataCount; i++)
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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, expectedItemsCount: 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, expectedItemsCount: 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, expectedItemsCount: 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using MiscUtil;
|
||||
using MiscUtil.Extensions;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks
|
||||
{
|
||||
public delegate T Compare<T>(T x, T y);
|
||||
|
||||
public readonly struct MyComparerReadOnlyStruct<T> : IComparer<T>
|
||||
{
|
||||
public int Compare(T? x, T? y) => Operator.Subtract(x, y) switch
|
||||
{
|
||||
> 0 => 1,
|
||||
< 0 => -1,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
public struct MyComparerStruct<T> : IComparer<T>
|
||||
{
|
||||
public int Compare(T? x, T? y) => Operator.Subtract(x, y) switch
|
||||
{
|
||||
> 0 => 1,
|
||||
< 0 => -1,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
public class MyComparer : IComparer<long>
|
||||
{
|
||||
public int Compare(long x, long y) => (x - y) switch
|
||||
{
|
||||
> 0 => 1,
|
||||
< 0 => -1,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
[MemoryDiagnoser]
|
||||
public class DelegateVsComparerBenchmark
|
||||
{
|
||||
private static readonly Comparer<long> _comparer = Comparer<long>.Default;
|
||||
|
||||
private const int _objectCount = 1_000_000;
|
||||
private static readonly IEnumerable<long> _testObjects = Enumerable.Range(1, _objectCount).Select(x => (long)x);
|
||||
private static readonly IEnumerable<Func<long, long, int>> _testListOfFunctions = _testObjects.Select<long, Func<long, long, int>>(static x => new MyComparer().Compare);
|
||||
private static readonly IEnumerable<IComparer<long>> _testListOfComparers = _testObjects.Select(x => (IComparer<long>)new MyComparer());
|
||||
private static readonly IEnumerable<Compare<long>> _testListOfDelegates = _testObjects.Select(x => new Compare<long>((x, y) => new MyComparer().Compare(x, y)));
|
||||
private static readonly IEnumerable<Func<long, long, int>> _testListOfLocalFunctions = GetLocalFunctions(_testObjects);
|
||||
private static readonly IEnumerable<MyComparer> _testListOfClasses = _testObjects.Select(x => new MyComparer());
|
||||
private static readonly IEnumerable<MyComparerStruct<long>> _testListOfStructs = _testObjects.Select(x => new MyComparerStruct<long>());
|
||||
private static readonly IEnumerable<IComparer<long>> _testListOfBoxedStructs = _testObjects.Select(x => (IComparer<long>)new MyComparerStruct<long>());
|
||||
private static IEnumerable<TComparer> _testListOfUnboxedStructs<TComparer, TValue>() where TComparer : IComparer<TValue>, new()
|
||||
{
|
||||
foreach (var item 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 item in testObjects)
|
||||
{
|
||||
yield return compareFunc;
|
||||
}
|
||||
}
|
||||
|
||||
private static 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]);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void Comparer()
|
||||
{
|
||||
TestWithComparer(_comparer);
|
||||
}
|
||||
|
||||
private static 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]);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Func()
|
||||
{
|
||||
TestWithFunc((x, y) => (x - y) switch
|
||||
{
|
||||
> 0 => 1,
|
||||
< 0 => -1,
|
||||
_ => 0
|
||||
});
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void StaticFunc()
|
||||
{
|
||||
TestWithFunc(static (x, y) => (x - y) switch
|
||||
{
|
||||
> 0 => 1,
|
||||
< 0 => -1,
|
||||
_ => 0
|
||||
});
|
||||
}
|
||||
|
||||
private static void TestWithDelegate<T>(Compare<long> compareFunc)
|
||||
{
|
||||
var testData = _testObjects.ToArray();
|
||||
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
|
||||
{
|
||||
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Delegate()
|
||||
{
|
||||
TestWithDelegate<long>((x, y) => (x - y) switch
|
||||
{
|
||||
> 0 => 1,
|
||||
< 0 => -1,
|
||||
_ => 0
|
||||
});
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
public class ListVsLinkedListBenchmark
|
||||
{
|
||||
private const int _objectCount = 1_000_000;
|
||||
private const int _blockSize = 100_000;
|
||||
private static readonly object[] _testObjects = Enumerable.Range(1, _objectCount).Select(x => new object()).ToArray();
|
||||
|
||||
private static void DoNothing<T>(T obj)
|
||||
{
|
||||
_ = obj;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Array()
|
||||
{
|
||||
var list = new object[_testObjects.LongLength];
|
||||
for (var i = 0; i < _testObjects.Length; i++)
|
||||
{
|
||||
list[i] = _testObjects[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < list.LongLength; i++)
|
||||
{
|
||||
DoNothing(list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void RecyclableList()
|
||||
{
|
||||
using var list = new RecyclableList<object>(_blockSize);
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Add(_testObjects[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
DoNothing(_testObjects[i]);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void List()
|
||||
{
|
||||
var list = new List<object>();
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Add(_testObjects[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
DoNothing(list[i]);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
//[Benchmark]
|
||||
public void LinkedList()
|
||||
{
|
||||
var list = new LinkedList<object>();
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
_ = list.AddLast(_testObjects[i]);
|
||||
}
|
||||
|
||||
foreach (var node in list)
|
||||
{
|
||||
DoNothing(node);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RecyclableQueue()
|
||||
{
|
||||
var list = new RecyclableQueue<object>(_blockSize);
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Enqueue(_testObjects[i]);
|
||||
}
|
||||
|
||||
foreach (var node in list)
|
||||
{
|
||||
DoNothing(node);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
//[Benchmark]
|
||||
public void Queue()
|
||||
{
|
||||
var list = new Queue<object>();
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Enqueue(_testObjects[i]);
|
||||
}
|
||||
|
||||
foreach (var node in list)
|
||||
{
|
||||
DoNothing(node);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
//[Benchmark]
|
||||
public void PriorityQueue()
|
||||
{
|
||||
var list = new PriorityQueue<object, long>();
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Enqueue(_testObjects[i], i);
|
||||
}
|
||||
|
||||
while (list.Count > 0)
|
||||
{
|
||||
var node = list.Dequeue();
|
||||
DoNothing(node);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
//[Benchmark]
|
||||
public void RecyclableSortedList_InUpdateMode()
|
||||
{
|
||||
var list = new RecyclableSortedList<long, object>(_blockSize);
|
||||
list.BeginUpdate();
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Add(i, _testObjects[i]);
|
||||
}
|
||||
|
||||
list.EndUpdate();
|
||||
|
||||
for (var i = 0; i < list.LongCount; i++)
|
||||
{
|
||||
DoNothing(_testObjects[i]);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
//[Benchmark]
|
||||
public void RecyclableSortedList()
|
||||
{
|
||||
var list = new RecyclableSortedList<long, object>(_blockSize);
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Add(i, _testObjects[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < list.LongCount; i++)
|
||||
{
|
||||
DoNothing(_testObjects[i]);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
//[Benchmark]
|
||||
public void SortedList()
|
||||
{
|
||||
var list = new SortedList<long, object>();
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Add(i, _testObjects[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
DoNothing(_testObjects[i]);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
//[Benchmark]
|
||||
public void RecyclableStack()
|
||||
{
|
||||
var list = new RecyclableStack<object>(_blockSize);
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Push(_testObjects[i]);
|
||||
}
|
||||
|
||||
foreach (var node in list)
|
||||
{
|
||||
DoNothing(node);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
//[Benchmark]
|
||||
public void Stack()
|
||||
{
|
||||
var list = new Stack<object>();
|
||||
for (var i = 0; i < _testObjects.LongLength; i++)
|
||||
{
|
||||
list.Push(_testObjects[i]);
|
||||
}
|
||||
|
||||
foreach (var node in list)
|
||||
{
|
||||
DoNothing(node);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 BenchmarkType { get => base.BenchmarkType; set => base.BenchmarkType = 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,357 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using MiscUtil;
|
||||
|
||||
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 BenchmarkType { get => base.BenchmarkType; set => base.BenchmarkType = value; }
|
||||
|
||||
[Params(DelegateVsComparerPocBenchmarkType.Comparer)]
|
||||
public override DelegateVsComparerPocBenchmarkType BaselineBenchmarkType { get => base.BaselineBenchmarkType; set => base.BaselineBenchmarkType = 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) => Operator.Subtract(x, y) switch
|
||||
{
|
||||
> 0 => 1,
|
||||
< 0 => -1,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
private struct MyComparerStruct<T> : IComparer<T>
|
||||
{
|
||||
public int Compare(T? x, T? y) => Operator.Subtract(x, y) switch
|
||||
{
|
||||
> 0 => 1,
|
||||
< 0 => -1,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
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<T>(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<long>((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,374 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
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 = 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 CalculateWithSubractWithWhileWithSse2(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 CalculateWithMixedApproachWithSubractWithWhile(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--)
|
||||
{
|
||||
CalculateWithSubractWithWhileWithSse2(i);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = false)]
|
||||
public void MixedApproachWithSubtractWithWhile()
|
||||
{
|
||||
long testNumber = TestNumber;
|
||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
||||
{
|
||||
CalculateWithMixedApproachWithSubractWithWhile(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,121 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks.POC
|
||||
{
|
||||
public abstract class PocBenchmarkBase<TBenchmarkType>
|
||||
{
|
||||
public virtual TBenchmarkType? BenchmarkType { get; set; }
|
||||
public virtual TBenchmarkType? BaselineBenchmarkType { get; set; }
|
||||
|
||||
private Action? _testMethod;
|
||||
protected Action TestMethod
|
||||
{
|
||||
get => _testMethod ?? throw new OperationCanceledException("Skip - Custom benchmark run in progress");
|
||||
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(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)]
|
||||
// [Params(RecyclableDefaults.MaxPooledBlockSize)]
|
||||
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 10_000_000, 33_554_432, 50_000_000, 100_000_000)]
|
||||
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization - 1)]
|
||||
//[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, 32, 40, 50, 60, 80, 90, 100, 128)]
|
||||
// [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)]
|
||||
// [Params(1_048_576)]
|
||||
public virtual int TestObjectCount { get; set; } = 1; //33_554_432; //524_288; //131_072;
|
||||
public virtual int TestObjectCountForSlowMethods => (TestObjectCount / 2) + 1;// Math.Min(TestObjectCount, 131_072);
|
||||
|
||||
//[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 => TestObjectCount switch
|
||||
{
|
||||
0 => 1,
|
||||
> 0 and <= 10_240 => TestObjectCount,
|
||||
_ => TestObjectCount / Divider > 0 ? TestObjectCount / Divider : TestObjectCount
|
||||
};
|
||||
|
||||
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 {{{BenchmarkType}}} *******");
|
||||
|
||||
if (!EqualityComparer<TBenchmarkType>.Default.Equals(BaselineBenchmarkType, default))
|
||||
{
|
||||
Console.WriteLine("******* SETTING UP BASELINE DATA *******");
|
||||
PrepareData(BaselineBenchmarkType);
|
||||
_baselineMethod = GetTestMethod(BaselineBenchmarkType);
|
||||
}
|
||||
|
||||
//~ 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(BaselineBenchmarkType, BenchmarkType))
|
||||
{
|
||||
Console.WriteLine("******* SETTING UP TEST CASE DATA *******");
|
||||
//~ If BaselineBenchmarkType == null, then we'll come here only if BaselineBenchmarkType != null.
|
||||
PrepareData(BenchmarkType!);
|
||||
_testMethod = GetTestMethod(BenchmarkType);
|
||||
}
|
||||
|
||||
Console.WriteLine("******* DATA PREPARED *******");
|
||||
}
|
||||
}
|
||||
}
|
||||
+2858
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,157 @@
|
||||
using System.Reflection;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Collections.Pooled;
|
||||
using Recyclable.Collections.Benchmarks.Core;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks.POC
|
||||
{
|
||||
public enum RecyclableLongListPocBenchmarkType
|
||||
{
|
||||
IndexOfV1,
|
||||
IndexOfV2,
|
||||
IndexOfV3,
|
||||
IndexOfV4,
|
||||
IndexOf_PooledList,
|
||||
IndexOf,
|
||||
EnsureCapacity_ByPowOf2,
|
||||
EnsureCapacityV1_ByPowOf2,
|
||||
EnsureCapacityV2_ByPowOf2,
|
||||
EnsureCapacityV3_ByPowOf2,
|
||||
}
|
||||
|
||||
[MemoryDiagnoser]
|
||||
public partial class RecyclableLongListPocBenchmarks : PocBenchmarkBase<RecyclableLongListPocBenchmarkType>
|
||||
{
|
||||
[Params(RecyclableLongListPocBenchmarkType.IndexOf)]
|
||||
public override RecyclableLongListPocBenchmarkType BenchmarkType { get => base.BenchmarkType; set => base.BenchmarkType = value; }
|
||||
|
||||
// [Params(BenchmarkType.Array, BenchmarkType.List, BenchmarkType.RecyclableList, BenchmarkType.RecyclableLongList)]
|
||||
[Params(RecyclableLongListPocBenchmarkType.IndexOf_PooledList)]
|
||||
public override RecyclableLongListPocBenchmarkType BaselineBenchmarkType { get => base.BaselineBenchmarkType; set => base.BaselineBenchmarkType = value; }
|
||||
|
||||
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 RecyclableLongListIndexOfV1<long>? _testRecyclableLongListIndexOfV1;
|
||||
internal RecyclableLongListIndexOfV1<long> TestObjectsAsRecyclableLongListIndexOfV1 => _testRecyclableLongListIndexOfV1 ?? throw new NullReferenceException($"Something is wrong and {nameof(RecyclableLongListIndexOfV1<long>)} is null");
|
||||
private RecyclableLongListIndexOfV2<long>? _testRecyclableLongListIndexOfV2;
|
||||
private RecyclableLongList<long>? _testRecyclableLongList;
|
||||
internal RecyclableLongListIndexOfV2<long> TestObjectsAsRecyclableLongListIndexOfV2 => _testRecyclableLongListIndexOfV2 ?? throw new NullReferenceException($"Something is wrong and {nameof(RecyclableLongListIndexOfV2<long>)} is null");
|
||||
private RecyclableLongListIndexOfV3<long>? _testRecyclableLongListIndexOfV3;
|
||||
internal RecyclableLongListIndexOfV3<long> TestObjectsAsRecyclableLongListIndexOfV3 => _testRecyclableLongListIndexOfV3 ?? throw new NullReferenceException($"Something is wrong and {nameof(RecyclableLongListIndexOfV3<long>)} is null");
|
||||
private RecyclableLongListIndexOfV4<long>? _testRecyclableLongListIndexOfV4;
|
||||
internal RecyclableLongListIndexOfV4<long> TestObjectsAsRecyclableLongListIndexOfV4 => _testRecyclableLongListIndexOfV4 ?? throw new NullReferenceException($"Something is wrong and {nameof(RecyclableLongListIndexOfV4<long>)} is null");
|
||||
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;
|
||||
private static readonly MethodInfo _ensureCapacityV1Func;
|
||||
private static readonly MethodInfo _ensureCapacityV2Func;
|
||||
private static readonly MethodInfo _ensureCapacityV3Func;
|
||||
|
||||
static RecyclableLongListPocBenchmarks()
|
||||
{
|
||||
_ensureCapacityNewFunc = typeof(RecyclableLongList<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic)
|
||||
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongList<long>)}");
|
||||
|
||||
_ensureCapacityV1Func = typeof(RecyclableLongListV1<long>).GetMethod("EnsureCapacity", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongListV1<long>)}");
|
||||
|
||||
_ensureCapacityV2Func = typeof(RecyclableLongListV2<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.Public)
|
||||
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongListV2<long>)}");
|
||||
|
||||
_ensureCapacityV3Func = typeof(RecyclableLongListV3<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic)
|
||||
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongListV3<long>)}");
|
||||
}
|
||||
|
||||
public static void Run()
|
||||
{
|
||||
var benchmark = new RecyclableLongListPocBenchmarks();
|
||||
benchmark.Setup();
|
||||
// benchmark.RecyclableLongList_EnsureCapacity_ByPowOf2();
|
||||
// benchmark.RecyclableLongList_EnsureCapacityV1_ByPowOf2();
|
||||
// benchmark.RecyclableLongList_EnsureCapacityV2_ByPowOf2();
|
||||
// benchmark.RecyclableLongList_EnsureCapacityV3_ByPowOf2();
|
||||
// benchmark.RecyclableLongList_EnsureCapacity_ByBlockSize();
|
||||
// benchmark.RecyclableLongList_EnsureCapacityV1_ByBlockSize();
|
||||
// benchmark.RecyclableLongList_EnsureCapacityV2_ByBlockSize();
|
||||
// benchmark.RecyclableLongList_EnsureCapacityV3_ByBlockSize();
|
||||
benchmark.RecyclableLongList_IndexOf();
|
||||
benchmark.RecyclableLongList_IndexOfV1();
|
||||
benchmark.RecyclableLongList_IndexOfV2();
|
||||
benchmark.RecyclableLongList_IndexOfV3();
|
||||
benchmark.RecyclableLongList_IndexOfV4();
|
||||
benchmark.Cleanup();
|
||||
}
|
||||
|
||||
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;
|
||||
_testRecyclableLongListIndexOfV1?.Dispose();
|
||||
_testRecyclableLongListIndexOfV1 = null;
|
||||
_testRecyclableLongListIndexOfV2?.Dispose();
|
||||
_testRecyclableLongListIndexOfV2 = null;
|
||||
_testRecyclableLongListIndexOfV3?.Dispose();
|
||||
_testRecyclableLongListIndexOfV3 = null;
|
||||
_testRecyclableLongListIndexOfV4?.Dispose();
|
||||
_testRecyclableLongListIndexOfV4 = null;
|
||||
_testPooledList?.Dispose();
|
||||
_testPooledList = null;
|
||||
|
||||
base.Cleanup();
|
||||
}
|
||||
|
||||
protected override Action? GetTestMethod(RecyclableLongListPocBenchmarkType benchmarkType) => benchmarkType switch
|
||||
{
|
||||
RecyclableLongListPocBenchmarkType.IndexOfV1 => () => RecyclableLongList_IndexOfV1(),
|
||||
RecyclableLongListPocBenchmarkType.IndexOfV2 => () => RecyclableLongList_IndexOfV2(),
|
||||
RecyclableLongListPocBenchmarkType.IndexOfV3 => () => RecyclableLongList_IndexOfV3(),
|
||||
RecyclableLongListPocBenchmarkType.IndexOfV4 => () => RecyclableLongList_IndexOfV4(),
|
||||
RecyclableLongListPocBenchmarkType.IndexOf_PooledList => () => PooledList_IndexOf(),
|
||||
RecyclableLongListPocBenchmarkType.IndexOf => () => RecyclableLongList_IndexOf(),
|
||||
RecyclableLongListPocBenchmarkType.EnsureCapacity_ByPowOf2 => () => RecyclableLongList_EnsureCapacity_ByPowOf2(),
|
||||
RecyclableLongListPocBenchmarkType.EnsureCapacityV1_ByPowOf2 => () => RecyclableLongList_EnsureCapacityV1_ByPowOf2(),
|
||||
RecyclableLongListPocBenchmarkType.EnsureCapacityV2_ByPowOf2 => () => RecyclableLongList_EnsureCapacityV2_ByPowOf2(),
|
||||
RecyclableLongListPocBenchmarkType.EnsureCapacityV3_ByPowOf2 => () => RecyclableLongList_EnsureCapacityV3_ByPowOf2(),
|
||||
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
||||
};
|
||||
|
||||
protected override void PrepareData<T>(T benchmarkType)
|
||||
{
|
||||
base.PrepareData(benchmarkType);
|
||||
|
||||
switch (BenchmarkType)
|
||||
{
|
||||
case RecyclableLongListPocBenchmarkType.IndexOfV1:
|
||||
_testRecyclableLongListIndexOfV1 = new(TestObjects, BlockSize, expectedItemsCount: TestObjectCount);
|
||||
break;
|
||||
case RecyclableLongListPocBenchmarkType.IndexOfV2:
|
||||
_testRecyclableLongListIndexOfV2 = new(TestObjects, BlockSize, expectedItemsCount: TestObjectCount);
|
||||
break;
|
||||
case RecyclableLongListPocBenchmarkType.IndexOfV3:
|
||||
_testRecyclableLongListIndexOfV3 = new(TestObjects, BlockSize, expectedItemsCount: TestObjectCount);
|
||||
break;
|
||||
case RecyclableLongListPocBenchmarkType.IndexOfV4:
|
||||
_testRecyclableLongListIndexOfV4 = new(TestObjects, BlockSize, expectedItemsCount: TestObjectCount);
|
||||
break;
|
||||
case RecyclableLongListPocBenchmarkType.IndexOf_PooledList:
|
||||
_testPooledList = new PooledList<long>(TestObjects, ClearMode.Auto);
|
||||
break;
|
||||
case RecyclableLongListPocBenchmarkType.IndexOf:
|
||||
_testRecyclableLongList = new(TestObjects, BlockSize, expectedItemsCount: TestObjectCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,362 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
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 BaselineBenchmarkType { get => base.BaselineBenchmarkType; set => base.BaselineBenchmarkType = value; }
|
||||
|
||||
[Params(RoundBenchmarkType.MathRound, RoundBenchmarkType.WithTypecasting)]
|
||||
public override RoundBenchmarkType BenchmarkType { get => base.BenchmarkType; set => base.BenchmarkType = 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,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 BenchmarkType { get => base.BenchmarkType; set => base.BenchmarkType = value; }
|
||||
|
||||
[Params(SpanVsArrayBenchmarkType.ArrayGetWithField)]
|
||||
// [Params(SpanVsArrayBenchmarkType.ArraySetWithField)]
|
||||
public override SpanVsArrayBenchmarkType BaselineBenchmarkType { get => base.BaselineBenchmarkType; set => base.BaselineBenchmarkType = 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,346 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using MoreLinq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections.Benchmarks.POC
|
||||
{
|
||||
public enum WhenParallelSearchBenchmarkType
|
||||
{
|
||||
IndexOfSequentially,
|
||||
IndexOfParallel,
|
||||
}
|
||||
|
||||
[MemoryDiagnoser]
|
||||
public class WhenParallelSearchBenchmarks : PocBenchmarkBase<WhenParallelSearchBenchmarkType>
|
||||
{
|
||||
#region ItemRange V2
|
||||
|
||||
private sealed class ItemRangeV2
|
||||
{
|
||||
public int BlockIndex;
|
||||
public long ItemsToSearchCount;
|
||||
internal bool _returned;
|
||||
|
||||
public ItemRangeV2()
|
||||
{
|
||||
}
|
||||
|
||||
public ItemRangeV2(int blockIndex, long itemsToSearchCount)
|
||||
{
|
||||
BlockIndex = blockIndex;
|
||||
ItemsToSearchCount = itemsToSearchCount;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_returned)
|
||||
{
|
||||
_returned = true;
|
||||
ItemRangePoolV2.Shared.Return(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ItemRangePoolV2
|
||||
|
||||
private static class ItemRangePoolV2
|
||||
{
|
||||
private static readonly SpinLock _updateLock = new(false);
|
||||
public static readonly ObjectPool<ItemRangeV2> Shared = new DefaultObjectPool<ItemRangeV2>(new DefaultPooledObjectPolicy<ItemRangeV2>());
|
||||
|
||||
public static ItemRangeV2 Create(int blockIndex, long itemsToSearchCount)
|
||||
{
|
||||
bool lockTaken = false;
|
||||
_updateLock.Enter(ref lockTaken);
|
||||
var itemRange = Shared.Get();
|
||||
if (lockTaken)
|
||||
{
|
||||
_updateLock.Exit(false);
|
||||
}
|
||||
|
||||
itemRange._returned = false;
|
||||
itemRange.BlockIndex = blockIndex;
|
||||
itemRange.ItemsToSearchCount = itemsToSearchCount;
|
||||
return itemRange;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32)]
|
||||
//public int NumberOfThreads { get; set; } = 3;
|
||||
|
||||
//[Params(10_000, 20_000, 35_000, 50_000, 75_000, 100_000, 125_000, 150_000, 175_000, 200_000, 225_000, 250_000, 275_000, 300_000, 325_000, 350_000, 375_000, 400_000, 500_000)]
|
||||
//[Params(320_000, 321_000, 322_000, 323_000, 324_000, 325_000, 326_000, 327_000, 328_000, 329_000, 330_000)]
|
||||
//[Params(329_000, 512_000, 750_000, 1_000_000, 3_290_000)]
|
||||
//public int Step { get; set; } = 329_000; // This was the fastest so far
|
||||
|
||||
private RecyclableLongList<long>? _testData;
|
||||
|
||||
public object? ItemToFind { get; set; }
|
||||
|
||||
[Params(WhenParallelSearchBenchmarkType.IndexOfParallel)]
|
||||
public override WhenParallelSearchBenchmarkType BenchmarkType { get => base.BenchmarkType; set => base.BenchmarkType = value; }
|
||||
|
||||
[Params(WhenParallelSearchBenchmarkType.IndexOfSequentially)]
|
||||
public override WhenParallelSearchBenchmarkType BaselineBenchmarkType { get => base.BaselineBenchmarkType; set => base.BaselineBenchmarkType = value; }
|
||||
|
||||
private long DoLongIndexOfSequentially()
|
||||
{
|
||||
// This won't work for TestObjectCount == 0
|
||||
int index;
|
||||
int blockSize = _testData!.BlockSize;
|
||||
int lastBlockWithData = _testData.LastBlockWithData;
|
||||
var memoryBlocks = _testData.AsArray;
|
||||
var itemToFind = ItemToFind;
|
||||
int blockIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
index = Array.IndexOf(memoryBlocks[blockIndex], itemToFind, 0, blockSize);
|
||||
if (index >= 0)
|
||||
{
|
||||
return (blockIndex << _testData.BlockSizePow2BitShift) + index;
|
||||
}
|
||||
|
||||
if (blockIndex + 1 < lastBlockWithData)
|
||||
{
|
||||
blockIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
index = lastBlockWithData == _testData.NextItemBlockIndex
|
||||
? Array.IndexOf(memoryBlocks[lastBlockWithData], itemToFind, 0, _testData.NextItemIndex)
|
||||
: Array.IndexOf(memoryBlocks[lastBlockWithData], itemToFind, 0, blockSize);
|
||||
|
||||
return index >= 0 ? (lastBlockWithData << _testData.BlockSizePow2BitShift) + index : -1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private static long DoLongIndexOfParallel(RecyclableLongList<long> list, in long itemToFind, ItemRangeV2 searchInfo, ManualResetEventSlim itemFoundSignal)
|
||||
{
|
||||
int index;
|
||||
int blockSize = list.BlockSize;
|
||||
int blockIndex = searchInfo.BlockIndex;
|
||||
long itemsToSearchCount = searchInfo.ItemsToSearchCount;
|
||||
long[][] memoryBlocks = list.AsArray;
|
||||
index = Array.IndexOf(memoryBlocks[searchInfo.BlockIndex], itemToFind, 0, blockSize);
|
||||
if (itemFoundSignal.IsSet)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
return (blockIndex << list.BlockSizePow2BitShift) + index;
|
||||
}
|
||||
|
||||
itemsToSearchCount -= blockSize;
|
||||
if (itemsToSearchCount < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lastBlockWithData = blockIndex + (int)(itemsToSearchCount >> list.BlockSizePow2BitShift) + ((itemsToSearchCount & (list.BlockSize - 1)) > 0 ? 1 : 0);
|
||||
blockIndex++;
|
||||
while (true)
|
||||
{
|
||||
index = Array.IndexOf(memoryBlocks[blockIndex], itemToFind, 0, blockSize);
|
||||
if (itemFoundSignal.IsSet)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
return ((long)blockIndex << list.BlockSizePow2BitShift) + index;
|
||||
}
|
||||
|
||||
itemsToSearchCount -= blockSize;
|
||||
if (blockIndex + 1 < lastBlockWithData && itemsToSearchCount < 0)
|
||||
{
|
||||
blockIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
index = itemsToSearchCount < 0 || itemFoundSignal.IsSet || lastBlockWithData > list.NextItemBlockIndex
|
||||
? -1
|
||||
: lastBlockWithData == list.NextItemBlockIndex
|
||||
? Array.IndexOf(memoryBlocks[lastBlockWithData], itemToFind, 0, checked((int)Math.Min(list.NextItemIndex, itemsToSearchCount)))
|
||||
: Array.IndexOf(memoryBlocks[lastBlockWithData], itemToFind, 0, checked((int)Math.Min(blockSize, itemsToSearchCount)));
|
||||
|
||||
return itemFoundSignal.IsSet || index < 0 ? -1 : ((long)lastBlockWithData << list.BlockSizePow2BitShift) + index;
|
||||
}
|
||||
|
||||
private long DoIndexOfParallel()
|
||||
{
|
||||
using var itemFoundSignal = new ManualResetEventSlim(false);
|
||||
using var allDoneSignal = new Barrier(1);
|
||||
//long step = Step;// TestObjectCount / NumberOfThreads;
|
||||
int blockSize = _testData!.BlockSize;
|
||||
//object[][] memoryBlocks = _testData.AsArray();
|
||||
//long itemsRemaining = testObjectCount;
|
||||
var foundItemIndex = -1L;
|
||||
var itemToFind = ItemToFind;
|
||||
//ItemRangesIterator.Iterate(0, blockSize, BlockSizePow2BitShift, TestObjectCount, (long)(TestObjectCount * 0.329), (searchInfo) =>
|
||||
//{
|
||||
// _ = allDoneSignal.AddParticipant();
|
||||
// _ = Task.Factory.StartNew((state) =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// var index = DoLongIndexOfParallel(_testData, itemToFind, searchInfo, itemFoundSignal);
|
||||
// if (index >= 0)
|
||||
// {
|
||||
// itemFoundSignal.Set();
|
||||
// lock (itemFoundSignal)
|
||||
// {
|
||||
// foundItemIndex = index;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// lock (allDoneSignal)
|
||||
// {
|
||||
// allDoneSignal.RemoveParticipant();
|
||||
// }
|
||||
// }
|
||||
// }, state: searchInfo);
|
||||
|
||||
// return !itemFoundSignal.IsSet;
|
||||
//});
|
||||
|
||||
//while (true)
|
||||
//{
|
||||
// var toGive = Math.Min(step, itemsRemaining);
|
||||
// SearchInfo<object> searchInfo = new(
|
||||
// blockIndex: blockIndex,
|
||||
// startingItemIndex: itemIndex,
|
||||
// itemsToSearchCount: toGive,
|
||||
// itemToFind: ItemToFind,
|
||||
// itemFoundSignal: itemFoundSignal);
|
||||
|
||||
// _ = Task.Factory.StartNew((state) =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// var index = DoLongIndexOfParallel(_testData, (SearchInfo<object>)state!);
|
||||
// if (index >= 0)
|
||||
// {
|
||||
// itemFoundSignal.Set();
|
||||
// lock (itemFoundSignal)
|
||||
// {
|
||||
// foundItemIndex = index;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// lock (allDoneSignal)
|
||||
// {
|
||||
// allDoneSignal.RemoveParticipant();
|
||||
// }
|
||||
// }
|
||||
// }, state: searchInfo);
|
||||
|
||||
// //itemIndex = checked((int)((itemIndex + toGive) >> BlockSizePow2BitShift)) - 1;
|
||||
// while (toGive > 0)
|
||||
// {
|
||||
// if (itemIndex + toGive < blockSize)
|
||||
// {
|
||||
// itemIndex += (int)toGive;
|
||||
// break;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// blockIndex++;
|
||||
// //itemIndex += blockSize;
|
||||
// //if (itemIndex > blockSize)
|
||||
// //{
|
||||
// // itemIndex
|
||||
// //}
|
||||
// //if (itemIndex + blockSize < )
|
||||
// //itemIndex = (int)(toGive - blockSize + itemIndex);
|
||||
// toGive -= blockSize;
|
||||
// }
|
||||
|
||||
// //if (blockIndex == _testData.LastBlockWithData)
|
||||
// //{
|
||||
// // itemIndex = checked((int)toGive);
|
||||
// // break;
|
||||
// //}
|
||||
|
||||
// }
|
||||
|
||||
// if (threadIndex + 1 < numberOfRanges && blockIndex <= _testData.LastBlockWithData)
|
||||
// {
|
||||
// threadIndex++;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
allDoneSignal.SignalAndWait();
|
||||
throw new NotImplementedException();
|
||||
return foundItemIndex;
|
||||
}
|
||||
|
||||
protected override void PrepareData<T>(T benchmarkType)
|
||||
{
|
||||
base.PrepareData(benchmarkType);
|
||||
|
||||
_testData ??= Enumerable.Range(1, TestObjectCount).Cast<long>().ToRecyclableLongList(BlockSize);
|
||||
ItemToFind ??= _testData.Last();
|
||||
}
|
||||
|
||||
protected override Action? GetTestMethod(WhenParallelSearchBenchmarkType benchmarkType) => BenchmarkType switch
|
||||
{
|
||||
WhenParallelSearchBenchmarkType.IndexOfSequentially => IndexOfSequentially,
|
||||
WhenParallelSearchBenchmarkType.IndexOfParallel => IndexOfParallel,
|
||||
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
||||
};
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
_testData = default;
|
||||
base.Cleanup();
|
||||
}
|
||||
|
||||
public void IndexOfSequentially()
|
||||
{
|
||||
long index = DoLongIndexOfSequentially();
|
||||
if (index >= 0)
|
||||
{
|
||||
DoNothing(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Item not found");
|
||||
}
|
||||
}
|
||||
|
||||
public void IndexOfParallel()
|
||||
{
|
||||
var index = DoIndexOfParallel();
|
||||
if (index >= 0)
|
||||
{
|
||||
DoNothing(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Item not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
using BenchmarkDotNet.Running;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Running;
|
||||
using Recyclable.Collections.Benchmarks;
|
||||
using Recyclable.Collections.Benchmarks.POC;
|
||||
|
||||
BenchmarkRunner.Run<ListVsLinkedListBenchmark>();
|
||||
//BenchmarkRunner.Run<DelegateVsComparerBenchmark>();
|
||||
public class Program
|
||||
{
|
||||
static IConfig BenchmarkConfig { get; } = ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary);
|
||||
|
||||
static void RunAssemblyBenchmarks()
|
||||
{
|
||||
@@ -19,40 +22,59 @@ BenchmarkRunner.Run<ListVsLinkedListBenchmark>();
|
||||
// ****************
|
||||
// *** Template ***
|
||||
// ****************
|
||||
var benchmark = new RecyclableCollectionsBenchmarks();
|
||||
benchmark.Setup();
|
||||
benchmark.RecyclableLongList_IndexOf_BestAndWorstCases();
|
||||
benchmark.Cleanup();
|
||||
//var benchmark = new RecyclableCollectionsBenchmarks();
|
||||
//benchmark.Setup();
|
||||
|
||||
//foreach (var _ in Enumerable.Range(1, 1000))
|
||||
//{
|
||||
static void RunAssemblyBenchmarks()
|
||||
{
|
||||
_ = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(new[] { "-f*" });
|
||||
}
|
||||
|
||||
static void RunRecyclableCollectionsBenchmarks()
|
||||
{
|
||||
_ = BenchmarkRunner.Run<RecyclableCollectionsBenchmarks>(BenchmarkConfig);
|
||||
}
|
||||
|
||||
static void RunSelectedBenchmarks()
|
||||
{
|
||||
// ****************
|
||||
// *** Template ***
|
||||
// ****************
|
||||
var benchmark = new RecyclableCollectionsBenchmarks();
|
||||
benchmark.Setup();
|
||||
benchmark.RecyclableLongList_IndexOf_BestAndWorstCases();
|
||||
benchmark.Cleanup();
|
||||
|
||||
//foreach (var _ in Enumerable.Range(1, 1000))
|
||||
//{
|
||||
// benchmark.RecyclableLongList_Remove_FirstItems();
|
||||
// benchmark.RecyclableLongList_RemoveAt_LastItems();
|
||||
// benchmark.List_RemoveAt_LastItems();
|
||||
//}
|
||||
//}
|
||||
|
||||
//benchmark.Cleanup();
|
||||
}
|
||||
//benchmark.Cleanup();
|
||||
}
|
||||
|
||||
static void RunPocBenchmarks()
|
||||
{
|
||||
//_ = BenchmarkRunner.Run<BoolOrComparePocBenchmarks>(BenchmarkConfig);
|
||||
//_ = BenchmarkRunner.Run<DelegateVsComparerPocBenchmarks>(BenchmarkConfig);
|
||||
//_ = BenchmarkRunner.Run<ModuloPocBenchmarks>(BenchmarkConfig);
|
||||
// _ = BenchmarkRunner.Run<RecyclableLongListPocBenchmarks>(BenchmarkConfig);
|
||||
_ = BenchmarkRunner.Run<TaskRunVsTaskFactoryStartNewBenchmarks>(BenchmarkConfig);
|
||||
//_ = BenchmarkRunner.Run<RefVsInstanceMemberPocBenchmarks>(BenchmarkConfig);
|
||||
//_ = BenchmarkRunner.Run<WhenParallelSearchBenchmarks>(BenchmarkConfig);
|
||||
//_ = BenchmarkRunner.Run<RoundBenchmarks>(BenchmarkConfig);
|
||||
// _ = BenchmarkRunner.Run<SpanVsArrayBenchmarks>(BenchmarkConfig);
|
||||
// ArraySizeLimitPocBenchmarks>(BenchmarkConfig);
|
||||
}
|
||||
static void RunPocBenchmarks()
|
||||
{
|
||||
_ = BenchmarkRunner.Run<BoolOrComparePocBenchmarks>(BenchmarkConfig);
|
||||
_ = BenchmarkRunner.Run<DelegateVsComparerPocBenchmarks>(BenchmarkConfig);
|
||||
_ = BenchmarkRunner.Run<ModuloPocBenchmarks>(BenchmarkConfig);
|
||||
_ = BenchmarkRunner.Run<RecyclableLongListPocBenchmarks>(BenchmarkConfig);
|
||||
_ = BenchmarkRunner.Run<TaskRunVsTaskFactoryStartNewBenchmarks>(BenchmarkConfig);
|
||||
_ = BenchmarkRunner.Run<RefVsInstanceMemberPocBenchmarks>(BenchmarkConfig);
|
||||
_ = BenchmarkRunner.Run<WhenParallelSearchBenchmarks>(BenchmarkConfig);
|
||||
_ = BenchmarkRunner.Run<RoundBenchmarks>(BenchmarkConfig);
|
||||
_ = BenchmarkRunner.Run<SpanVsArrayBenchmarks>(BenchmarkConfig);
|
||||
// ArraySizeLimitPocBenchmarks>(BenchmarkConfig);
|
||||
}
|
||||
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
RunRecyclableCollectionsBenchmarks();
|
||||
// RunPocBenchmarks();
|
||||
// RunSelectedBenchmarks();
|
||||
// RunAssemblyBenchmarks();
|
||||
static void Main(string[] args)
|
||||
{
|
||||
RunRecyclableCollectionsBenchmarks();
|
||||
// RunPocBenchmarks();
|
||||
// RunSelectedBenchmarks();
|
||||
// RunAssemblyBenchmarks();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"profiles": {
|
||||
"WSL": {
|
||||
"commandName": "WSL2",
|
||||
"distributionName": ""
|
||||
},
|
||||
".Net": {
|
||||
"commandName": "Project",
|
||||
"nativeDebugging": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,12 @@
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.3" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
|
||||
<PackageReference Include="Collections.Pooled" Version="1.0.82" />
|
||||
<PackageReference Include="JonSkeet.MiscUtil" Version="0.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -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 wront 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, expectedItemsCount: 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();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
| Method | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio |
|
||||
|------------------------------------------------------------ |-----------------------:|--------------------:|--------------------:|-----------------------:|-----------:|--------:|----------:|----------:|---------:|-----------:|------------:|
|
||||
|
||||
|
||||
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio |
|
||||
|------------------------------------------------------------ |-----------:|---------:|---------:|------:|--------:|--------:|--------:|--------:|----------:|------------:|
|
||||
| Array_Create_WithCapacity | 2,447.9 us | 8.35 us | 7.40 us | 1.99 | 0.02 | 58.5938 | 58.5938 | 58.5938 | 8000061 B | 137,932.09 |
|
||||
| List_Create_WithCapacity | 2,377.6 us | 43.03 us | 40.25 us | 1.93 | 0.03 | 54.6875 | 54.6875 | 54.6875 | 8000451 B | 137,938.81 |
|
||||
| PooledList_Create_WithCapacity | 1,229.4 us | 10.70 us | 9.48 us | 1.00 | 0.00 | - | - | - | 58 B | 1.00 |
|
||||
| RecyclableList_Create_WithCapacity | 1,233.9 us | 3.93 us | 3.68 us | 1.00 | 0.01 | - | - | - | 41 B | 0.71 |
|
||||
| RecyclableLongList_Create_WithCapacity | 786.5 us | 2.32 us | 1.94 us | 0.64 | 0.01 | - | - | - | 425 B | 7.33 |
|
||||
|
||||
| List_Create | 3.5523 ns | 0.0181 ns | 0.0141 ns | 3.5476 ns | 0.000 | 0.00 | 0.0153 | - | - | 32 B | 0.000 |
|
||||
| PooledList_Create | 6.9236 ns | 0.0110 ns | 0.0098 ns | 6.9222 ns | 0.000 | 0.00 | 0.0268 | - | - | 56 B | 0.000 |
|
||||
| RecyclableList_Create | 4.6451 ns | 0.0063 ns | 0.0053 ns | 4.6457 ns | 0.000 | 0.00 | 0.0191 | - | - | 40 B | 0.000 |
|
||||
| RecyclableLongList_Create | 212.8164 ns | 11.9841 ns | 35.3355 ns | 213.2258 ns | 0.000 | 0.00 | 0.0381 | 0.0379 | - | 80 B | 0.000 |
|
||||
|
||||
Array_GetItem | 239.7 us | 0.36 us | 0.32 us | 0.66 | 0.00 | - | NA |
|
||||
List_GetItem | 363.5 us | 0.67 us | 0.56 us | 1.00 | 0.00 | - | NA |
|
||||
PooledList_GetItem | 365.3 us | 0.90 us | 0.75 us | 1.00 | 0.00 | - | NA |
|
||||
RecyclableList_GetItem | 239.2 us | 0.07 us | 0.06 us | 0.65 | 0.00 | - | NA |
|
||||
RecyclableLongList_GetItem | 538.0 us | 1.01 us | 0.89 us | 1.47 | 0.00 | 1 B | NA |
|
||||
RecyclableLongList_GetItem_AsSpan | 673.5 us | 4.85 us | 4.30 us | 1.84 | 0.01 | 1 B | NA |
|
||||
RecyclableLongList_GetItem_AsArray | 239.4 us | 0.22 us | 0.20 us | 0.66 | 0.00 | - | NA |
|
||||
RecyclableLongList_GetItem_AsForEach | 5,514.6 us | 27.28 us | 22.78 us | 15.10 | 0.06 | 109 B | NA |
|
||||
RecyclableLongList_GetItem_AsWhile | 459.3 us | 0.38 us | 0.34 us | 1.26 | 0.00 | - | NA |
|
||||
|
||||
| Array_SetItem | 2,576,086.8389 ns | 13,880.4584 ns | 11,590.8114 ns | 2,576,978.1250 ns | 2.194 | 0.01 | - | - | - | 2 B | 0.000 |
|
||||
| List_SetItem | 3,361,745.7734 ns | 98,390.1277 ns | 290,105.5114 ns | 3,505,120.8984 ns | 2.771 | 0.29 | - | - | - | 2 B | 0.000 |
|
||||
| PooledList_SetItem | 2,676,757.7524 ns | 1,228.8178 ns | 1,026.1185 ns | 2,676,991.4062 ns | 2.280 | 0.01 | - | - | - | 2 B | 0.000 |
|
||||
| RecyclableList_SetItem | 2,547,059.6540 ns | 9,002.9953 ns | 7,980.9208 ns | 2,542,056.8359 ns | 2.170 | 0.01 | - | - | - | 2 B | 0.000 |
|
||||
| RecyclableLongList_SetItem | 2,326,771.9866 ns | 3,186.0942 ns | 2,824.3895 ns | 2,326,636.7188 ns | 1.981 | 0.01 | - | - | - | 2 B | 0.000 |
|
||||
|
||||
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio |
|
||||
|------------------------------------------------------------ |----------:|----------:|----------:|------:|--------:|---------:|--------:|--------:|-----------:|------------:|
|
||||
| List_Add | 11.002 ms | 0.2189 ms | 0.2996 ms | 1.36 | 0.04 | 140.6250 | 93.7500 | 93.7500 | 16778117 B | 236,311.51 |
|
||||
| PooledList_Add | 8.117 ms | 0.0115 ms | 0.0102 ms | 1.00 | 0.00 | - | - | - | 71 B | 1.00 |
|
||||
| RecyclableList_Add | 8.771 ms | 0.0169 ms | 0.0141 ms | 1.08 | 0.00 | - | - | - | 1233 B | 17.37 |
|
||||
| RecyclableLongList_Add | 6.024 ms | 0.1101 ms | 0.1030 ms | 0.74 | 0.01 | - | - | - | 3317 B | 46.72 |
|
||||
|
||||
| List_Add_WithCapacity | 3,724,682.0668 ns | 88,719.4866 ns | 260,198.8962 ns | 3,717,873.0469 ns | 3.326 | 0.09 | 50.7813 | 50.7813 | 50.7813 | 4000368 B | 1.000 |
|
||||
| PooledList_Add_WithCapacity | 2,575,618.8337 ns | 5,277.9410 ns | 4,678.7572 ns | 2,575,805.2734 ns | 2.194 | 0.01 | - | - | - | 60 B | 0.000 |
|
||||
| RecyclableList_Add_WithCapacity | 2,785,606.2500 ns | 4,595.4354 ns | 4,073.7338 ns | 2,785,292.7734 ns | 2.372 | 0.01 | - | - | - | 42 B | 0.000 |
|
||||
| RecyclableLongList_Add_WithCapacity | 2,878,685.9701 ns | 44,974.7609 ns | 35,113.3350 ns | 2,873,102.9297 ns | 2.453 | 0.03 | - | - | - | 170 B | 0.000 |
|
||||
|
||||
| List_AddRangeWhenSourceIsArray | 1,966,607.1581 ns | 39,075.4380 ns | 100,165.2861 ns | 1,958,457.4219 ns | 1.721 | 0.07 | 42.9688 | 42.9688 | 42.9688 | 4000364 B | 1.000 |
|
||||
| PooledList_AddRangeWhenSourceIsArray | 972,973.4049 ns | 12,365.6121 ns | 11,566.8012 ns | 974,744.8242 ns | 0.829 | 0.01 | - | - | - | 57 B | 0.000 |
|
||||
| RecyclableList_AddRangeWhenSourceIsArray | 1,098,651.7578 ns | 3,202.7829 ns | 2,995.8851 ns | 1,097,979.4922 ns | 0.936 | 0.00 | - | - | - | 41 B | 0.000 |
|
||||
| RecyclableLongList_AddRangeWhenSourceIsArray | 1,023,799.0234 ns | 2,772.1984 ns | 2,457.4816 ns | 1,023,618.7500 ns | 0.872 | 0.00 | - | - | - | 169 B | 0.000 |
|
||||
|
||||
| List_AddRangeWhenSourceIsList | 1,954,805.9462 ns | 37,631.3829 ns | 71,597.6170 ns | 1,947,531.6406 ns | 1.656 | 0.06 | 42.9688 | 42.9688 | 42.9688 | 4000363 B | 1.000 |
|
||||
| PooledList_AddRangeWhenSourceIsList | 963,657.7116 ns | 5,594.8195 ns | 5,233.3976 ns | 965,055.4199 ns | 0.820 | 0.01 | - | - | - | 57 B | 0.000 |
|
||||
| RecyclableList_AddRangeWhenSourceIsList | 1,114,697.8385 ns | 4,015.8164 ns | 3,756.3971 ns | 1,115,863.6719 ns | 0.950 | 0.00 | - | - | - | 41 B | 0.000 |
|
||||
| RecyclableLongList_AddRangeWhenSourceIsList | 1,008,424.0374 ns | 2,179.7502 ns | 1,932.2918 ns | 1,008,779.8828 ns | 0.859 | 0.00 | - | - | - | 169 B | 0.000 |
|
||||
|
||||
| List_AddRangeWhenSourceIsIList | 2,045,453.8895 ns | 40,653.4327 ns | 98,956.0738 ns | 2,040,041.7969 ns | 1.734 | 0.12 | 42.9688 | 42.9688 | 42.9688 | 4000366 B | 1.000 |
|
||||
| PooledList_AddRangeWhenSourceIsIList | 977,402.4790 ns | 4,165.1933 ns | 3,478.1251 ns | 978,420.3125 ns | 0.832 | 0.00 | - | - | - | 58 B | 0.000 |
|
||||
| RecyclableList_AddRangeWhenSourceIsIList | 1,109,563.8477 ns | 4,480.2733 ns | 4,190.8505 ns | 1,108,281.9336 ns | 0.945 | 0.00 | - | - | - | 41 B | 0.000 |
|
||||
| RecyclableLongList_AddRangeWhenSourceIsIList | 2,632,797.0052 ns | 3,899.2563 ns | 3,044.2828 ns | 2,632,267.1875 ns | 2.243 | 0.01 | - | - | - | 170 B | 0.000 |
|
||||
|
||||
| PooledList_AddRangeWhenSourceIsIEnumerable | 28,102,425.4630 ns | 556,294.1092 ns | 779,846.6478 ns | 28,009,543.7500 ns | 24.007 | 0.74 | 2187.5000 | 1375.0000 | 218.7500 | 12000526 B | 3.000 |
|
||||
| List_AddRangeWhenSourceIsIEnumerable | 47,884,030.8442 ns | 955,391.3313 ns | 1,370,193.6989 ns | 48,152,172.7273 ns | 40.578 | 1.04 | 2272.7273 | 1363.6364 | 363.6364 | 20389288 B | 5.097 |
|
||||
| RecyclableList_AddRangeWhenSourceIsIEnumerable | 27,102,237.2533 ns | 535,598.7947 ns | 595,316.6412 ns | 27,248,382.8125 ns | 23.009 | 0.61 | 2250.0000 | 1406.2500 | 250.0000 | 12000305 B | 3.000 |
|
||||
| RecyclableLongList_AddRangeWhenSourceIsIEnumerable | 27,206,882.1023 ns | 517,997.7196 ns | 636,147.7188 ns | 27,262,153.1250 ns | 23.049 | 0.64 | 2218.7500 | 1468.7500 | 250.0000 | 12000361 B | 3.000 |
|
||||
|
||||
| PooledList_AddRangeWhenSourceIsPooledList | 958,132.3033 ns | 4,444.5508 ns | 3,939.9785 ns | 957,355.9570 ns | 0.816 | 0.00 | - | - | - | 57 B | 0.000 |
|
||||
| RecyclableList_AddRangeWhenSourceIsRecyclableList | 1,068,135.4492 ns | 15,036.9473 ns | 12,556.5320 ns | 1,063,337.2070 ns | 0.910 | 0.01 | - | - | - | 41 B | 0.000 |
|
||||
| RecyclableLongList_AddRangeWhenSourceIsRecyclableList | 1,057,253.7249 ns | 2,697.5224 ns | 2,391.2834 ns | 1,057,475.9766 ns | 0.901 | 0.00 | - | - | - | 169 B | 0.000 |
|
||||
|
||||
| List_AddRange_WithCapacity | 2,022,247.3039 ns | 44,489.4467 ns | 129,071.9520 ns | 2,030,418.9453 ns | 1.767 | 0.06 | 39.0625 | 39.0625 | 39.0625 | 4000318 B | 1.000 |
|
||||
| PooledList_AddRange_WithCapacity | 965,635.9570 ns | 11,740.2154 ns | 10,981.8048 ns | 968,593.2617 ns | 0.822 | 0.01 | - | - | - | 57 B | 0.000 |
|
||||
| RecyclableList_AddRange_WithCapacity | 1,044,438.5517 ns | 3,047.5353 ns | 2,544.8300 ns | 1,045,038.8672 ns | 0.890 | 0.00 | - | - | - | 41 B | 0.000 |
|
||||
| RecyclableLongList_AddRange_WithCapacity | 1,028,953.5156 ns | 17,735.8499 ns | 14,810.2379 ns | 1,022,104.4922 ns | 0.876 | 0.01 | - | - | - | 169 B | 0.000 |
|
||||
|
||||
| Array_Count | 0.0087 ns | 0.0110 ns | 0.0103 ns | 0.0024 ns | 0.000 | 0.00 | - | - | - | - | 0.000 |
|
||||
| List_Count | 1.2190 ns | 0.0046 ns | 0.0040 ns | 1.2173 ns | 0.000 | 0.00 | - | - | - | - | 0.000 |
|
||||
| PooledList_Count | 0.0037 ns | 0.0015 ns | 0.0012 ns | 0.0040 ns | 0.000 | 0.00 | - | - | - | - | 0.000 |
|
||||
| RecyclableList_Count | 1.2096 ns | 0.0009 ns | 0.0008 ns | 1.2092 ns | 0.000 | 0.00 | - | - | - | - | 0.000 |
|
||||
| RecyclableLongList_Count | 0.0103 ns | 0.0008 ns | 0.0007 ns | 0.0102 ns | 0.000 | 0.00 | - | - | - | - | 0.000 |
|
||||
|
||||
| Array_LongCount | 0.0021 ns | 0.0005 ns | 0.0004 ns | 0.0022 ns | 0.000 | 0.00 | - | - | - | - | 0.000 |
|
||||
| RecyclableLongList_LongCount | 0.0030 ns | 0.0004 ns | 0.0003 ns | 0.0031 ns | 0.000 | 0.00 | - | - | - | - | 0.000 |
|
||||
|
||||
| List_Contains_FirstItems | 1,855,720,661.5385 ns | 16,632,292.4881 ns | 13,888,717.5079 ns | 1,850,307,400.0000 ns | 1,580.480 | 10.24 | - | - | - | 600 B | 0.000 |
|
||||
| PooledList_Contains_FirstItems | 1,854,542,814.2857 ns | 3,151,361.3032 ns | 2,793,599.7119 ns | 1,855,147,650.0000 ns | 1,579.345 | 5.84 | - | - | - | 600 B | 0.000 |
|
||||
| RecyclableLongList_Contains_FirstItems | 1,842,715,553.8462 ns | 5,837,438.3043 ns | 4,874,525.3630 ns | 1,840,917,300.0000 ns | 1,569.420 | 6.23 | - | - | - | 600 B | 0.000 |
|
||||
|
||||
| List_Contains_LastItems | 56,934,350,853.3333 ns | 257,718,254.2389 ns | 241,069,814.9149 ns | 56,884,611,800.0000 ns | 48,516.143 | 270.39 | - | - | - | 600 B | 0.000 |
|
||||
| PooledList_Contains_LastItems | 57,262,565,633.3333 ns | 295,685,051.7358 ns | 276,583,980.8497 ns | 57,246,796,900.0000 ns | 48,801.131 | 237.64 | - | - | - | 600 B | 0.000 |
|
||||
| RecyclableLongList_Contains_LastItems | 57,019,747,564.2857 ns | 264,241,719.1753 ns | 234,243,401.3552 ns | 57,059,345,900.0000 ns | 48,552.402 | 223.09 | - | - | - | 600 B | 0.000 |
|
||||
|
||||
| Array_IndexOf_FirstItems | 1,874,810,260.0000 ns | 25,774,115.5271 ns | 24,109,123.6555 ns | 1,867,388,700.0000 ns | 1,597.521 | 22.54 | - | - | - | 600 B | 0.000 |
|
||||
| List_IndexOf_FirstItems | 1,864,704,725.0000 ns | 10,982,470.8154 ns | 8,574,390.8164 ns | 1,866,253,200.0000 ns | 1,588.702 | 10.94 | - | - | - | 600 B | 0.000 |
|
||||
| PooledList_IndexOf_FirstItems | 1,840,856,800.0000 ns | 4,046,589.8784 ns | 3,159,311.2037 ns | 1,839,944,050.0000 ns | 1,568.374 | 6.17 | - | - | - | 600 B | 0.000 |
|
||||
| RecyclableList_IndexOf_FirstItems | 1,832,934,950.0000 ns | 1,729,308.0131 ns | 1,350,129.9476 ns | 1,832,475,300.0000 ns | 1,561.625 | 5.52 | - | - | - | 600 B | 0.000 |
|
||||
| RecyclableLongList_IndexOf_FirstItems | 1,844,986,792.3077 ns | 3,209,050.1952 ns | 2,679,702.2516 ns | 1,843,943,600.0000 ns | 1,571.359 | 6.49 | - | - | - | 600 B | 0.000 |
|
||||
| RecyclableLongList_LongIndexOf_FirstItems | 1,878,933,500.0000 ns | 17,118,131.6171 ns | 16,012,310.9355 ns | 1,870,009,600.0000 ns | 1,601.616 | 14.82 | - | - | - | 600 B | 0.000 |
|
||||
|
||||
| List_IndexOf_LastItems | 57,303,575,140.0000 ns | 297,612,744.3548 ns | 278,387,145.7216 ns | 57,391,576,000.0000 ns | 48,792.865 | 303.56 | - | - | - | 600 B | 0.000 |
|
||||
| PooledList_IndexOf_LastItems | 57,032,399,106.6667 ns | 281,241,865.3817 ns | 263,073,815.3730 ns | 56,895,992,200.0000 ns | 48,566.622 | 322.88 | - | - | - | 600 B | 0.000 |
|
||||
| RecyclableLongList_IndexOf_LastItems | 57,308,279,713.3333 ns | 272,718,905.6589 ns | 255,101,433.5602 ns | 57,297,645,400.0000 ns | 48,767.562 | 239.96 | - | - | - | 600 B | 0.000 |
|
||||
| RecyclableLongList_LongIndexOf_LastItems | 57,198,323,460.0000 ns | 282,971,632.5816 ns | 264,691,840.6850 ns | 57,198,418,300.0000 ns | 48,669.107 | 304.11 | - | - | - | 600 B | 0.000 |
|
||||
|
||||
| List_Remove_FirstItems | 5,023,030,012.5000 ns | 82,615,052.4052 ns | 107,422,903.9562 ns | 5,000,417,000.0000 ns | 4,295.171 | 116.37 | - | - | - | 4000656 B | 1.000 |
|
||||
| PooledList_Remove_FirstItems | 4,750,601,721.4286 ns | 17,989,258.8487 ns | 15,947,009.4038 ns | 4,747,622,550.0000 ns | 4,043.997 | 21.26 | - | - | - | 992 B | 0.000 |
|
||||
| RecyclableList_Remove_FirstItems | 4,950,634,900.0000 ns | 33,761,094.2570 ns | 29,928,330.6849 ns | 4,936,519,050.0000 ns | 4,218.075 | 23.90 | - | - | - | 640 B | 0.000 |
|
||||
| RecyclableLongList_Remove_FirstItems | 5,020,383,250.0000 ns | 100,209,734.5497 ns | 115,401,679.3276 ns | 5,029,163,750.0000 ns | 4,306.772 | 86.95 | - | - | - | 952 B | 0.000 |
|
||||
|
||||
| List_Remove_LastItems | 56,593,869,333.3333 ns | 258,693,958.6041 ns | 241,982,489.3835 ns | 56,513,091,800.0000 ns | 48,189.227 | 272.02 | - | - | - | 4000656 B | 1.000 |
|
||||
| PooledList_Remove_LastItems | 56,465,443,542.8571 ns | 99,443,506.1766 ns | 88,154,078.0245 ns | 56,458,364,050.0000 ns | 48,092.866 | 216.09 | - | - | - | 992 B | 0.000 |
|
||||
| RecyclableList_Remove_LastItems | 57,432,405,146.6667 ns | 264,062,625.8226 ns | 247,004,344.0307 ns | 57,409,508,000.0000 ns | 48,952.707 | 234.83 | - | - | - | 640 B | 0.000 |
|
||||
| RecyclableLongList_Remove_LastItems | 57,077,678,728.5714 ns | 210,992,675.8689 ns | 187,039,511.4398 ns | 57,038,290,150.0000 ns | 48,614.503 | 242.79 | - | - | - | 952 B | 0.000 |
|
||||
|
||||
| List_RemoveAt_FirstItems | 4,517,669,893.3333 ns | 77,360,576.7460 ns | 72,363,131.4861 ns | 4,546,302,700.0000 ns | 3,850.590 | 62.34 | - | - | - | 4000656 B | 1.000 |
|
||||
| PooledList_RemoveAt_FirstItems | 4,439,417,700.0000 ns | 40,915,112.8948 ns | 38,272,021.9874 ns | 4,418,366,400.0000 ns | 3,779.751 | 38.92 | - | - | - | 992 B | 0.000 |
|
||||
| RecyclableList_RemoveAt_FirstItems | 4,558,102,892.8571 ns | 25,076,973.8515 ns | 22,230,084.1404 ns | 4,559,174,800.0000 ns | 3,882.107 | 16.61 | - | - | - | 640 B | 0.000 |
|
||||
| RecyclableLongList_RemoveAt_FirstItems | 4,617,033,766.6667 ns | 85,061,197.3076 ns | 79,566,296.7373 ns | 4,632,994,800.0000 ns | 3,928.768 | 72.17 | - | - | - | 952 B | 0.000 |
|
||||
|
||||
| List_RemoveAt_LastItems | 2,005,211.3281 ns | 40,078.0351 ns | 84,538.1787 ns | 2,010,579.1016 ns | 1.725 | 0.08 | 42.9688 | 42.9688 | 42.9688 | 4000306 B | 1.000 |
|
||||
| PooledList_RemoveAt_LastItems | 1,047,436.1654 ns | 5,655.2027 ns | 5,289.8801 ns | 1,044,690.9180 ns | 0.892 | 0.01 | - | - | - | 58 B | 0.000 |
|
||||
| RecyclableList_RemoveAt_LastItems | 1,256,380.8969 ns | 9,142.3285 ns | 7,634.2584 ns | 1,254,354.9805 ns | 1.070 | 0.01 | - | - | - | 41 B | 0.000 |
|
||||
| RecyclableLongList_RemoveAt_LastItems | 1,621,193.5826 ns | 20,030.6198 ns | 17,756.6227 ns | 1,620,789.8438 ns | 1.379 | 0.02 | - | - | - | 353 B | 0.000 |
|
||||
+13
-11
@@ -7,38 +7,40 @@ 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Recyclable.Collections.Benchmarks", "Recyclable.Collections.Benchmarks\Recyclable.Collections.Benchmarks.csproj", "{CDC0CF78-FC11-43E3-9059-3261DA3639D1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceCodeGenerator.Net", "SourceCodeGenerator.Net\SourceCodeGenerator.Net.csproj", "{30C187CF-D469-48FC-8D35-6EB031535668}"
|
||||
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
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Debug|x64.Build.0 = Debug|x64
|
||||
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|x64.ActiveCfg = Release|x64
|
||||
{7EC0F96D-FB4C-418A-9172-612CC5BCE705}.Release|x64.Build.0 = Release|x64
|
||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Debug|x64.Build.0 = Debug|x64
|
||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|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
|
||||
{30C187CF-D469-48FC-8D35-6EB031535668}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{30C187CF-D469-48FC-8D35-6EB031535668}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{30C187CF-D469-48FC-8D35-6EB031535668}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{30C187CF-D469-48FC-8D35-6EB031535668}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{30C187CF-D469-48FC-8D35-6EB031535668}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{30C187CF-D469-48FC-8D35-6EB031535668}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{30C187CF-D469-48FC-8D35-6EB031535668}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{30C187CF-D469-48FC-8D35-6EB031535668}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.ExceptionServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
internal static class ExceptionExtensions
|
||||
{
|
||||
public static void CaptureAndRethrow(this Exception ex)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(ex).Throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Style", "IDE0032:Use auto property", Justification = "<Pending>", Scope = "member", Target = "~F:Recyclable.Collections.RecyclableList`1._count")]
|
||||
@@ -6,6 +6,79 @@ namespace Recyclable.Collections
|
||||
// It was only beautified for .Net 6.0+.
|
||||
internal static class ListExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Contains<T>(this RecyclableList<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CopyTo<T>(this RecyclableList<T[]> arrays, long startingIndex, int blockSize, int lastBlockSize, T[] destinationArray, int destinationArrayIndex)
|
||||
{
|
||||
Span<T> arrayMemory = destinationArray.AsSpan();
|
||||
arrayMemory = arrayMemory[destinationArrayIndex..];
|
||||
int startingArrayIndex = (int)(startingIndex / blockSize);
|
||||
startingIndex %= blockSize;
|
||||
if (arrays.Count > 0)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[0].AsSpan();
|
||||
var maxToCopy = (int)Math.Min(blockSize - startingIndex, arrayMemory.Length);
|
||||
if (arrays.Count == 1)
|
||||
{
|
||||
maxToCopy = (int)Math.Min(maxToCopy, lastBlockSize);
|
||||
}
|
||||
|
||||
sourceMemory = sourceMemory[(int)startingIndex..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
arrayMemory = arrayMemory[maxToCopy..];
|
||||
}
|
||||
|
||||
for (var arrayIdx = startingArrayIndex + 1; arrayIdx < arrays.Count - 1 && arrayMemory.Length > 0; arrayIdx++)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[arrayIdx].AsSpan();
|
||||
var maxToCopy = Math.Min(blockSize, arrayMemory.Length);
|
||||
sourceMemory = sourceMemory[..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
arrayMemory = arrayMemory[maxToCopy..];
|
||||
}
|
||||
|
||||
if (arrays.Count > 1 && arrayMemory.Length > 0)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[^1].AsSpan();
|
||||
var maxToCopy = Math.Min(lastBlockSize, arrayMemory.Length);
|
||||
sourceMemory = sourceMemory[..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IEnumerable<T> Enumerate<T>(this RecyclableList<T[]> arrays, int chunkSize, long totalCount)
|
||||
{
|
||||
long currentCount = 0;
|
||||
for (var arrayIdx = 0; (arrayIdx < arrays.Count) && (currentCount < totalCount); arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
currentCount += Math.Min(chunkSize, totalCount);
|
||||
switch (currentCount < totalCount)
|
||||
{
|
||||
case true:
|
||||
for (var valueIdx = 0; valueIdx < chunkSize; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case false:
|
||||
var partialCount = (int)(totalCount % chunkSize);
|
||||
int maxCount = partialCount > 0 ? partialCount : chunkSize;
|
||||
for (var valueIdx = 0; valueIdx < maxCount; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void InsertionSort<T>(this IList<T> values, int startIndex, int endIndex, IComparer<T> comparer)
|
||||
{
|
||||
var left = startIndex;
|
||||
@@ -29,6 +102,26 @@ namespace Recyclable.Collections
|
||||
{
|
||||
values.InsertionSort(Comparer<T>.Default);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long LongIndexOf<T>(this RecyclableList<T[]> arrays, int blockSize, T item, IEqualityComparer<T> comparer)
|
||||
{
|
||||
for (var arrayIdx = 0; arrayIdx < arrays.Count; arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
Span<T> arrayMemory = array.AsSpan();
|
||||
for (int memoryIdx = 0; memoryIdx < arrayMemory.Length; memoryIdx++)
|
||||
{
|
||||
if (comparer.Equals(arrayMemory[memoryIdx], item))
|
||||
{
|
||||
return (arrayIdx * (long)blockSize) + memoryIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void QuickSort<T>(this IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
|
||||
{
|
||||
var range = (startIndex, endIndex);
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections.Parallel
|
||||
{
|
||||
public sealed class BarrierSlimmer : IDisposable
|
||||
{
|
||||
private static readonly ObjectPool<BarrierSlimmer> _defaultPool = new DefaultObjectPool<BarrierSlimmer>(new DefaultPooledObjectPolicy<BarrierSlimmer>());
|
||||
private ObjectPool<BarrierSlimmer>? _assignedPool;
|
||||
private readonly SpinLock _lock = new(false);
|
||||
|
||||
public bool HasParticipants;
|
||||
public int Participants;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void AddParticipant()
|
||||
{
|
||||
bool isLocked = false;
|
||||
_lock.Enter(ref isLocked);
|
||||
|
||||
Participants++;
|
||||
if (Participants == 1)
|
||||
{
|
||||
HasParticipants = true;
|
||||
}
|
||||
|
||||
if (isLocked)
|
||||
{
|
||||
_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void RemoveParticipant()
|
||||
{
|
||||
if (!HasParticipants)
|
||||
{
|
||||
ThrowNoMoreParticipants();
|
||||
}
|
||||
|
||||
bool isLocked = false;
|
||||
_lock.Enter(ref isLocked);
|
||||
|
||||
Participants--;
|
||||
if (Participants == 0)
|
||||
{
|
||||
HasParticipants = false;
|
||||
}
|
||||
|
||||
if (isLocked)
|
||||
{
|
||||
_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowNoMoreParticipants()
|
||||
{
|
||||
throw new InvalidOperationException("There are no more participants. The operation is not allowed");
|
||||
}
|
||||
|
||||
public BarrierSlimmer()
|
||||
{
|
||||
_lock = new(false);
|
||||
}
|
||||
|
||||
public BarrierSlimmer(int participants)
|
||||
{
|
||||
_lock = new(false);
|
||||
Participants = participants;
|
||||
HasParticipants = participants > 0;
|
||||
}
|
||||
|
||||
public static BarrierSlimmer Create(int participants)
|
||||
{
|
||||
var result = _defaultPool.Get();
|
||||
result.Participants = participants;
|
||||
result.HasParticipants = participants > 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var assignedPool = _assignedPool;
|
||||
_assignedPool = null;
|
||||
assignedPool?.Return(this);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void SignalAndWait()
|
||||
{
|
||||
RemoveParticipant();
|
||||
|
||||
//if (!HasParticipants)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
//if (!HasParticipants)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
//if (!HasParticipants)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
// SpinWait.SpinUntil(() => HasParticipants);
|
||||
var waitSpinner = new SpinWait();
|
||||
while (HasParticipants)
|
||||
{
|
||||
waitSpinner.SpinOnce(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
namespace Recyclable.Collections.Parallel
|
||||
{
|
||||
public sealed class IndexOfSynchronizationContext
|
||||
{
|
||||
private SpinWait _participantsSpinWait = new();
|
||||
private SpinLockSlimmer _participantsSpinLock = new();
|
||||
private SpinLockSlimmer _indexOfSpinLock = new();
|
||||
|
||||
internal bool _isItemFound;
|
||||
internal int _participants;
|
||||
internal bool _returned;
|
||||
|
||||
public void AddParticipant()
|
||||
{
|
||||
_participantsSpinLock.Enter();
|
||||
_participants++;
|
||||
_participantsSpinLock.Exit();
|
||||
}
|
||||
|
||||
public void AddParticipants(int newParticipants)
|
||||
{
|
||||
_participantsSpinLock.Enter();
|
||||
_participants += newParticipants;
|
||||
_participantsSpinLock.Exit();
|
||||
}
|
||||
|
||||
public void Lock()
|
||||
{
|
||||
_indexOfSpinLock.Enter();
|
||||
}
|
||||
|
||||
public void RemoveParticipants(int participantsToRemove)
|
||||
{
|
||||
if (_participants < participantsToRemove)
|
||||
{
|
||||
ThrowNotEnoughParticipants();
|
||||
}
|
||||
|
||||
_participantsSpinLock.Enter();
|
||||
_participants -= participantsToRemove;
|
||||
_participantsSpinLock.Exit();
|
||||
}
|
||||
|
||||
public void RemoveParticipant()
|
||||
{
|
||||
if (_participants == 0)
|
||||
{
|
||||
ThrowNoMoreParticipants();
|
||||
}
|
||||
|
||||
_participantsSpinLock.Enter();
|
||||
_participants--;
|
||||
_participantsSpinLock.Exit();
|
||||
}
|
||||
|
||||
public void SignalAndWait()
|
||||
{
|
||||
if (_participants == 0)
|
||||
{
|
||||
ThrowNoMoreParticipants();
|
||||
}
|
||||
|
||||
_participantsSpinLock.Enter();
|
||||
_participants--;
|
||||
_participantsSpinLock.Exit();
|
||||
|
||||
while (_participants > 0)
|
||||
{
|
||||
_participantsSpinWait.SpinOnce();
|
||||
}
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
_indexOfSpinLock.Exit();
|
||||
}
|
||||
|
||||
private static void ThrowNotEnoughParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are not enough participants.");
|
||||
private static void ThrowNoMoreParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are no more participants.");
|
||||
|
||||
public long FoundBlockIndex;
|
||||
public long FoundItemIndex;
|
||||
public Exception? Exception;
|
||||
public int Participants => _participants;
|
||||
|
||||
public IndexOfSynchronizationContext()
|
||||
{
|
||||
}
|
||||
|
||||
public IndexOfSynchronizationContext(int participantCount)
|
||||
{
|
||||
_participants = participantCount;
|
||||
}
|
||||
|
||||
public void SetItemFound()
|
||||
{
|
||||
_isItemFound = true;
|
||||
}
|
||||
|
||||
public bool IsItemFound => _isItemFound;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_returned)
|
||||
{
|
||||
_returned = true;
|
||||
IndexOfSynchronizationContextPool.Shared.Return(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
|
||||
namespace Recyclable.Collections.Parallel
|
||||
{
|
||||
public static class IndexOfSynchronizationContextPool
|
||||
{
|
||||
private static readonly SpinLock _updateLock = new(false);
|
||||
public static readonly ObjectPool<IndexOfSynchronizationContext> Shared = new DefaultObjectPool<IndexOfSynchronizationContext>(new DefaultPooledObjectPolicy<IndexOfSynchronizationContext>());
|
||||
|
||||
public static IndexOfSynchronizationContext GetWithOneParticipant()
|
||||
{
|
||||
bool lockTaken = false;
|
||||
_updateLock.Enter(ref lockTaken);
|
||||
var context = Shared.Get();
|
||||
if (lockTaken)
|
||||
{
|
||||
_updateLock.Exit(false);
|
||||
}
|
||||
|
||||
if (context._isItemFound)
|
||||
{
|
||||
context._isItemFound = false;
|
||||
}
|
||||
|
||||
switch (context._participants)
|
||||
{
|
||||
case > 1:
|
||||
context.RemoveParticipants(context._participants - 1);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
context.AddParticipant();
|
||||
break;
|
||||
}
|
||||
|
||||
if (context.Exception != null)
|
||||
{
|
||||
context.Exception = null;
|
||||
}
|
||||
|
||||
if (context.FoundItemIndex >= 0)
|
||||
{
|
||||
context.FoundItemIndex = RecyclableDefaults.ItemNotFoundIndexLong;
|
||||
}
|
||||
|
||||
context._returned = false;
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Recyclable.Collections.Parallel
|
||||
{
|
||||
public sealed class ManualResetEventSlimmer
|
||||
{
|
||||
internal bool _isSet;
|
||||
public bool IsSet => _isSet;
|
||||
|
||||
internal bool _returned;
|
||||
|
||||
public ManualResetEventSlimmer()
|
||||
{
|
||||
}
|
||||
|
||||
public ManualResetEventSlimmer(bool initialState)
|
||||
{
|
||||
_isSet = initialState;
|
||||
}
|
||||
|
||||
public void Set()
|
||||
{
|
||||
_isSet = true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_isSet = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_returned)
|
||||
{
|
||||
_returned = true;
|
||||
ManualResetEventSlimmerPool.Shared.Return(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
|
||||
namespace Recyclable.Collections.Parallel
|
||||
{
|
||||
public static class ManualResetEventSlimmerPool
|
||||
{
|
||||
public static readonly SpinLock _updateLock = new(false);
|
||||
public static readonly ObjectPool<ManualResetEventSlimmer> Shared = new DefaultObjectPool<ManualResetEventSlimmer>(new DefaultPooledObjectPolicy<ManualResetEventSlimmer>());
|
||||
|
||||
public static ManualResetEventSlimmer Create(bool initialState)
|
||||
{
|
||||
bool lockTaken = false;
|
||||
_updateLock.Enter(ref lockTaken);
|
||||
ManualResetEventSlimmer result = Shared.Get();
|
||||
if (lockTaken)
|
||||
{
|
||||
_updateLock.Exit(false);
|
||||
}
|
||||
|
||||
result._returned = false;
|
||||
if (result._isSet != initialState)
|
||||
{
|
||||
result._isSet = initialState;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Recyclable.Collections.Parallel
|
||||
{
|
||||
public struct SpinLockSlimmer
|
||||
{
|
||||
private volatile int _locked;
|
||||
|
||||
public void Enter()
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _locked, 1, 0) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SpinWait waiter = new();
|
||||
while (Interlocked.CompareExchange(ref _locked, 1, 0) == 1)
|
||||
{
|
||||
waiter.SpinOnce();
|
||||
}
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
_locked = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System.Buffers;
|
||||
|
||||
namespace Recyclable.Collections.Pools
|
||||
{
|
||||
public class OneSizeArrayPool<T> : ArrayPool<T>
|
||||
{
|
||||
private readonly int _blockSize;
|
||||
private readonly int _minBlockCount;
|
||||
private int _blockCount;
|
||||
private MemoryBucket<T>? _currentBucket;
|
||||
private readonly SpinLock _updateLock = new(false);
|
||||
|
||||
public OneSizeArrayPool(int blockSize, int minBlockCount)
|
||||
{
|
||||
_blockSize = blockSize;
|
||||
_minBlockCount = minBlockCount;
|
||||
}
|
||||
|
||||
public override T[] Rent(int minimumLength)
|
||||
{
|
||||
if (minimumLength != _blockSize)
|
||||
{
|
||||
ThrowWrongRentedArraySizeException(minimumLength, _blockSize);
|
||||
}
|
||||
|
||||
MemoryBucket<T>? bucket;
|
||||
bool lockTaken = false;
|
||||
_updateLock.Enter(ref lockTaken);
|
||||
if (_currentBucket != null)
|
||||
{
|
||||
_blockCount--;
|
||||
bucket = _currentBucket;
|
||||
_currentBucket = _currentBucket.NextBucket;
|
||||
}
|
||||
else
|
||||
{
|
||||
bucket = null;
|
||||
}
|
||||
|
||||
if (lockTaken)
|
||||
{
|
||||
_updateLock.Exit(false);
|
||||
}
|
||||
|
||||
return bucket != null ? bucket.Memory : new T[_blockSize];
|
||||
}
|
||||
|
||||
public override void Return(T[] array, bool clearArray = false)
|
||||
{
|
||||
if (array.Length != _blockSize)
|
||||
{
|
||||
ThrowWrongReturnedArraySizeException(array.Length, _blockSize);
|
||||
}
|
||||
|
||||
MemoryBucket<T> bucket = new(array);
|
||||
if (clearArray)
|
||||
{
|
||||
Array.Clear(array, 0, array.Length);
|
||||
}
|
||||
|
||||
bool lockTaken = false;
|
||||
_updateLock.Enter(ref lockTaken);
|
||||
if (_blockCount < _minBlockCount)
|
||||
{
|
||||
bucket.NextBucket = _currentBucket;
|
||||
_currentBucket = bucket;
|
||||
_blockCount++;
|
||||
}
|
||||
|
||||
if (lockTaken)
|
||||
{
|
||||
_updateLock.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowWrongReturnedArraySizeException(int returnedLength, int blockSize)
|
||||
{
|
||||
throw new InvalidOperationException($"The array size {returnedLength} doesn't match the size {blockSize} of the pool buffer and cannot be returned to this pool.");
|
||||
}
|
||||
|
||||
private static void ThrowWrongRentedArraySizeException(int requestedSize, int blockSize)
|
||||
{
|
||||
throw new InvalidOperationException($"The requested size {requestedSize} doesn't match the size {blockSize} of the pool buffer and cannot be taken from this pool.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Recyclable.Collections.Pools
|
||||
{
|
||||
public static class RecyclableArrayPool<T>
|
||||
{
|
||||
public static readonly OneSizeArrayPool<T> Null = new(0, 0);
|
||||
|
||||
private static readonly ConcurrentDictionary<int, ArrayPool<T>> _pools = new();
|
||||
|
||||
public static ArrayPool<T> Shared(int blockSize)
|
||||
{
|
||||
//lock (_lock)
|
||||
{
|
||||
if (!_pools.TryGetValue(blockSize, out ArrayPool<T>? pool))
|
||||
{
|
||||
pool = new OneSizeArrayPool<T>(blockSize, 100);
|
||||
pool = _pools.TryAdd(blockSize, pool) ? pool : _pools[blockSize];
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
//_lock.Wait();
|
||||
//try
|
||||
//{
|
||||
//}
|
||||
//finally
|
||||
//{
|
||||
// _lock.Release();
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,4 +31,9 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
|
||||
<PackageReference Include="morelinq" Version="3.4.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,7 +1,13 @@
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public class RecyclableDefaults
|
||||
public static class RecyclableDefaults
|
||||
{
|
||||
public const int BlockSize = 10_240;
|
||||
public const int BlockSize = 16_384;
|
||||
public const int Capacity = 1;
|
||||
public const long MinItemsCountForParallelization = 850_000;
|
||||
public const int MinPooledArrayLength = 128;
|
||||
public const int MaxPooledBlockSize = 2_147_483_591;
|
||||
public const long ItemNotFoundIndexLong = -1L;
|
||||
public const int ItemNotFoundIndex = -1;
|
||||
}
|
||||
}
|
||||
@@ -1,154 +1,493 @@
|
||||
using System.Collections;
|
||||
using System.Buffers;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public class RecyclableList<T> : IDisposable, IList<T>
|
||||
public class RecyclableList<T> : IList<T>, IDisposable
|
||||
{
|
||||
private static readonly IEqualityComparer<T> _equalityComparer = EqualityComparer<T>.Default;
|
||||
private static readonly ArrayPool<T> _arrayPool = ArrayPool<T>.Create();
|
||||
|
||||
private readonly List<T[]> _arrays = new();
|
||||
private bool _disposedValue;
|
||||
protected static readonly bool NeedsClearing = !typeof(T).IsValueType;
|
||||
|
||||
public RecyclableList(int blockSize = RecyclableDefaults.BlockSize)
|
||||
protected T[] _memoryBlock;
|
||||
|
||||
private static void ThrowArgumentOutOfRangeException(in string message)
|
||||
{
|
||||
BlockSize = blockSize;
|
||||
throw new ArgumentOutOfRangeException(message, (Exception?)null);
|
||||
}
|
||||
|
||||
public RecyclableList(IEnumerable<T> source, int blockSize = RecyclableDefaults.BlockSize)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
protected static T[] Resize(in T[]? source, int newSize)
|
||||
{
|
||||
BlockSize = blockSize;
|
||||
foreach (var item in source)
|
||||
ArrayPool<T> arrayPool = _arrayPool;
|
||||
T[] newMemoryBlock = newSize >= RecyclableDefaults.MinPooledArrayLength
|
||||
? arrayPool.Rent(newSize)
|
||||
: new T[newSize];
|
||||
|
||||
if (source?.Length > 0)
|
||||
{
|
||||
Add(item);
|
||||
Array.Copy(source!, 0, newMemoryBlock, 0, source!.Length);
|
||||
//var sourceSpan = new Memory<T>(source);
|
||||
//var newArraySpan = new Memory<T>(newMemoryBlock);
|
||||
|
||||
//sourceSpan.CopyTo(newArraySpan);
|
||||
|
||||
if (source!.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||
{
|
||||
arrayPool.Return(source!, NeedsClearing);
|
||||
}
|
||||
}
|
||||
|
||||
return newMemoryBlock;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
protected static int EnsureCapacity(RecyclableList<T> list, int requestedCapacity)
|
||||
{
|
||||
int newCapacity;
|
||||
switch (list._capacity > 0)
|
||||
{
|
||||
case true:
|
||||
newCapacity = list._capacity;
|
||||
while (newCapacity < requestedCapacity)
|
||||
{
|
||||
newCapacity <<= 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case false:
|
||||
newCapacity = requestedCapacity;
|
||||
break;
|
||||
}
|
||||
|
||||
list._memoryBlock = Resize(list._memoryBlock, newCapacity);
|
||||
newCapacity = list._memoryBlock.Length;
|
||||
list._capacity = newCapacity;
|
||||
return newCapacity;
|
||||
}
|
||||
|
||||
#pragma warning disable CS8618 // _memory will be initialized when the 1st item is added
|
||||
public RecyclableList()
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable CS8618 // _memory will be initialized when the 1st item is added
|
||||
public RecyclableList(int initialCapacity)
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
if (initialCapacity > 0)
|
||||
{
|
||||
_memoryBlock = Resize(_memoryBlock, initialCapacity);
|
||||
_capacity = _memoryBlock.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public int BlockSize { get; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetItem(List<T[]> arrays, in int blockSize, in long index, in T value)
|
||||
=> arrays[(int)(index / blockSize)][index % blockSize] = value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T GetItem(List<T[]> arrays, in int blockSize, in long index)
|
||||
=> arrays[(int)(index / blockSize)][index % blockSize];
|
||||
|
||||
public T this[long index]
|
||||
#pragma warning disable CS8618 // _memory will be initialized when the 1st item is added
|
||||
public RecyclableList(RecyclableList<T> source)
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
get => GetItem(_arrays, BlockSize, index);
|
||||
set => SetItem(_arrays, BlockSize, index, value);
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
#pragma warning disable CS8618 // _memory will be initialized when the 1st item is added
|
||||
public RecyclableList(in T[] source)
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
#pragma warning disable CS8618 // _memory will be initialized when the 1st item is added
|
||||
public RecyclableList(List<T> source)
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
#pragma warning disable CS8618 // _memory will be initialized when the 1st item is added
|
||||
public RecyclableList(IList<T> source)
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.Capacity)
|
||||
{
|
||||
_memoryBlock = Resize(_memoryBlock, initialCapacity);
|
||||
_capacity = _memoryBlock.Length;
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => GetItem(_arrays, BlockSize, index);
|
||||
set => SetItem(_arrays, BlockSize, index, value);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _memoryBlock[index];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => _memoryBlock[index] = value;
|
||||
}
|
||||
|
||||
protected int _capacity;
|
||||
public int Capacity
|
||||
{
|
||||
get => _capacity;
|
||||
set
|
||||
{
|
||||
if (_capacity != value)
|
||||
{
|
||||
_memoryBlock = Resize(_memoryBlock, value);
|
||||
_capacity = _memoryBlock.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int _count;
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _count;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => _count = value;
|
||||
}
|
||||
|
||||
public long Capacity { get; set; }
|
||||
public int Count => (int)LongCount;
|
||||
public long LongCount { get; set; }
|
||||
public bool IsReadOnly { get; } = false;
|
||||
public int BlockCount => _arrays.Count;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(T item)
|
||||
{
|
||||
if (LongCount == Capacity)
|
||||
int requiredCapacity = _count + 1;
|
||||
if (_capacity < requiredCapacity)
|
||||
{
|
||||
T[] newArray = BlockSize.RentArrayFromPool<T>();
|
||||
_arrays.Add(newArray);
|
||||
Capacity += BlockSize;
|
||||
_ = EnsureCapacity(this, requiredCapacity);
|
||||
}
|
||||
|
||||
this[LongCount] = item;
|
||||
LongCount++;
|
||||
_memoryBlock[_count++] = item;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(in T[] items)
|
||||
{
|
||||
if (items.LongLength == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int sourceItemsCount = items.Length;
|
||||
int targetCapacity = _count + sourceItemsCount;
|
||||
if (_capacity < targetCapacity)
|
||||
{
|
||||
_ = EnsureCapacity(this, targetCapacity);
|
||||
}
|
||||
|
||||
var targetSpan = new Span<T>(_memoryBlock, _count, sourceItemsCount);
|
||||
Span<T> itemsSpan = new(items);
|
||||
itemsSpan.CopyTo(targetSpan);
|
||||
_count = targetCapacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(List<T> items)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceItemsCount = items.Count;
|
||||
var targetCapacity = _count + sourceItemsCount;
|
||||
if (_capacity < targetCapacity)
|
||||
{
|
||||
_ = EnsureCapacity(this, targetCapacity);
|
||||
}
|
||||
|
||||
items.CopyTo(_memoryBlock, _count);
|
||||
_count = targetCapacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(IList<T> items)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceItemsCount = items.Count;
|
||||
var targetCapacity = _count + sourceItemsCount;
|
||||
if (_capacity < targetCapacity)
|
||||
{
|
||||
_ = EnsureCapacity(this, targetCapacity);
|
||||
}
|
||||
|
||||
items.CopyTo(_memoryBlock, _count);
|
||||
_count = targetCapacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(RecyclableList<T> items)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceItemsCount = items._count;
|
||||
var targetCapacity = _count + sourceItemsCount;
|
||||
if (_capacity < targetCapacity)
|
||||
{
|
||||
_ = EnsureCapacity(this, targetCapacity);
|
||||
}
|
||||
|
||||
var targetSpan = new Span<T>(_memoryBlock, _count, sourceItemsCount);
|
||||
Span<T> itemsSpan = new(items._memoryBlock, 0, sourceItemsCount);
|
||||
itemsSpan.CopyTo(targetSpan);
|
||||
_count = targetCapacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(RecyclableLongList<T> items)
|
||||
{
|
||||
if (items.LongCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceItemsCount = items.LongCount;
|
||||
if (sourceItemsCount > int.MaxValue)
|
||||
{
|
||||
ThrowArgumentOutOfRangeException($"The number of items exceeds the maximum capacity of {nameof(RecyclableList<T>)}, equal {int.MaxValue}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList<T>)}, instead");
|
||||
}
|
||||
|
||||
if (_count + sourceItemsCount > int.MaxValue)
|
||||
{
|
||||
ThrowArgumentOutOfRangeException($"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 = _count + (int)sourceItemsCount;
|
||||
if (_capacity < targetCapacity)
|
||||
{
|
||||
_ = EnsureCapacity(this, targetCapacity);
|
||||
}
|
||||
|
||||
Span<T> targetSpan = new Span<T>(_memoryBlock)[_count..],
|
||||
itemsSpan;
|
||||
|
||||
int blockIndex,
|
||||
sourceBlockSize = items.BlockSize,
|
||||
lastBlockIndex = (int)(sourceItemsCount / sourceBlockSize) - 1;
|
||||
|
||||
for (blockIndex = 0; blockIndex <= lastBlockIndex; blockIndex++)
|
||||
{
|
||||
itemsSpan = new(items.AsArray[blockIndex], 0, sourceBlockSize);
|
||||
itemsSpan.CopyTo(targetSpan);
|
||||
targetSpan = targetSpan[sourceBlockSize..];
|
||||
}
|
||||
|
||||
if (blockIndex == 0)
|
||||
{
|
||||
itemsSpan = new(items.AsArray[blockIndex], 0, (int)sourceItemsCount);
|
||||
itemsSpan.CopyTo(targetSpan);
|
||||
}
|
||||
else if (blockIndex <= items.LastBlockWithData)
|
||||
{
|
||||
itemsSpan = new(items.AsArray[blockIndex], 0, items.NextItemIndex);
|
||||
itemsSpan.CopyTo(targetSpan);
|
||||
}
|
||||
|
||||
_count = targetCapacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(IEnumerable<T> source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
||||
{
|
||||
if (source is T[] sourceArray)
|
||||
{
|
||||
AddRange(sourceArray);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source is List<T> sourceList)
|
||||
{
|
||||
AddRange(sourceList);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source is RecyclableList<T> sourceRecyclableList)
|
||||
{
|
||||
AddRange(sourceRecyclableList);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source is IList<T> sourceIList)
|
||||
{
|
||||
AddRange(sourceIList);
|
||||
return;
|
||||
}
|
||||
|
||||
int targetItemIdx = _count;
|
||||
Span<T> memorySpan;
|
||||
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
||||
{
|
||||
_ = EnsureCapacity(this, targetItemIdx + requiredAdditionalCapacity);
|
||||
memorySpan = new(_memoryBlock);
|
||||
foreach (var item in source)
|
||||
{
|
||||
memorySpan[targetItemIdx++] = item;
|
||||
}
|
||||
|
||||
_count = targetItemIdx;
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
using var enumerator = source.GetEnumerator();
|
||||
|
||||
int capacity = _capacity;
|
||||
memorySpan = new(_memoryBlock);
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
int available = capacity - targetItemIdx;
|
||||
do
|
||||
{
|
||||
if (targetItemIdx + growByCount > capacity)
|
||||
{
|
||||
capacity = EnsureCapacity(this, capacity + growByCount);
|
||||
memorySpan = new(_memoryBlock);
|
||||
available = capacity - targetItemIdx;
|
||||
}
|
||||
|
||||
for (i = 0; i < available; i++)
|
||||
{
|
||||
memorySpan[targetItemIdx++] = enumerator.Current;
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (i >= available);
|
||||
}
|
||||
|
||||
_count = targetItemIdx;
|
||||
}
|
||||
|
||||
public T[] AsArray => _memoryBlock;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Clear()
|
||||
{
|
||||
try
|
||||
if (NeedsClearing)
|
||||
{
|
||||
// Remove in reversed order for performance savings
|
||||
while (_arrays.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
RemoveBlock(_arrays.Count - 1);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// We want to try returning as many arrays, as possible, before
|
||||
// the list is cleared.
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
LongCount = 0;
|
||||
_arrays.Clear();
|
||||
Array.Clear(_memoryBlock, 0, _count);
|
||||
}
|
||||
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public bool Contains(T item) => _arrays.Contains(item);
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) => _arrays.CopyTo(0, BlockSize, (int)LongCount % BlockSize, array, arrayIndex);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IEnumerator<T> GetEnumerator() => _arrays.Enumerate(BlockSize, LongCount).GetEnumerator();
|
||||
public bool Contains(T item) => _count > 0 && Array.IndexOf(_memoryBlock, item, 0, _count) >= 0;
|
||||
|
||||
public int IndexOf(T item) => (int)_arrays.LongIndexOf(BlockSize, item, _equalityComparer);
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void CopyTo(T[] array, int arrayIndex) => Array.Copy(_memoryBlock, 0, array, arrayIndex, _count);
|
||||
|
||||
public void Insert(int index, T item) => throw new NotSupportedException();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public long LongIndexOf(T item) => _arrays.LongIndexOf(BlockSize, item, _equalityComparer);
|
||||
|
||||
public bool Remove(T item) => throw new NotSupportedException();
|
||||
|
||||
public void RemoveAt(int index) => throw new NotSupportedException();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _arrays.Enumerate(BlockSize, LongCount).GetEnumerator();
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
protected static IEnumerable<T> Enumerate(RecyclableList<T> list)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
int count = list._count;
|
||||
var memory = list._memoryBlock;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
yield return memory[i];
|
||||
}
|
||||
}
|
||||
|
||||
// override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
||||
// ~RecyclableList()
|
||||
// {
|
||||
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
// Dispose(disposing: false);
|
||||
// }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(T itemToFind) => _count > 0 ? Array.IndexOf(_memoryBlock, itemToFind, 0, _count) : RecyclableDefaults.ItemNotFoundIndex;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
int oldCount = _count;
|
||||
int requestedCapacity = oldCount + 1;
|
||||
if (_capacity < requestedCapacity)
|
||||
{
|
||||
_ = EnsureCapacity(this, requestedCapacity);
|
||||
}
|
||||
|
||||
if (oldCount > 0)
|
||||
{
|
||||
var sourceSpan = new Span<T>(_memoryBlock, index, oldCount);
|
||||
var targetSpan = new Span<T>(_memoryBlock, index + 1, oldCount);
|
||||
sourceSpan.CopyTo(targetSpan);
|
||||
}
|
||||
|
||||
_memoryBlock[index] = item;
|
||||
_count++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public bool Remove(T itemToRemove)
|
||||
{
|
||||
var index = IndexOf(itemToRemove);
|
||||
if (index >= 0)
|
||||
{
|
||||
_count--;
|
||||
if (index < _count)
|
||||
{
|
||||
Array.Copy(_memoryBlock, index + 1, _memoryBlock, index, _count - index);
|
||||
}
|
||||
|
||||
if (NeedsClearing)
|
||||
{
|
||||
#pragma warning disable CS8601 // In real use cases we'll never access it
|
||||
_memoryBlock[_count] = default;
|
||||
#pragma warning restore CS8601
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index >= _count)
|
||||
{
|
||||
ThrowArgumentOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range");
|
||||
}
|
||||
|
||||
_count--;
|
||||
if (index < _count)
|
||||
{
|
||||
Array.Copy(_memoryBlock, index + 1, _memoryBlock, index, _count - index);
|
||||
}
|
||||
|
||||
if (NeedsClearing)
|
||||
{
|
||||
#pragma warning disable CS8601 // In real use cases we'll never access it
|
||||
_memoryBlock[_count] = default;
|
||||
#pragma warning restore CS8601
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IEnumerator<T> GetEnumerator() => Enumerate(this).GetEnumerator();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
IEnumerator IEnumerable.GetEnumerator() => Enumerate(this).GetEnumerator();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void RemoveBlock(int index)
|
||||
{
|
||||
try
|
||||
if (_capacity > 0)
|
||||
{
|
||||
_arrays[index].ReturnToPool();
|
||||
_arrays.RemoveAt(index);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Capacity -= BlockSize;
|
||||
LongCount -= BlockSize;
|
||||
Clear();
|
||||
_capacity = 0;
|
||||
_memoryBlock.ReturnToPool(_arrayPool, NeedsClearing);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public static class RecyclableListExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CopyTo<T>(this List<T[]> arrays, long startingIndex, int blockSize, int lastBlockSize, T[] destinationArray, int destinationArrayIndex)
|
||||
{
|
||||
Span<T> arrayMemory = destinationArray.AsSpan();
|
||||
arrayMemory = arrayMemory[destinationArrayIndex..];
|
||||
int startingArrayIndex = (int)(startingIndex / blockSize);
|
||||
startingIndex %= blockSize;
|
||||
if (arrays.Count > 0)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[0].AsSpan();
|
||||
var maxToCopy = (int)Math.Min(blockSize - startingIndex, arrayMemory.Length);
|
||||
if (arrays.Count == 1)
|
||||
{
|
||||
maxToCopy = (int)Math.Min(maxToCopy, lastBlockSize);
|
||||
}
|
||||
|
||||
sourceMemory = sourceMemory[(int)startingIndex..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
arrayMemory = arrayMemory[maxToCopy..];
|
||||
}
|
||||
|
||||
for (var arrayIdx = startingArrayIndex + 1; arrayIdx < arrays.Count - 1 && arrayMemory.Length > 0; arrayIdx++)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[arrayIdx].AsSpan();
|
||||
var maxToCopy = Math.Min(blockSize, arrayMemory.Length);
|
||||
sourceMemory = sourceMemory[..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
arrayMemory = arrayMemory[maxToCopy..];
|
||||
}
|
||||
|
||||
if (arrays.Count > 1 && arrayMemory.Length > 0)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[^1].AsSpan();
|
||||
var maxToCopy = Math.Min(lastBlockSize, arrayMemory.Length);
|
||||
sourceMemory = sourceMemory[..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Contains<T>(this List<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IEnumerable<T> Enumerate<T>(this List<T[]> arrays, int chunkSize, long totalCount)
|
||||
{
|
||||
long currentCount = 0;
|
||||
for (var arrayIdx = 0; arrayIdx < arrays.Count; arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
currentCount += chunkSize;
|
||||
switch (currentCount < totalCount)
|
||||
{
|
||||
case true:
|
||||
for (var valueIdx = 0; valueIdx < chunkSize; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case false:
|
||||
var partialCount = (int)(totalCount % chunkSize);
|
||||
int maxCount = partialCount > 0 ? partialCount : chunkSize;
|
||||
for (var valueIdx = 0; valueIdx < maxCount; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T LimitTo<T>(this T value, T maxValue, IComparer<T> comparer)
|
||||
=> comparer.Compare(value, maxValue) > 0 ? maxValue : value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long LongIndexOf<T>(this List<T[]> arrays, int blockSize, T item, IEqualityComparer<T> comparer)
|
||||
{
|
||||
for (var arrayIdx = 0; arrayIdx < arrays.Count; arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
Span<T> arrayMemory = array.AsSpan();
|
||||
for (int memoryIdx = 0; memoryIdx < arrayMemory.Length; memoryIdx++)
|
||||
{
|
||||
if (comparer.Equals(arrayMemory[memoryIdx], item))
|
||||
{
|
||||
return (arrayIdx * (long)blockSize) + memoryIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ToArrayIndex(this long index, int blockSize) => (int)(index / blockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long ToItemIndex(this long index, int blockSize) => index % blockSize;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this IList<T> values, int blockSize = RecyclableDefaults.BlockSize) => new(values, blockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this IEnumerable<T> values, int blockSize = RecyclableDefaults.BlockSize) => new(values, blockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T[] RentArrayFromPool<T>(this int minSize) => ArrayPool<T>.Shared.Rent(minSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ReturnToPool<T>(this T[] array) => ArrayPool<T>.Shared.Return(array);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetItem<T>(this List<T[]> arrays, in int blockSize, in long index, in T value)
|
||||
=> arrays[(int)(index / blockSize)][index % blockSize] = value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T GetItem<T>(this List<T[]> arrays, in int blockSize, in long index)
|
||||
=> arrays[(int)(index / blockSize)][index % blockSize];
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
// #define MAX_BLOCKS_FOR_TASK_2
|
||||
// #define MAX_BLOCKS_FOR_TASK_3
|
||||
#define MAX_BLOCKS_FOR_TASK_4
|
||||
// #define MAX_BLOCKS_FOR_TASK_5
|
||||
// #define MAX_BLOCKS_FOR_TASK_6
|
||||
// #define MAX_BLOCKS_FOR_TASK_7
|
||||
// #define MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using Recyclable.Collections.Parallel;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public partial class RecyclableLongList<T>
|
||||
{
|
||||
public static class IndexOfHelpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void IndexOfInConsecutiveBlocks(IndexOfSynchronizationContext context, RecyclableLongList<T> list, int blockIndex, T itemToFind)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (context._isItemFound && blockIndex >= context.FoundBlockIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long index = Array.IndexOf(list._memoryBlocks[blockIndex], itemToFind, 0, list._blockSize);
|
||||
if (index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex, ((long)blockIndex << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
else if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_2 || MAX_BLOCKS_FOR_TASK_3 || MAX_BLOCKS_FOR_TASK_4 || MAX_BLOCKS_FOR_TASK_5 || MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 1], itemToFind, 0, list._blockSize);
|
||||
if (index >= 0)
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 1, ((long)(blockIndex + 1) << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
else if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_3 || MAX_BLOCKS_FOR_TASK_4 || MAX_BLOCKS_FOR_TASK_5 || MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 2], itemToFind, 0, list._blockSize);
|
||||
if (index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 2, ((long)(blockIndex + 2) << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
else if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_4 || MAX_BLOCKS_FOR_TASK_5 || MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 3], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0)
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 3, ((long)(blockIndex + 3) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_5 || MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 4], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 4, ((long)(blockIndex + 4) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 5], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 5, ((long)(blockIndex + 5) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 6], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 6, ((long)(blockIndex + 6) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 7], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 7, ((long)(blockIndex + 7) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.Data.Add("list", list);
|
||||
e.Data.Add("itemToFind", itemToFind);
|
||||
e.Data.Add("blockIndex", blockIndex);
|
||||
_ = Interlocked.Exchange(ref context.Exception, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.RemoveParticipant();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void ExchangeFoundItemIndexIfLower(IndexOfSynchronizationContext context, int blockIndex, long newItemIndex)
|
||||
{
|
||||
context.Lock();
|
||||
|
||||
if (newItemIndex < context.FoundItemIndex || context.FoundItemIndex == RecyclableDefaults.ItemNotFoundIndexLong)
|
||||
{
|
||||
context.FoundBlockIndex = blockIndex;
|
||||
context.FoundItemIndex = newItemIndex;
|
||||
}
|
||||
|
||||
context._isItemFound = true;
|
||||
context.Unlock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void IndexOfIn1Block(IndexOfSynchronizationContext context, RecyclableLongList<T> list, int blockIndex, T itemToFind)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long index = Array.IndexOf(list._memoryBlocks[blockIndex], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0)
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex, ((long)blockIndex << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ = Interlocked.Exchange(ref context.Exception, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.RemoveParticipant();
|
||||
}
|
||||
}
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_2
|
||||
private const int BlocksForEachTask = 2;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_3
|
||||
private const int BlocksForEachTask = 3;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_4
|
||||
private const int BlocksForEachTask = 4;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_5
|
||||
private const int BlocksForEachTask = 5;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_6
|
||||
private const int BlocksForEachTask = 6;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_7
|
||||
private const int BlocksForEachTask = 7;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_8
|
||||
private const int BlocksForEachTask = 8;
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static long IndexOfParallel(RecyclableLongList<T> list, T item)
|
||||
{
|
||||
// & WAS SLOWER
|
||||
//long itemsCount = Array.IndexOf(list._memoryBlocks[0], item, 0, (int)Math.Min(list._blockSize, list._longCount));
|
||||
//if (itemsCount >= 0)
|
||||
//{
|
||||
// return itemsCount;
|
||||
//}
|
||||
|
||||
//using var context = new ParallelSynchronizationContext(1);
|
||||
IndexOfSynchronizationContext context = IndexOfSynchronizationContextPool.GetWithOneParticipant();
|
||||
// (long)(_longCount * 0.329) => most efficient step, based on benchmarks
|
||||
//Iterate(this, context, item);
|
||||
|
||||
// & WAS SLOWER without
|
||||
int blockIndex = 2;
|
||||
// & WAS SLOWER
|
||||
//long itemsCount = list._longCount - list._blockSize;
|
||||
// & WAS SLOWER
|
||||
// int blockSize = _blockSize;
|
||||
// & WAS SLOWER
|
||||
// while ((blockIndex << list._blockSizePow2BitShift) + itemIndex > step)
|
||||
//long blockSize = list._blockSize << 1;
|
||||
int lastBlockWithData = list._lastBlockWithData - BlocksForEachTask;
|
||||
while (blockIndex < lastBlockWithData)
|
||||
{
|
||||
// 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 < list._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 = Array.IndexOf(list._memoryBlocks[list._lastBlockWithData], item, 0, list._nextItemIndex > 0 ? list._nextItemIndex : list._blockSize);
|
||||
if (!context._isItemFound && itemIndex >= 0)
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex, ((long)blockIndex << 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 IndexOfSequential(RecyclableLongList<T> list, in T item) //, in T[][] memoryBlocks, int lastBlockIndex, int blockSize, int nextItemIndex)
|
||||
{
|
||||
int itemIndex;
|
||||
// & WAS SLOWER
|
||||
//int lastBlockWithData = list._lastBlockWithData;
|
||||
Span<T[]> memoryBlocksSpan = new(list._memoryBlocks, 0, list._lastBlockWithData + 1);
|
||||
int blockIndex;
|
||||
for (blockIndex = 1; blockIndex < list._lastBlockWithData; blockIndex += 4)
|
||||
{
|
||||
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, list._blockSize);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
return itemIndex + ((long)blockIndex << list._blockSizePow2BitShift);
|
||||
}
|
||||
|
||||
if (blockIndex + 1 < list._lastBlockWithData)
|
||||
{
|
||||
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 1], item, 0, list._blockSize);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
return itemIndex + ((long)(blockIndex + 1)<< list._blockSizePow2BitShift);
|
||||
}
|
||||
|
||||
if (blockIndex + 2 < list._lastBlockWithData)
|
||||
{
|
||||
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 2], item, 0, list._blockSize);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
return itemIndex + ((long)(blockIndex + 2)<< list._blockSizePow2BitShift);
|
||||
}
|
||||
|
||||
if (blockIndex + 3 < list._lastBlockWithData)
|
||||
{
|
||||
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 3], item, 0, list._blockSize);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
return itemIndex + ((long)(blockIndex + 3)<< list._blockSizePow2BitShift);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
itemIndex = list._nextItemIndex != 0
|
||||
? Array.IndexOf(memoryBlocksSpan[list._lastBlockWithData], item, 0, list._nextItemIndex)
|
||||
: Array.IndexOf(memoryBlocksSpan[list._lastBlockWithData], item, 0, list._blockSize);
|
||||
|
||||
return itemIndex >= 0 ? itemIndex + ((long)list._lastBlockWithData << list._blockSizePow2BitShift) : RecyclableDefaults.ItemNotFoundIndexLong;
|
||||
}
|
||||
|
||||
public static int Min(int a, int b, int c) => a < b ? a < c ? a : c : b < c ? b : c;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -4,28 +4,61 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public class RecyclableQueue<T> : IRecyclableOwner<T>, IList<T>, IDisposable
|
||||
internal class RecyclableQueue<T> : IRecyclableOwner<T>, IList<T>, IDisposable
|
||||
{
|
||||
private static readonly ArrayPool<T> _arrayPool = ArrayPool<T>.Create();
|
||||
private static readonly IEqualityComparer<T> _equalityComparer = EqualityComparer<T>.Default;
|
||||
private static readonly IComparer<long> _comparer = Comparer<long>.Default;
|
||||
|
||||
private bool _disposedValue;
|
||||
private RecyclableList<T[]> Memory { get; }
|
||||
|
||||
protected List<T[]> Arrays { get; }
|
||||
protected long GetAbsoluteIndex(long index) => index + RemovedCount;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static T Dequeue(RecyclableQueue<T> queue)
|
||||
{
|
||||
if (queue.LongCount <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(queue), "There are no items in the queue");
|
||||
}
|
||||
|
||||
var toRemove = queue[0];
|
||||
queue.RemovedCount++;
|
||||
queue.LongCount--;
|
||||
if (queue.Capacity - queue.LongCount == queue.BlockSize)
|
||||
{
|
||||
queue.RemoveBlock(0);
|
||||
queue.RemovedCount -= queue.BlockSize;
|
||||
}
|
||||
|
||||
return toRemove;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool TryDequeue(RecyclableQueue<T> queue, out T? item)
|
||||
{
|
||||
if (queue.LongCount > 0)
|
||||
{
|
||||
item = queue.Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
item = default;
|
||||
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; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static T[] RentArrayFromPool(int minSize) => ArrayPool<T>.Shared.Rent(minSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ReturnToPool(T[] array) => ArrayPool<T>.Shared.Return(array);
|
||||
|
||||
public RecyclableQueue(IEnumerable<T> source, int blockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
BlockSize = blockSize;
|
||||
Arrays = new();
|
||||
Memory = new(1);
|
||||
foreach (var item in source)
|
||||
{
|
||||
Enqueue(item);
|
||||
@@ -35,7 +68,7 @@ namespace Recyclable.Collections
|
||||
public RecyclableQueue(int blockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
BlockSize = blockSize;
|
||||
Arrays = new();
|
||||
Memory = new(1);
|
||||
}
|
||||
|
||||
public int BlockSize { get; protected set; }
|
||||
@@ -43,31 +76,47 @@ namespace Recyclable.Collections
|
||||
|
||||
public int Count
|
||||
{
|
||||
get => (int)LongCount.LimitTo(int.MaxValue, _comparer);
|
||||
get => (int)LongCount;
|
||||
set => LongCount = value;
|
||||
}
|
||||
|
||||
public long LongCount { get; set; }
|
||||
public bool IsReadOnly { get; } = false;
|
||||
public bool IsReadOnly { get; }
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => GetItem(index);
|
||||
set => SetItem(index, value);
|
||||
}
|
||||
|
||||
public T this[int index] { get => this[(long)index]; set => this[(long)index] = value; }
|
||||
public T this[long index]
|
||||
{
|
||||
get
|
||||
{
|
||||
long absoluteIndex = GetAbsoluteIndex(index);
|
||||
int arrayIndex = absoluteIndex.ToArrayIndex(BlockSize);
|
||||
long itemIndex = absoluteIndex.ToItemIndex(BlockSize);
|
||||
return Arrays[arrayIndex][itemIndex];
|
||||
}
|
||||
get => GetItem(index);
|
||||
set => SetItem(index, value);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
long absoluteIndex = GetAbsoluteIndex(index);
|
||||
int arrayIndex = absoluteIndex.ToArrayIndex(BlockSize);
|
||||
long itemIndex = absoluteIndex.ToItemIndex(BlockSize);
|
||||
Arrays[arrayIndex][itemIndex] = value;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ToArrayIndex(long index, int blockSize) => (int)(index / blockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long ToItemIndex(long index, int blockSize) => index % blockSize;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SetItem(long index, T value)
|
||||
{
|
||||
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
|
||||
int arrayIndex = ToArrayIndex(absoluteIndex, BlockSize);
|
||||
long itemIndex = ToItemIndex(absoluteIndex, BlockSize);
|
||||
Memory[arrayIndex][itemIndex] = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private T GetItem(long index)
|
||||
{
|
||||
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
|
||||
int arrayIndex = ToArrayIndex(absoluteIndex, BlockSize);
|
||||
long itemIndex = ToItemIndex(absoluteIndex, BlockSize);
|
||||
return Memory[arrayIndex][itemIndex];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -75,33 +124,22 @@ namespace Recyclable.Collections
|
||||
{
|
||||
if (LongCount == Capacity)
|
||||
{
|
||||
T[] newArray = BlockSize.RentArrayFromPool<T>();
|
||||
Arrays.Add(newArray);
|
||||
Capacity += BlockSize;
|
||||
AddBlock();
|
||||
}
|
||||
|
||||
var newIndex = LongCount;
|
||||
// We don't want this[newIndex] set to raise the update event, because
|
||||
// the count would be wrong. We need to increase it, first.
|
||||
this[newIndex] = item;
|
||||
SetItem(LongCount, item);
|
||||
LongCount++;
|
||||
}
|
||||
|
||||
|
||||
public T Dequeue()
|
||||
public void AddBlock()
|
||||
{
|
||||
var toRemove = this[0];
|
||||
RemovedCount++;
|
||||
LongCount--;
|
||||
if (Capacity - LongCount == BlockSize)
|
||||
{
|
||||
RemoveBlock(0);
|
||||
RemovedCount -= BlockSize;
|
||||
}
|
||||
|
||||
return toRemove;
|
||||
T[] newArray = BlockSize.RentArrayFromPool<T>(_arrayPool);
|
||||
Memory.Add(newArray);
|
||||
Capacity += BlockSize;
|
||||
}
|
||||
|
||||
public T Dequeue() => Dequeue(this);
|
||||
|
||||
public void Add(T item) => Enqueue(item);
|
||||
|
||||
public void Clear()
|
||||
@@ -109,11 +147,11 @@ namespace Recyclable.Collections
|
||||
try
|
||||
{
|
||||
// Remove in reversed order for performance savings
|
||||
while (Arrays.Count > 0)
|
||||
while (Memory.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
RemoveBlock(Arrays.Count - 1);
|
||||
RemoveBlock(Memory.Count - 1);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -126,62 +164,43 @@ namespace Recyclable.Collections
|
||||
{
|
||||
LongCount = 0;
|
||||
RemovedCount = 0;
|
||||
Arrays.Clear();
|
||||
Memory.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(T item) => Arrays.Contains(item);
|
||||
public void CopyTo(T[] array, int arrayIndex) => Arrays.CopyTo(RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IEnumerable<T> Enumerate() => Arrays.Enumerate(BlockSize, LongCount);
|
||||
public IEnumerator<T> GetEnumerator() => Enumerate().GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public int IndexOf(T item) => (int)LongIndexOf(item).LimitTo(int.MaxValue, _comparer);
|
||||
public long LongIndexOf(T item) => GetRelativeIndex(Arrays.LongIndexOf(BlockSize, item, _equalityComparer));
|
||||
public bool Contains(T item) => Memory.Contains(item);
|
||||
public void CopyTo(T[] array, int arrayIndex) => Memory.CopyTo(RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
|
||||
public IEnumerable<T> Enumerate() => Memory.Enumerate(BlockSize, LongCount);
|
||||
public IEnumerator<T> GetEnumerator() => Memory.Enumerate(BlockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => Memory.Enumerate(BlockSize, LongCount).GetEnumerator();
|
||||
public int IndexOf(T item) => (int)LongIndexOf(item);
|
||||
public long LongIndexOf(T item) => GetRelativeIndex(Memory.LongIndexOf(BlockSize, item, _equalityComparer));
|
||||
public void Insert(int index, T item) => throw new NotSupportedException();
|
||||
public void RemoveAt(int index) => throw new NotSupportedException();
|
||||
|
||||
public bool TryDequeue(out T? item)
|
||||
{
|
||||
if (LongCount > 0)
|
||||
{
|
||||
item = Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
item = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Remove(T item) => 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)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReturnToPool(Arrays[index]);
|
||||
Arrays.RemoveAt(index);
|
||||
Memory[index].ReturnToPool(_arrayPool, NeedsClearing);
|
||||
Memory.RemoveAt(index);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Capacity -= BlockSize;
|
||||
LongCount -= BlockSize;
|
||||
//LongCount -= BlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
Clear();
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public class RecyclableSortedList<TKey, TValue> : RecyclableList<(TKey Key, TValue Value)>
|
||||
internal class RecyclableSortedList<TKey, TValue> : RecyclableLongList<(TKey Key, TValue Value)>
|
||||
where TKey : notnull
|
||||
{
|
||||
private static readonly IComparer<(TKey, TValue)> _comparer = new FrequencyTupleComparer<TKey, TValue>();
|
||||
@@ -27,7 +27,7 @@
|
||||
QuickSort();
|
||||
}
|
||||
|
||||
public RecyclableSortedList(int blockSize = RecyclableDefaults.BlockSize)
|
||||
public RecyclableSortedList(int blockSize = RecyclableDefaults.BlockSize)
|
||||
: base(blockSize)
|
||||
{
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
public void BeginUpdate() => _isUpdating++;
|
||||
|
||||
public void Clear()
|
||||
public new void Clear()
|
||||
{
|
||||
BeginUpdate();
|
||||
try
|
||||
@@ -73,7 +73,7 @@
|
||||
finally
|
||||
{
|
||||
EndUpdate(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public class RecyclableStack<T> : IList<T>, IDisposable
|
||||
internal class RecyclableStack<T> : IList<T>, IDisposable
|
||||
{
|
||||
private bool _disposedValue;
|
||||
private readonly int _blockSize;
|
||||
|
||||
protected RecyclableList<T> List { get; }
|
||||
protected RecyclableLongList<T> List { get; }
|
||||
public int Count => List.Count;
|
||||
public long LongCount
|
||||
{
|
||||
@@ -32,7 +32,7 @@ namespace Recyclable.Collections
|
||||
List = new(list, blockSize);
|
||||
}
|
||||
|
||||
public RecyclableStack(RecyclableList<T> list, int blockSize = RecyclableDefaults.BlockSize)
|
||||
public RecyclableStack(RecyclableLongList<T> list, int blockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
_blockSize = blockSize;
|
||||
List = new(list, blockSize);
|
||||
@@ -47,9 +47,9 @@ namespace Recyclable.Collections
|
||||
{
|
||||
var toRemove = List[LongCount - 1];
|
||||
LongCount--;
|
||||
if (List.Capacity - LongCount == _blockSize)
|
||||
if ((List.Capacity * _blockSize) - LongCount == _blockSize)
|
||||
{
|
||||
List.RemoveBlock(List.BlockCount - 1);
|
||||
List.RemoveBlock(List.ReservedBlockCount - 1);
|
||||
}
|
||||
|
||||
return toRemove;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
{
|
||||
public class CustomIList<T> : IList<T>
|
||||
{
|
||||
protected List<T> _list;
|
||||
|
||||
public CustomIList()
|
||||
{
|
||||
_list = new List<T>();
|
||||
}
|
||||
|
||||
public CustomIList(IEnumerable<T> items)
|
||||
{
|
||||
_list = new List<T>(items);
|
||||
}
|
||||
|
||||
public T this[int index] { get => _list[index]; set => _list[index] = value; }
|
||||
public int Count => _list.Count;
|
||||
public bool IsReadOnly => false;
|
||||
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 IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
|
||||
public int IndexOf(T item) => _list.IndexOf(item);
|
||||
public void Insert(int index, T item) => _list.Insert(index, item);
|
||||
public bool Remove(T item) => _list.Remove(item);
|
||||
public void RemoveAt(int index) => _list.RemoveAt(index);
|
||||
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
{
|
||||
public class EnumerableWithoutCount<T> : IEnumerable<T>
|
||||
{
|
||||
private readonly IEnumerable<T> _testData;
|
||||
|
||||
public EnumerableWithoutCount(IEnumerable<T> testData)
|
||||
{
|
||||
_testData = testData;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _testData.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _testData.GetEnumerator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using FluentAssertions;
|
||||
using Recyclable.Collections;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
{
|
||||
public class MathUtilsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(int.MaxValue / 1024, int.MaxValue / 10 / 1024)]
|
||||
public void DivMod(long testNumber, int blockSize)
|
||||
{
|
||||
for (var testCase = testNumber; testCase >= 0; testCase--)
|
||||
{
|
||||
// Prepare
|
||||
var expectedResult = testCase / blockSize;
|
||||
var expectedRemainder = testCase % blockSize;
|
||||
|
||||
// Act
|
||||
var actualResult = MathUtils.DivMod(testCase, blockSize, out long actualRemainder);
|
||||
|
||||
// Validate
|
||||
_ = actualResult.Should().Be(expectedResult);
|
||||
_ = actualRemainder.Should().Be(expectedRemainder);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, false)]
|
||||
[InlineData(1, true)]
|
||||
[InlineData(2, true)]
|
||||
[InlineData(3, false)]
|
||||
[InlineData(4, true)]
|
||||
[InlineData(5, false)]
|
||||
[InlineData(8, true)]
|
||||
[InlineData(16, true)]
|
||||
[InlineData(20, false)]
|
||||
[InlineData(32, true)]
|
||||
[InlineData(64, true)]
|
||||
[InlineData(128, true)]
|
||||
[InlineData(256, true)]
|
||||
[InlineData(512, true)]
|
||||
[InlineData(1024, true)]
|
||||
[InlineData(2048, true)]
|
||||
[InlineData(4096, true)]
|
||||
[InlineData(8192, true)]
|
||||
[InlineData(16384, true)]
|
||||
[InlineData(32768, true)]
|
||||
[InlineData(65536, true)]
|
||||
[InlineData(131072, true)]
|
||||
[InlineData(262144, true)]
|
||||
[InlineData(524288, true)]
|
||||
[InlineData(1048576, true)]
|
||||
[InlineData(2097152, true)]
|
||||
[InlineData(4194304, true)]
|
||||
[InlineData(8388608, true)]
|
||||
[InlineData(16777216, true)]
|
||||
[InlineData(33554432, true)]
|
||||
[InlineData(67108864, true)]
|
||||
[InlineData(134217728, true)]
|
||||
[InlineData(268435456, true)]
|
||||
[InlineData(536870912, true)]
|
||||
[InlineData(1073741824, true)]
|
||||
[InlineData(2147483648, true)]
|
||||
[InlineData(2147483649, false)]
|
||||
[InlineData(4294967296, true)]
|
||||
public void IsPow2(long testNumber, bool expected)
|
||||
{
|
||||
_ = MathUtils.IsPow2(testNumber).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-2, -1)]
|
||||
[InlineData(-1, -1)]
|
||||
[InlineData(0, -1)]
|
||||
[InlineData(1, 0)]
|
||||
[InlineData(2, 1)]
|
||||
[InlineData(3, -1)]
|
||||
[InlineData(4, 2)]
|
||||
[InlineData(8, 3)]
|
||||
public void GetPow2Shift(long testNumber, int expected)
|
||||
{
|
||||
_ = MathUtils.GetPow2Shift(testNumber).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,20 @@
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.8.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0">
|
||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
|
||||
<PackageReference Include="morelinq" Version="3.4.2" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -3,189 +3,549 @@ using Recyclable.Collections;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
{
|
||||
[TestClass()]
|
||||
public class RecyclableListTests
|
||||
{
|
||||
private const int _totalObjectCount = 5_000;
|
||||
private static RecyclableList<int> NewRecyclableList => CreateReversedRecyclableList(_testData);
|
||||
private static readonly IEnumerable<int> _testData = Enumerable.Range(1, _totalObjectCount);
|
||||
private static RecyclableList<int> CreateReversedRecyclableList(IEnumerable<int> source) => new(source);
|
||||
|
||||
[TestMethod]
|
||||
public void AddShouldAddItems()
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldAddItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<int>();
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
const int itemsCount = 5;
|
||||
foreach (var index in Enumerable.Range(1, itemsCount))
|
||||
if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
else if (testData is List<long> testDataList)
|
||||
{
|
||||
list.AddRange(testDataList);
|
||||
}
|
||||
else if (testData is RecyclableList<long> testDataRecyclableList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableList);
|
||||
}
|
||||
else if (testData is RecyclableLongList<long> testDataRecyclableLongList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableLongList);
|
||||
}
|
||||
else if (testData is IList<long> testDataIList)
|
||||
{
|
||||
list.AddRange(testDataIList);
|
||||
}
|
||||
else if (testData is IEnumerable<long> testDataIEnumerable)
|
||||
{
|
||||
list.AddRange(testDataIEnumerable);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidCastException("Unknown type of test data");
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount, "when capacity == 0, then we should allocate as much memory as needed, only");
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.EmptySourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldDoNothingWhenSourceIsEmpty(string testCase, IEnumerable<long> testData)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
else if (testData is List<long> testDataList)
|
||||
{
|
||||
list.AddRange(testDataList);
|
||||
}
|
||||
else if (testData is RecyclableList<long> testDataRecyclableList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableList);
|
||||
}
|
||||
else if (testData is RecyclableLongList<long> testDataRecyclableLongList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableLongList);
|
||||
}
|
||||
else if (testData is IList<long> testDataIList)
|
||||
{
|
||||
list.AddRange(testDataIList);
|
||||
}
|
||||
else if (testData is IEnumerable<long> testDataIEnumerable)
|
||||
{
|
||||
list.AddRange(testDataIEnumerable);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidCastException("Unknown type of test data");
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().Be(0);
|
||||
_ = list.Count.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldNotOverrideItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
else if (testData is List<long> testDataList)
|
||||
{
|
||||
list.AddRange(testDataList);
|
||||
list.AddRange(testDataList);
|
||||
}
|
||||
else if (testData is RecyclableList<long> testDataRecyclableList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableList);
|
||||
list.AddRange(testDataRecyclableList);
|
||||
}
|
||||
else if (testData is RecyclableLongList<long> testDataRecyclableLongList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableLongList);
|
||||
list.AddRange(testDataRecyclableLongList);
|
||||
}
|
||||
else if (testData is IList<long> testDataIList)
|
||||
{
|
||||
list.AddRange(testDataIList);
|
||||
list.AddRange(testDataIList);
|
||||
}
|
||||
else if (testData is IEnumerable<long> testDataIEnumerable)
|
||||
{
|
||||
list.AddRange(testDataIEnumerable);
|
||||
list.AddRange(testDataIEnumerable);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidCastException("Unknown type of test data");
|
||||
}
|
||||
|
||||
// Validate
|
||||
var expectedData = testData.Concat(testData).ToArray();
|
||||
|
||||
if (itemsCount > 0)
|
||||
{
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount * 2);
|
||||
}
|
||||
|
||||
_ = list.Count.Should().Be(expectedData.Length);
|
||||
_ = list.Should().ContainInConsecutiveOrder(expectedData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
testData = testData.Concat(testData).ToArray();
|
||||
|
||||
// Act
|
||||
using var list = new RecyclableList<long>();
|
||||
foreach (var index in testData)
|
||||
{
|
||||
list.Add(index);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount, "we added so many items");
|
||||
for (var index = 0; index < itemsCount; index++)
|
||||
{
|
||||
var actual = list[index];
|
||||
_ = actual.Should().Be(index + 1);
|
||||
}
|
||||
_ = list.Count.Should().Be((int)(itemsCount * 2));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddShouldAcceptDuplicates()
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddShouldAddItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
int[] testNumbers = new[] { 1, 2, 2, 3 };
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
using var list = new RecyclableList<int>();
|
||||
foreach (var index in testNumbers)
|
||||
foreach (var item in testData)
|
||||
{
|
||||
list.Add(item);
|
||||
_ = list.Count.Should().Be((int)item);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddShouldAddItemWhenAfterAddRange(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Act
|
||||
using var list = new RecyclableList<long>();
|
||||
list.AddRange(testData);
|
||||
foreach (var index in testData)
|
||||
{
|
||||
list.Add(index);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(testNumbers.Length);
|
||||
_ = list.Should().BeEquivalentTo(testNumbers);
|
||||
_ = list.Count.Should().Be((int)(itemsCount * 2));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Concat(testData));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ConstructorShouldAcceptDuplicates()
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ClearShouldRemoveAllItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
int[] testNumbers = new[] { 1, 2, 2, 3 };
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
|
||||
// Act
|
||||
using var list = new RecyclableList<int>(testNumbers);
|
||||
// Act
|
||||
list.Clear();
|
||||
list.Clear();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(testNumbers.Length);
|
||||
_ = list.Should().BeEquivalentTo(testNumbers);
|
||||
// Validate
|
||||
_ = list.Count.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void InitializeSortClearShouldSucceed()
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConsecutiveDisposeShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
var expectedList = _testData;
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
list.Dispose();
|
||||
|
||||
// Act
|
||||
using RecyclableList<int> list = NewRecyclableList;
|
||||
list.Dispose();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConstructorShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Act
|
||||
using var list = new RecyclableList<long>(testData.Concat(testData), initialCapacity: (int)itemsCount * 2);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)itemsCount * 2);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Concat(testData));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConstructorShouldAddItemsInCorrectOrderWhenSourceIsIEnumerable(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Act
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount, "when we succeed in taking count without enumerating, then we allocate as much memory as needed, only");
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConstructorSourceShouldInitializeList(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Act
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ContainsShouldFindAllItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act
|
||||
foreach (var item in testData)
|
||||
{
|
||||
// Validate
|
||||
_ = list.Contains(item).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContainsShouldNotFindAnythingWhenListEmpty()
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<int>();
|
||||
|
||||
// Validate
|
||||
_ = list.Contains(0).Should().BeFalse();
|
||||
_ = list.Contains(1).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ContainsShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act
|
||||
foreach (var item in testData)
|
||||
{
|
||||
// Validate
|
||||
_ = list.Contains(-item).Should().BeFalse();
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Contains(0).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void CopyToShouldCopyAllItemsInTheCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
long[] copiedItems = new long[itemsCount];
|
||||
|
||||
// Act
|
||||
list.CopyTo(copiedItems, 0);
|
||||
|
||||
// Validate
|
||||
_ = copiedItems.Length.Should().Be((int)itemsCount);
|
||||
_ = copiedItems.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void EnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
|
||||
// Act
|
||||
var yieldedItems = new List<long>((int)itemsCount);
|
||||
yieldedItems.AddRange(list);
|
||||
|
||||
// Validate
|
||||
_ = yieldedItems.Count.Should().Be((int)itemsCount);
|
||||
_ = yieldedItems.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IndexOfShouldNotFindAnythingWhenListEmpty()
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<int>();
|
||||
|
||||
// Validate
|
||||
_ = list.IndexOf(0).Should().Be(-1);
|
||||
_ = list.IndexOf(1).Should().Be(-1);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void IndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
// There is nothing to search for when our lists are empty
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act
|
||||
foreach (var item in testData)
|
||||
{
|
||||
// Validate
|
||||
_ = list.IndexOf(-item).Should().Be(-1);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.IndexOf(0).Should().Be(-1);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void IndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
|
||||
// Act & Validate
|
||||
foreach (var index in testData)
|
||||
{
|
||||
var actual = list.IndexOf(index);
|
||||
_ = actual.Should().Be((int)(index - 1));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Act
|
||||
using RecyclableList<long> list = new(testData);
|
||||
list.QuickSort();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(_totalObjectCount);
|
||||
_ = list.Should().Equal(expectedList);
|
||||
_ = expectedList.Should().AllSatisfy(expected => list.Contains(expected));
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Should().Equal(testData);
|
||||
|
||||
// Act
|
||||
list.Clear();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CopyToShouldCopyAllItems()
|
||||
{
|
||||
// Prepare
|
||||
using var list = NewRecyclableList;
|
||||
|
||||
// Act
|
||||
var actual = new int[list.LongCount];
|
||||
list.CopyTo(actual, 0);
|
||||
_ = actual.Should().BeEquivalentTo(_testData);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetEnumeratorShouldYieldAllItems()
|
||||
{
|
||||
// Prepare
|
||||
using var list = NewRecyclableList;
|
||||
_ = list.Count.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
|
||||
// Act
|
||||
var actual = new List<int>();
|
||||
var enumerator = list.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
list.AddRange(testData);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
|
||||
// Act
|
||||
foreach (var item in testData.Reverse())
|
||||
{
|
||||
actual.Add(enumerator.Current);
|
||||
_ = list.Remove(item).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void InsertAtTheBeginningShouldMoveItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
foreach (var item in testData)
|
||||
{
|
||||
list.Insert(0, item);
|
||||
_ = list.Count.Should().Be((int)item);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = actual.Should().BeEquivalentTo(_testData);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Reverse());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IndexOfShouldFindTheIndexes()
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveAtFromTheBeginningShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = NewRecyclableList;
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
|
||||
// Act & Validate
|
||||
foreach(var index in _testData)
|
||||
for (var deleted = 1; deleted <= itemsCount; deleted++)
|
||||
{
|
||||
var actual = list.IndexOf(index);
|
||||
_ = actual.Should().Be(index - 1);
|
||||
// Act
|
||||
list.RemoveAt(0);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount - deleted));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Skip(deleted));
|
||||
}
|
||||
|
||||
_ = list.Should().BeEmpty();
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveAtFromTheEndShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
|
||||
// Act & Validate
|
||||
for (var deleted = 1; deleted <= itemsCount; deleted++)
|
||||
{
|
||||
// Act
|
||||
list.RemoveAt(list.Count - 1);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount - deleted));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Take((int)(itemsCount - deleted)));
|
||||
}
|
||||
|
||||
_ = list.Should().BeEmpty();
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveFromTheBeginningShouldRemoveTheCorrectItem(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
|
||||
// Act & Validate
|
||||
for (var deleted = 1; deleted <= itemsCount; deleted++)
|
||||
{
|
||||
// Act
|
||||
_ = list.Remove(deleted).Should().BeTrue();
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount - deleted));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Skip(deleted));
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void InsertShouldRaiseNotSupportedException()
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveFromTheEndShouldRemoveTheCorrectItem(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = NewRecyclableList;
|
||||
// Act & Validate
|
||||
using var list = new RecyclableList<long>(testData);
|
||||
long deleted = 0;
|
||||
foreach (var item in testData.Reverse())
|
||||
{
|
||||
// Act
|
||||
_ = list.Remove(item).Should().BeTrue();
|
||||
deleted++;
|
||||
|
||||
// Act
|
||||
list.Insert(0, -1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void RemoveShouldRaiseNotSupportedException()
|
||||
{
|
||||
// Prepare
|
||||
using var list = NewRecyclableList;
|
||||
|
||||
// Act
|
||||
bool removed = list.Remove(_testData.First());
|
||||
_ = removed.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void RemoveAtShouldRaiseNotSupportedException()
|
||||
{
|
||||
// Prepare
|
||||
using var list = NewRecyclableList;
|
||||
|
||||
// Act
|
||||
list.RemoveAt(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DisposeShouldSucceed()
|
||||
{
|
||||
// Prepare
|
||||
using var list = NewRecyclableList;
|
||||
|
||||
// Act
|
||||
list.Dispose();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(0L);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ConsecutiveDisposeShouldSucceed()
|
||||
{
|
||||
// Prepare
|
||||
using var list = NewRecyclableList;
|
||||
list.Dispose();
|
||||
|
||||
// Act
|
||||
list.Dispose();
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount - deleted));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Take((int)(itemsCount - deleted)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using Recyclable.Collections;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
{
|
||||
public static class RecyclableLongListTestData
|
||||
{
|
||||
private static IEnumerable<long> DoCreateTestData(long itemsCount)
|
||||
{
|
||||
for (long index = 1; index <= itemsCount; index++)
|
||||
{
|
||||
yield return index;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<long> CreateTestData(long itemsCount) => DoCreateTestData(itemsCount).ToArray();
|
||||
|
||||
public static readonly IEnumerable<long> ItemsCountVariants = new long[]
|
||||
{
|
||||
0, 1, 2, 3, 10, 16, 32, 64, 128, 256, 333, 512, 1_024, 8_192, 16_384,
|
||||
100_000
|
||||
};
|
||||
|
||||
public static readonly IEnumerable<int> BlockSizeVariants = new int[]
|
||||
{
|
||||
1, 2, 4, 8, 10, 16, 32, 64, 128, 256, 512, 1_024, 2_048, 4_096,
|
||||
8_192, 16_384
|
||||
};
|
||||
|
||||
private static IEnumerable<object[]> GetTargetDataVariants()
|
||||
{
|
||||
foreach (var itemsCount in ItemsCountVariants)
|
||||
{
|
||||
foreach (var targetBlockSize in BlockSizeVariants)
|
||||
{
|
||||
yield return new object[] { itemsCount, targetBlockSize };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> TargetDataVariants => GetTargetDataVariants().ToArray();
|
||||
|
||||
private static IEnumerable<object[]> CreateSourceDataVariants()
|
||||
{
|
||||
foreach (var itemsCount in ItemsCountVariants)
|
||||
{
|
||||
yield return new object[] { $"int[{itemsCount}]", CreateTestData(itemsCount).ToArray(), itemsCount };
|
||||
yield return new object[] { $"List<int>(itemsCount: {itemsCount}", CreateTestData(itemsCount).ToList(), itemsCount };
|
||||
yield return new object[] { $"RecyclableList<int>(itemsCount: {itemsCount})", CreateTestData(itemsCount).ToRecyclableList(), itemsCount };
|
||||
foreach (var sourceBlockSize in BlockSizeVariants)
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
$"RecyclableLongList<int>(itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize})",
|
||||
CreateTestData(itemsCount).ToRecyclableLongList(sourceBlockSize),
|
||||
itemsCount
|
||||
};
|
||||
}
|
||||
|
||||
yield return new object[] { $"IList<int>(itemsCount: {itemsCount})", new CustomIList<long>(CreateTestData(itemsCount)), itemsCount };
|
||||
yield return new object[] { $"IEnumerable<int>(itemsCount: {itemsCount}) with non-enumerated count", CreateTestData(itemsCount), itemsCount };
|
||||
yield return new object[] { $"IEnumerable<int>(itemsCount: {itemsCount}) without non-enumerated count", new EnumerableWithoutCount<long>(CreateTestData(itemsCount)), itemsCount };
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> SourceDataVariants => CreateSourceDataVariants().ToArray();
|
||||
|
||||
private static IEnumerable<object[]> CreateSourceEmptyDataVariants() => new[]
|
||||
{
|
||||
new object[] { "int[]", Array.Empty<long>() },
|
||||
new object[] { "List<int>", Array.Empty<long>().ToList() },
|
||||
new object[] { "RecyclableList<int>", Array.Empty<long>().ToRecyclableList() },
|
||||
new object[] { "RecyclableLongList<int>", Array.Empty<long>().ToRecyclableLongList() },
|
||||
new object[] { "IList<int>", new CustomIList<long>(Array.Empty<long>()) },
|
||||
new object[] { "IEnumerable<int> with non-enumerated count", Array.Empty<long>().AsEnumerable() },
|
||||
new object[] { "IEnumerable<int> without non-enumerated count", new EnumerableWithoutCount<long>(Array.Empty<long>()) }
|
||||
};
|
||||
|
||||
public static IEnumerable<object[]> EmptySourceDataVariants => CreateSourceEmptyDataVariants().ToArray();
|
||||
|
||||
private static IEnumerable<object[]> CreateSourceTargetDataVariants()
|
||||
{
|
||||
foreach (var testData in SourceDataVariants)
|
||||
{
|
||||
foreach (var targetBlockSize in BlockSizeVariants)
|
||||
{
|
||||
yield return new object[] { testData[0], testData[1], testData[2], targetBlockSize };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> SourceTargetDataVariants => CreateSourceTargetDataVariants().ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,662 @@
|
||||
using FluentAssertions;
|
||||
using Recyclable.Collections;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
{
|
||||
public class RecyclableLongListTests
|
||||
{
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldAddItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
else if (testData is List<long> testDataList)
|
||||
{
|
||||
list.AddRange(testDataList);
|
||||
}
|
||||
else if (testData is RecyclableList<long> testDataRecyclableList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableList);
|
||||
}
|
||||
else if (testData is RecyclableLongList<long> testDataRecyclableLongList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableLongList);
|
||||
}
|
||||
else if (testData is IList<long> testDataIList)
|
||||
{
|
||||
list.AddRange(testDataIList);
|
||||
}
|
||||
else if (testData is IEnumerable<long> testDataIEnumerable)
|
||||
{
|
||||
list.AddRange(testDataIEnumerable);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidCastException("Unknown type of test data");
|
||||
}
|
||||
|
||||
// Validate
|
||||
int expectedItemsCount = testData.Count();
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedItemsCount, "when capacity == 0, then we should allocate as much memory as needed, only");
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount / list.BlockSize) - (itemsCount % list.BlockSize != 0 ? 0 : 1));
|
||||
_ = list.LongCount.Should().Be(expectedItemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.EmptySourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldDoNothingWhenSourceIsEmpty(string testCase, IEnumerable<long> testData)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>();
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
else if (testData is List<long> testDataList)
|
||||
{
|
||||
list.AddRange(testDataList);
|
||||
}
|
||||
else if (testData is RecyclableList<long> testDataRecyclableList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableList);
|
||||
}
|
||||
else if (testData is RecyclableLongList<long> testDataRecyclableLongList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableLongList);
|
||||
}
|
||||
else if (testData is IList<long> testDataIList)
|
||||
{
|
||||
list.AddRange(testDataIList);
|
||||
}
|
||||
else if (testData is IEnumerable<long> testDataIEnumerable)
|
||||
{
|
||||
list.AddRange(testDataIEnumerable);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidCastException("Unknown type of test data");
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().Be(0);
|
||||
_ = list.LastBlockWithData.Should().Be(-1);
|
||||
_ = list.LongCount.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldNotOverrideItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
else if (testData is List<long> testDataList)
|
||||
{
|
||||
list.AddRange(testDataList);
|
||||
list.AddRange(testDataList);
|
||||
}
|
||||
else if (testData is RecyclableList<long> testDataRecyclableList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableList);
|
||||
list.AddRange(testDataRecyclableList);
|
||||
}
|
||||
else if (testData is RecyclableLongList<long> testDataRecyclableLongList)
|
||||
{
|
||||
list.AddRange(testDataRecyclableLongList);
|
||||
list.AddRange(testDataRecyclableLongList);
|
||||
}
|
||||
else if (testData is IList<long> testDataIList)
|
||||
{
|
||||
list.AddRange(testDataIList);
|
||||
list.AddRange(testDataIList);
|
||||
}
|
||||
else if (testData is IEnumerable<long> testDataIEnumerable)
|
||||
{
|
||||
list.AddRange(testDataIEnumerable);
|
||||
list.AddRange(testDataIEnumerable);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidCastException("Unknown type of test data");
|
||||
}
|
||||
|
||||
// Validate
|
||||
var expectedData = testData.Concat(testData).ToArray();
|
||||
|
||||
if (itemsCount > 0)
|
||||
{
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(targetBlockSize);
|
||||
}
|
||||
|
||||
_ = list.LongCount.Should().Be(expectedData.Length);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / list.BlockSize) - (itemsCount * 2 % list.BlockSize != 0 ? 0 : 1));
|
||||
_ = list.Should().ContainInConsecutiveOrder(expectedData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
testData = testData.Concat(testData).ToArray();
|
||||
|
||||
// Act
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
foreach (var index in testData)
|
||||
{
|
||||
list.Add(index);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount * 2);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / list.BlockSize) - (itemsCount * 2 % list.BlockSize != 0 ? 0 : 1));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddShouldAddItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
foreach (var index in testData)
|
||||
{
|
||||
list.Add(index);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount, "we added so many items");
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount / list.BlockSize) - (itemsCount % list.BlockSize == 0 ? 1 : 0));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void GetByIndexShouldReturnCorrectItem(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act & Validate
|
||||
foreach (var item in testData)
|
||||
{
|
||||
_ = list[item - 1].Should().Be(item);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
//[InlineData("Debugging case", new long[1] { 1 }, 1, 2)]
|
||||
public void AddShouldAddItemWhenAfterAddRange(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Act
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
list.AddRange(testData);
|
||||
foreach (var index in testData)
|
||||
{
|
||||
list.Add(index);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount * 2);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / list.BlockSize) - (itemsCount * 2 % list.BlockSize != 0 ? 0 : 1));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Concat(testData));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ClearShouldRemoveAllItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize, expectedItemsCount: itemsCount);
|
||||
_ = list.LongCount.Should().Be(itemsCount);
|
||||
|
||||
// Act
|
||||
list.Clear();
|
||||
list.Clear();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConsecutiveDisposeShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
list.Dispose();
|
||||
|
||||
// Act
|
||||
list.Dispose();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConstructorShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
testData = testData.Concat(testData).ToArray();
|
||||
|
||||
// Act
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount * 2);
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConstructorShouldAddItemsInCorrectOrderWhenSourceIsIEnumerable(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Act
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount, "when we succeed in taking count without enumerating, then we allocate as much memory as needed, only");
|
||||
_ = list.LongCount.Should().Be(itemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConstructorSourceShouldInitializeList(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Act
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ContainsShouldFindAllItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
// There is nothing to search for when our lists are empty
|
||||
return;
|
||||
}
|
||||
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act
|
||||
foreach (var item in testData)
|
||||
{
|
||||
// Validate
|
||||
_ = list.Contains(item).Should().BeTrue($"we searched for {item}");
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.TargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ContainsShouldNotFindAnythingWhenListEmpty(long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<int>(minBlockSize: targetBlockSize, expectedItemsCount: itemsCount);
|
||||
|
||||
// Validate
|
||||
_ = list.Contains(0).Should().BeFalse();
|
||||
_ = list.Contains(1).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ContainsShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
// There is nothing to search for when our lists are empty
|
||||
return;
|
||||
}
|
||||
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act
|
||||
foreach (var item in testData)
|
||||
{
|
||||
// Validate
|
||||
_ = list.Contains(-item).Should().BeFalse();
|
||||
}
|
||||
|
||||
// Validate - this case is special, because this is the default item value
|
||||
_ = list.Contains(0).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
//[InlineData("Debuging case", new[] { 1L, 2, }, 2, 2)]
|
||||
public void CopyToShouldCopyAllItemsInTheCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
var actual = new long[itemsCount];
|
||||
list.CopyTo(actual, 0);
|
||||
_ = actual.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void DisposeShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
list.Dispose();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(0L);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void EnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
var actual = new List<long>();
|
||||
using var enumerator = list.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
actual.Add(enumerator.Current);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = actual.Should().ContainInConsecutiveOrder(testData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IndexOfShouldNotFindAnythingWhenListEmpty()
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<int>();
|
||||
|
||||
// Validate
|
||||
_ = list.IndexOf(0).Should().Be(-1);
|
||||
_ = list.IndexOf(1).Should().Be(-1);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void IndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
// There is nothing to search for when our lists are empty
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act
|
||||
foreach (var item in testData)
|
||||
{
|
||||
// Validate
|
||||
_ = list.IndexOf(-item).Should().Be(-1);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.IndexOf(0).Should().Be(-1);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void IndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act & Validate
|
||||
foreach (var item in testData)
|
||||
{
|
||||
var actual = list.IndexOf(item);
|
||||
_ = actual.Should().Be((int)item - 1);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Act
|
||||
using RecyclableLongList<long> list = new(testData, minBlockSize: targetBlockSize);
|
||||
list.QuickSort();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount);
|
||||
_ = list.Should().Equal(testData);
|
||||
|
||||
// Act
|
||||
list.Clear();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
|
||||
// Act
|
||||
list.AddRange(testData);
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount);
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData);
|
||||
|
||||
// Act
|
||||
foreach (var item in testData.Reverse())
|
||||
{
|
||||
_ = list.Remove(item).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void InsertAtTheBeginningShouldMoveItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
_ = Assert.Throws<NotSupportedException>(() => list.Insert(0, -1));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void InsertShouldRaiseNotSupportedException(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
_ = Assert.Throws<NotSupportedException>(() => list.Insert(0, -1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LongIndexOfShouldNotFindAnythingWhenListEmpty()
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>();
|
||||
|
||||
// Validate
|
||||
_ = list.LongIndexOf(0).Should().Be(-1);
|
||||
_ = list.LongIndexOf(1).Should().Be(-1);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void LongIndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act
|
||||
foreach (var item in testData)
|
||||
{
|
||||
// Validate
|
||||
_ = list.LongIndexOf(-item).Should().Be(-1);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.LongIndexOf(0).Should().Be(-1);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void LongIndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act & Validate
|
||||
foreach (var item in testData)
|
||||
{
|
||||
// Act
|
||||
var index = list.LongIndexOf(item);
|
||||
|
||||
// Validate
|
||||
_ = index.Should().Be(item - 1);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveAtFromTheBeginningShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act & Validate
|
||||
for (var deleted = 1; deleted <= itemsCount; deleted++)
|
||||
{
|
||||
// Act
|
||||
list.RemoveAt(0);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount - deleted));
|
||||
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Skip(deleted));
|
||||
}
|
||||
|
||||
_ = list.Should().BeEmpty();
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveAtFromTheEndShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act & Validate
|
||||
for (var deleted = 1; deleted <= itemsCount; deleted++)
|
||||
{
|
||||
// Act
|
||||
list.RemoveAt(list.Count - 1);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount - deleted));
|
||||
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Take((int)(itemsCount - deleted)));
|
||||
}
|
||||
|
||||
_ = list.Should().BeEmpty();
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveFromTheBeginningShouldRemoveTheCorrectItem(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Act & Validate
|
||||
for (var deleted = 1; deleted <= itemsCount; deleted++)
|
||||
{
|
||||
// Act
|
||||
_ = list.Remove(deleted).Should().BeTrue();
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount - deleted));
|
||||
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
|
||||
_ = list.Should().ContainInConsecutiveOrder(testData.Skip(deleted));
|
||||
}
|
||||
|
||||
_ = list.Should().BeEmpty();
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveFromTheEndShouldRemoveTheCorrectItem(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
var expectedData = testData.ToArray();
|
||||
int removedCount = 0;
|
||||
foreach (var item in testData.Reverse())
|
||||
{
|
||||
// Act & Validate
|
||||
_ = list.Remove(item).Should().BeTrue($"we search for {item} which should exist");
|
||||
removedCount++;
|
||||
var expectedList = expectedData[0..((int)itemsCount - removedCount)];
|
||||
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
|
||||
_ = list.LongCount.Should().Be(expectedList.Length);
|
||||
_ = list.Should().ContainInConsecutiveOrder(expectedList);
|
||||
}
|
||||
|
||||
_ = list.Should().BeEmpty();
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,11 @@ using Recyclable.Collections;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
{
|
||||
[TestClass]
|
||||
public class RecyclableQueueTests
|
||||
{
|
||||
private static readonly string[] _testData = new[] { "a", "d", "c", "b", "a" };
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldNotBeSortedWhenCreated()
|
||||
{
|
||||
// Act
|
||||
@@ -17,9 +16,10 @@ namespace Recyclable.CollectionsTests
|
||||
// Validate
|
||||
_ = list.Should().HaveCount(_testData.Length)
|
||||
.And.ContainInConsecutiveOrder(_testData);
|
||||
_ = list.LongCount.Should().Be(_testData.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldBeEmptyWhenNotInitialized()
|
||||
{
|
||||
// Act
|
||||
@@ -27,9 +27,10 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
// Validate
|
||||
_ = list.Should().BeEmpty();
|
||||
_ = list.LongCount.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldNotBeSortedWhenInitialized()
|
||||
{
|
||||
// Act
|
||||
@@ -48,7 +49,7 @@ namespace Recyclable.CollectionsTests
|
||||
.And.BeEquivalentTo(_testData);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldBeEmptyAfterClear()
|
||||
{
|
||||
// Prepare
|
||||
@@ -61,7 +62,7 @@ namespace Recyclable.CollectionsTests
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void DequeueShouldReturnItemsInTheSameOrder()
|
||||
{
|
||||
// Prepare
|
||||
@@ -73,18 +74,18 @@ namespace Recyclable.CollectionsTests
|
||||
list.Enqueue(_testData[itemIdx]);
|
||||
}
|
||||
|
||||
var dequeuedItems = new RecyclableList<string>();
|
||||
var dequeuedItems = new RecyclableLongList<string>();
|
||||
while (list.LongCount > 0)
|
||||
{
|
||||
dequeuedItems.Add(list.Dequeue());
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = dequeuedItems.Should().ContainInConsecutiveOrder(_testData);
|
||||
_ = dequeuedItems.Should().ContainInConsecutiveOrder(_testData)
|
||||
.And.BeEquivalentTo(_testData);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
[Fact]
|
||||
public void DequeueShoudRaiseArgumentOutOfRangeWhenNoMoreElementsFound()
|
||||
{
|
||||
// Prepare
|
||||
@@ -96,25 +97,27 @@ namespace Recyclable.CollectionsTests
|
||||
_ = list.Dequeue();
|
||||
}
|
||||
|
||||
_ = list.Dequeue();
|
||||
_ = Assert.Throws<ArgumentOutOfRangeException>(() => list.Dequeue());
|
||||
_ = list.LongCount.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void GetEnumeratorShouldYieldAllItemsInTheSameOrder()
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableQueue<string>(_testData);
|
||||
|
||||
// Act
|
||||
var actual = new RecyclableList<string>();
|
||||
var enumerator = list.GetEnumerator();
|
||||
var actual = new RecyclableLongList<string>();
|
||||
using var enumerator = list.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
actual.Add(enumerator.Current);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = actual.Should().ContainInConsecutiveOrder(_testData);
|
||||
_ = actual.Should().ContainInConsecutiveOrder(_testData)
|
||||
.And.BeEquivalentTo(_testData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,11 @@ using Recyclable.Collections;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
{
|
||||
[TestClass()]
|
||||
public class RecyclableSortedListTests
|
||||
{
|
||||
private static readonly (int, string)[] _testData = new[] { (5, "a"), (4, "d"), (3, "c"), (2, "b"), (1, "a") };
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldBeSortedWhenCreated()
|
||||
{
|
||||
// Act
|
||||
@@ -20,7 +19,7 @@ namespace Recyclable.CollectionsTests
|
||||
.And.BeInAscendingOrder((item) => item.Key);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldBeEmptyWhenNotInitialized()
|
||||
{
|
||||
// Act
|
||||
@@ -30,7 +29,7 @@ namespace Recyclable.CollectionsTests
|
||||
_ = sortedList.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldNotBeSortedWhenInitialized()
|
||||
{
|
||||
// Act
|
||||
@@ -48,7 +47,7 @@ namespace Recyclable.CollectionsTests
|
||||
.And.ContainInConsecutiveOrder(_testData);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldBeSortedWhenUpdateEnds()
|
||||
{
|
||||
// Prepare
|
||||
@@ -71,7 +70,7 @@ namespace Recyclable.CollectionsTests
|
||||
_ = sortedList.Should().BeInAscendingOrder(x => x.Key);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldBeEmptyAfterClear()
|
||||
{
|
||||
// Prepare
|
||||
|
||||
@@ -3,20 +3,19 @@ using Recyclable.Collections;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
{
|
||||
[TestClass]
|
||||
public class RecyclableStackTests
|
||||
{
|
||||
private static readonly string[] _testData = new[] { "a", "d", "c", "b", "a" };
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void GetEnumeratorShouldYieldAllItemsInReversedOrder()
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableStack<string>(_testData);
|
||||
|
||||
// Act
|
||||
var actual = new RecyclableList<string>();
|
||||
var enumerator = list.GetEnumerator();
|
||||
var actual = new RecyclableLongList<string>();
|
||||
using var enumerator = list.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
actual.Add(enumerator.Current);
|
||||
@@ -26,7 +25,7 @@ namespace Recyclable.CollectionsTests
|
||||
_ = actual.Should().ContainInConsecutiveOrder(_testData.Reverse());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldNotBeSortedWhenCreated()
|
||||
{
|
||||
// Act
|
||||
@@ -37,7 +36,7 @@ namespace Recyclable.CollectionsTests
|
||||
.And.ContainInConsecutiveOrder(_testData);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldBeEmptyWhenNotInitialized()
|
||||
{
|
||||
// Act
|
||||
@@ -47,7 +46,7 @@ namespace Recyclable.CollectionsTests
|
||||
_ = sortedList.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldNotBeSortedWhenInitialized()
|
||||
{
|
||||
// Act
|
||||
@@ -66,7 +65,7 @@ namespace Recyclable.CollectionsTests
|
||||
.And.BeEquivalentTo(_testData);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void ShouldBeEmptyAfterClear()
|
||||
{
|
||||
// Prepare
|
||||
@@ -79,7 +78,7 @@ namespace Recyclable.CollectionsTests
|
||||
_ = sortedList.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void PopShouldReturnItemsInReversedOrder()
|
||||
{
|
||||
// Prepare
|
||||
@@ -91,7 +90,7 @@ namespace Recyclable.CollectionsTests
|
||||
list.Push(_testData[itemIdx]);
|
||||
}
|
||||
|
||||
var dequeuedItems = new RecyclableList<string>();
|
||||
var dequeuedItems = new RecyclableLongList<string>();
|
||||
while (list.LongCount > 0)
|
||||
{
|
||||
dequeuedItems.Add(list.Pop());
|
||||
@@ -101,8 +100,7 @@ namespace Recyclable.CollectionsTests
|
||||
_ = dequeuedItems.Should().ContainInConsecutiveOrder(_testData.Reverse());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
[Fact]
|
||||
public void PopShoudRaiseArgumentOutOfRangeWhenNoMoreElementsFound()
|
||||
{
|
||||
// Prepare
|
||||
@@ -114,7 +112,7 @@ namespace Recyclable.CollectionsTests
|
||||
_ = list.Dequeue();
|
||||
}
|
||||
|
||||
_ = list.Dequeue();
|
||||
_ = Assert.Throws<ArgumentOutOfRangeException>(() => list.Dequeue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
global using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
global using Xunit;
|
||||
Reference in new issue
Block a user