Skip to content

Commit f06cd26

Browse files
Improved performance with hot-path optimization.
1 parent 2884870 commit f06cd26

File tree

3 files changed

+33
-21
lines changed

3 files changed

+33
-21
lines changed

Trie/Open.Collections.Trie.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<PackageProjectUrl>https://github.com/Open-NET-Libraries/Open.Collections/tree/master/Trie</PackageProjectUrl>
1919
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Collections/</RepositoryUrl>
2020
<RepositoryType>git</RepositoryType>
21-
<Version>1.1.0</Version>
21+
<Version>1.1.1</Version>
2222
<PackageReleaseNotes></PackageReleaseNotes>
2323
<PackageLicenseExpression>MIT</PackageLicenseExpression>
2424
<PublishRepositoryUrl>true</PublishRepositoryUrl>

Trie/TrieBase.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ internal abstract class NodeBase : ITrieNode<TKey, TValue>
221221

222222
private (bool isSet, TValue? value) _value;
223223

224+
// It's not uncommon to have a 'hot path' that will be requested frequently.
225+
// This facilitates that by caching the last child that was requested.
226+
private (bool exists, TKey key, ITrieNode<TKey, TValue> child) _recentChild;
227+
224228
public bool IsSet => _value.isSet;
225229

226230
internal TValue GetValueOrThrow()
@@ -261,7 +265,16 @@ public bool TryGetChild(TKey key, out ITrieNode<TKey, TValue> child)
261265
return false;
262266
}
263267

264-
return Children!.TryGetValue(key, out child);
268+
var recent = _recentChild;
269+
if(recent.exists && recent.key!.Equals(key))
270+
{
271+
child = recent.child;
272+
return true;
273+
}
274+
275+
bool found = Children!.TryGetValue(key, out child);
276+
_recentChild = (true, key, child);
277+
return found;
265278
}
266279

267280
public ITrieNode<TKey, TValue> GetChild(TKey key)

benchmarking/Benchmarks/TrieBenchmarks.cs

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class TrieBenchmark
2828

2929
private Trie<string, int> trie;
3030
private Dictionary<string, int> dictionary;
31-
private List<(string[], string)> keys;
31+
private (string[], string)[] keys;
3232

3333
[GlobalSetup]
3434
public void GlobalSetup()
@@ -38,20 +38,19 @@ public void GlobalSetup()
3838
dictionary = new();
3939
ctrie = new();
4040
cdictionary = new();
41-
keys = new();
42-
43-
foreach (string[] key in GenerateTree(Depth, NodeSize))
44-
{
45-
x++;
46-
trie.Add(key, x);
47-
ctrie.Add(key, x);
41+
keys = GenerateTree(Depth, NodeSize)
42+
.Select(key=>
43+
{
44+
x++;
45+
trie.Add(key, x);
46+
ctrie.Add(key, x);
4847

49-
string keyString = string.Join('/', key);
50-
dictionary.Add(keyString, x);
51-
cdictionary.TryAdd(keyString, x);
48+
string keyString = string.Join('/', key);
49+
dictionary.Add(keyString, x);
50+
cdictionary.TryAdd(keyString, x);
5251

53-
keys.Add((key, keyString));
54-
}
52+
return (key, keyString);
53+
}).ToArray();
5554
}
5655

5756
public static IEnumerable<string[]> GenerateTree(int depth, int nodeCount, Stack<string> stack = null)
@@ -79,7 +78,7 @@ public static IEnumerable<string[]> GenerateTree(int depth, int nodeCount, Stack
7978
public int TrieLookup()
8079
{
8180
int result = 0;
82-
foreach(var key in keys)
81+
foreach(var key in keys.AsSpan())
8382
{
8483
trie.TryGetValue(key.Item1, out result);
8584
}
@@ -90,7 +89,7 @@ public int TrieLookup()
9089
public int TrieWalkLookup()
9190
{
9291
int result = 0;
93-
foreach (var key in keys)
92+
foreach (var key in keys.AsSpan())
9493
{
9594
string[] k = key.Item1;
9695
result = trie.GetChild(k[0]).GetChild(k[1]).GetChild(k[2]).GetChild(k[3]).Value;
@@ -102,7 +101,7 @@ public int TrieWalkLookup()
102101
public int TrieLookupWithToArray()
103102
{
104103
int result = 0;
105-
foreach (var key in keys)
104+
foreach (var key in keys.AsSpan())
106105
{
107106
trie.TryGetValue(key.Item1.ToArray(), out result);
108107
}
@@ -113,7 +112,7 @@ public int TrieLookupWithToArray()
113112
public int DictionaryLookup()
114113
{
115114
int result = 0;
116-
foreach (var key in keys)
115+
foreach (var key in keys.AsSpan())
117116
{
118117
result = dictionary[key.Item2];
119118

@@ -125,7 +124,7 @@ public int DictionaryLookup()
125124
public int DictionaryLookupKeyConcat()
126125
{
127126
int result = 0;
128-
foreach (var key in keys)
127+
foreach (var key in keys.AsSpan())
129128
{
130129
string[] k = key.Item1;
131130
result = dictionary[$"{k[0]}/{k[1]}/{k[2]}/{k[3]}"];
@@ -137,7 +136,7 @@ public int DictionaryLookupKeyConcat()
137136
public int DictionaryLookupWithJoin()
138137
{
139138
int result = 0;
140-
foreach (var key in keys)
139+
foreach (var key in keys.AsSpan())
141140
{
142141
result = dictionary[string.Join("/", key.Item1)];
143142
}

0 commit comments

Comments
 (0)