A high-performance C#/.NET priority queue inspired by the 2026 SimdQuickHeap paper.
SimdQuickHeap<TElement, TPriority> is a fast priority queue for .NET.
A priority queue is the data structure you use when you always want to take the "most urgent" item first. For example:
- the next task a scheduler should run
- the next event in a simulation
- the next cheapest node in a graph search
- the next message or job with the smallest priority value
.NET already has PriorityQueue<TElement, TPriority>. SimdQuickHeap is built for cases where the queue gets large and performance starts to matter.
Most heaps store items in one tree-like array. SimdQuickHeap uses a layout from the paper SimdQuickHeap: The QuickHeap Reconsidered:
- pivots are stored next to each other in memory
- items live in dedicated buckets between those pivots
- integer priority comparisons can be done in chunks with SIMD CPU instructions
- large buckets are partitioned quickly instead of bubbling one item at a time
In simple terms: it organizes memory so modern CPUs can compare and move many priorities at once.
This repo is usable today for large online priority queues with default int or long priorities.
It currently includes:
net8.0andnet10.0targets- generic
SimdQuickHeap<TElement, TPriority> - min-priority behavior by default
- custom comparer support
Enqueue,Dequeue,TryDequeue,Peek,TryPeekPushandPopaliases- capacity helpers:
EnsureCapacity,TrimExcess,Clear - lock-based synchronized wrapper
- SIMD pivot scanning for default integer priorities
- SIMD-assisted partitioning for
intandlongpriorities - AVX2 packed partitioning for
SimdQuickHeap<int, int> - .NET 10 AVX-512 compress-store path for
SimdQuickHeap<int, int> - BenchmarkDotNet benchmark suite
- GitHub Actions build, test, and package workflow
using SimdQuickHeap;
var heap = new SimdQuickHeap<string, int>();
heap.Enqueue("compile", 20);
heap.Enqueue("ship", 10);
heap.Enqueue("benchmark", 30);
string next = heap.Dequeue();
Console.WriteLine(next); // shipLower priority values come out first by default, just like a min-heap.
var heap = new SimdQuickHeap<int, int>(capacity: 100_000);
heap.Enqueue(42, priority: 10);
heap.Push(99, priority: 5);
int best = heap.Dequeue();
if (heap.TryDequeue(out int item, out int priority))
{
Console.WriteLine($"{item} had priority {priority}");
}
if (heap.TryPeek(out int nextItem, out int nextPriority))
{
Console.WriteLine($"Next: {nextItem}, priority {nextPriority}");
}Custom ordering is supported:
IComparer<int> highestFirst = Comparer<int>.Create((left, right) => right.CompareTo(left));
var maxQueue = new SimdQuickHeap<string, int>(highestFirst);Good fits:
- large task schedulers
- event simulation
- large graph workloads
- pathfinding queues
- interleaved push/pop workloads
- bulk enqueue followed by many pops
- default
intorlongpriorities
Less ideal fits:
- tiny queues under a few thousand items
- one-shot batch sorting where
List.Sortis enough - heavy custom comparer workloads
- floating-point priority workloads where
NaNbehavior must match .NET exactly - workloads that require true decrease-key handles
For Dijkstra-style algorithms, use reinsertion plus lazy deletion. That is the approach used by .NET's PriorityQueue too, and it avoids handle overhead.
Benchmarks were run with BenchmarkDotNet on .NET 10.0.7, x64 RyuJIT, AVX2 hardware.
Configuration: 1 launch, 1 warmup, 3 measured iterations. These are practical direction numbers, not a final academic benchmark paper.
This is the most realistic priority queue shape for schedulers and simulations.
| Size | PriorityQueue | SimdQuickHeap | Binary heap | 4-ary heap | SortedSet | SortedDictionary |
|---|---|---|---|---|---|---|
| 1,000 | 9.122 us | 22.890 us | 9.548 us | 9.089 us | 46.391 us | 116.283 us |
| 10,000 | 268.036 us | 225.743 us | 279.573 us | 271.788 us | 893.015 us | 1,718.333 us |
| 100,000 | 2,768.723 us | 2,307.906 us | 3,173.822 us | 3,096.644 us | 14,928.945 us | 33,247.396 us |
At 100,000 operations, SimdQuickHeap was about 17% faster than PriorityQueue<TElement, TPriority> on this machine.
This stresses the full drain path.
| Size | PriorityQueue | SimdQuickHeap | Binary heap | 4-ary heap | Queue + List.Sort |
|---|---|---|---|---|---|
| 1,000 | 40.905 us | 42.948 us | 31.041 us | 46.322 us | 27.749 us |
| 10,000 | 749.218 us | 464.211 us | 728.265 us | 766.593 us | 659.951 us |
| 100,000 | 9,383.464 us | 4,780.002 us | 9,349.505 us | 10,450.365 us | 7,662.619 us |
At 100,000 items, SimdQuickHeap was roughly 2x faster than PriorityQueue<TElement, TPriority>.
Queue + List.Sort is included only as a batch baseline. It is not an online priority queue because it cannot efficiently answer "give me the next item now" while new items keep arriving.
dotnet run -c Release --project benchmarks\SimdQuickHeap.Benchmarks\SimdQuickHeap.Benchmarks.csproj -- --filter "*CollectionComparisonBenchmark*"Run a smaller filtered benchmark:
dotnet run -c Release --project benchmarks\SimdQuickHeap.Benchmarks\SimdQuickHeap.Benchmarks.csproj -- --filter "*PushPopBenchmark*PushPopAll*"Benchmark results are written to BenchmarkDotNet.Artifacts/results.
Restore:
dotnet restore SimdQuickHeap.sln --configfile NuGet.ConfigBuild:
dotnet build SimdQuickHeap.sln -c Release --configfile NuGet.Config --no-restoreRun tests:
dotnet run -c Release --project tests\SimdQuickHeap.Tests\SimdQuickHeap.Tests.csproj --framework net8.0 --no-build
dotnet run -c Release --project tests\SimdQuickHeap.Tests\SimdQuickHeap.Tests.csproj --framework net10.0 --no-buildCreate a package:
dotnet pack src\SimdQuickHeap\SimdQuickHeap.csproj -c Release --no-build -o artifactsThe workflow builds, tests, and packs the project on every push to main, every pull request to main, and every v* tag.
It installs both .NET 8 and .NET 10, then verifies both target frameworks.
Implemented:
- adjacent pivot storage
- dedicated buckets between pivots
- median-of-three pivot selection
- duplicate-safe partitioning
- small-bucket cutoff
- SIMD pivot scanning for integer priorities
- SIMD-assisted bucket partitioning
- AVX2 path for packed
intpriorities and elements - .NET 10 AVX-512 compress-store path for
SimdQuickHeap<int, int>
Not implemented:
- stable decrease-key handles
- full rebalancing strategy
- fully SIMD floating-point priority path
Floating-point priorities intentionally use comparer-based logic so .NET NaN ordering remains correct.
- More Dijkstra and graph benchmark coverage
- More real scheduler and simulation workloads
- AVX-512 hardware validation on a real AVX-512 host
- NuGet publishing pipeline
- More docs and examples
- Additional tuning for small queues
MIT. See LICENSE.
