speedups maybe

changed Set function and Find(routing)Index) to specialize on class (no
virtual dispatch) or using generics.
This commit is contained in:
Linus Björnstam 2026-05-21 12:29:20 +02:00
parent a2458e727a
commit 7ee2238248
2 changed files with 21 additions and 18 deletions

View file

@ -1,4 +1,3 @@
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -42,27 +41,28 @@ namespace PersistentOrderedMap
} }
} }
public static Node<K> Set<K, V>(Node<K> root, K key, V value, IKeyStrategy<K> strategy, OwnerId owner, out bool countChanged) public static Node<K> Set<K, V, TStrategy>(Node<K> root, K key, V value, TStrategy strategy, OwnerId owner, out bool countChanged)
where TStrategy : IKeyStrategy<K>
{ {
root = root.EnsureEditable(owner); root = root.EnsureEditable(owner);
// Todo, this should really be made a tuple return value to not stress the GC // Todo, this should really be made a tuple return value to not stress the GC
var splitResult = InsertRecursive(root, key, value, strategy, owner, out countChanged); var (newNode, sep) = InsertRecursive(root, key, value, strategy, owner, out countChanged);
if (splitResult != null) if (newNode != null)
{ {
var newRoot = strategy.UsesPrefixes var newRoot = strategy.UsesPrefixes
? new PrefixInternalNode<K>(owner) ? new PrefixInternalNode<K>(owner)
: new InternalNode<K>(owner); : new InternalNode<K>(owner);
newRoot.Keys[0] = splitResult.Separator; newRoot.Keys[0] = sep;
newRoot.Children[0] = root; newRoot.Children[0] = root;
newRoot.Children[1] = splitResult.NewNode; newRoot.Children[1] = newNode;
newRoot.SetCount(1); newRoot.SetCount(1);
if (strategy.UsesPrefixes) if (strategy.UsesPrefixes)
{ {
newRoot.AllPrefixes[0] = strategy.GetPrefix(splitResult.Separator); newRoot.AllPrefixes[0] = strategy.GetPrefix(sep);
} }
return newRoot; return newRoot;
@ -71,7 +71,7 @@ namespace PersistentOrderedMap
return root; return root;
} }
private static SplitResult<K>? InsertRecursive<K, V>(Node<K> node, K key, V value, IKeyStrategy<K> strategy, OwnerId owner, out bool added) private static (Node<K>? newNode, K separator ) InsertRecursive<K, V>(Node<K> node, K key, V value, IKeyStrategy<K> strategy, OwnerId owner, out bool added)
{ {
long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0; long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0;
@ -84,14 +84,14 @@ namespace PersistentOrderedMap
{ {
leaf.Values[index] = value; leaf.Values[index] = value;
added = false; added = false;
return null; return (null, default);
} }
added = true; added = true;
if (leaf.Header.Count < LeafNode<K, V>.Capacity) if (leaf.Header.Count < LeafNode<K, V>.Capacity)
{ {
InsertIntoLeaf(leaf, index, key, value, strategy); InsertIntoLeaf(leaf, index, key, value, strategy);
return null; return (null, default);
} }
else else
{ {
@ -106,21 +106,21 @@ namespace PersistentOrderedMap
var child = internalNode.Children[index]!.EnsureEditable(owner); var child = internalNode.Children[index]!.EnsureEditable(owner);
internalNode.Children[index] = child; internalNode.Children[index] = child;
var split = InsertRecursive(child, key, value, strategy, owner, out added); var (newNode, sep) = InsertRecursive(child, key, value, strategy, owner, out added);
if (split != null) if (newNode != null)
{ {
if (internalNode.Header.Count < InternalNode<K>.Capacity - 1) if (internalNode.Header.Count < InternalNode<K>.Capacity - 1)
{ {
InsertIntoInternal(internalNode, index, split.Separator, split.NewNode, strategy); InsertIntoInternal(internalNode, index, sep, newNode, strategy);
return null; return (null, default);
} }
else else
{ {
return SplitInternal(internalNode, index, split.Separator, split.NewNode, strategy, owner); return SplitInternal(internalNode, index, sep, newNode, strategy, owner);
} }
} }
return null; return (null, default);
} }
} }
@ -189,7 +189,7 @@ namespace PersistentOrderedMap
// --------------------------------------------------------- // ---------------------------------------------------------
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int FindIndex<K, TStrategy>(Node<K> node, K key, long keyPrefix, TStrategy strategy) internal static int FindIndex<K,V, TStrategy>(LeafNode<K,V> node, K key, long keyPrefix, TStrategy strategy)
where TStrategy : IKeyStrategy<K> where TStrategy : IKeyStrategy<K>
{ {
if (typeof(K) == typeof(int)) if (typeof(K) == typeof(int))

View file

@ -31,6 +31,9 @@ where TStrategy : IKeyStrategy<K>
// Fixed-size buffer for the path. // Fixed-size buffer for the path.
// Depth 16 * 32 (branching factor) = Exabytes of capacity. // Depth 16 * 32 (branching factor) = Exabytes of capacity.
// The B tree is, theoretically, not limited by the size of an int, like
// all int-indexed data structures (or bit partitioned ones).
// This should be enough for anyone.
[InlineArray(16)] [InlineArray(16)]
internal struct IterNodeBuffer<K> internal struct IterNodeBuffer<K>
{ {
@ -138,7 +141,7 @@ public struct BTreeEnumerator<K, V, TStrategy> : IEnumerator<KeyValuePair<K, V>>
// Find index in Leaf // Find index in Leaf
_currentLeaf = node.AsLeaf<V>(); _currentLeaf = node.AsLeaf<V>();
int index = BTreeFunctions.FindIndex<K, TStrategy>(_currentLeaf, key, keyPrefix, _strategy); int index = BTreeFunctions.FindIndex<K,V, TStrategy>(_currentLeaf, key, keyPrefix, _strategy);
// Set position to (index - 1) so that the first MoveNext() lands on 'index' // Set position to (index - 1) so that the first MoveNext() lands on 'index'
_currentLeafIndex = index - 1; _currentLeafIndex = index - 1;