fix: Optimize OrderedHashSet and introduce more high perf collections (#679)

* Optimizes OrderedHashSet using the learnings here: https://github.com/dotnet/runtime/issues/10050
* Removes ValueCollection since it was a relic from the conversion of the OrderedDictionary
* Centralizes throw strings.
* Adds pooled ordered hash set (but doesn't use it)

Closes #448
This commit is contained in:
Kamron Batman 2021-08-08 21:37:25 -07:00 • committed by GitHub
parent d10e782371
commit b0f8fe0350
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 2495 additions and 481 deletions

View file

@ -9,51 +9,37 @@ namespace Benchmarks
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
public class BenchmarkOrderedHashSet
{
private List<string> _list;
private OrderedHashSet<string> _ordered;
private HashSet<string> _hashSet;
private const int iterations = 16;
private readonly string[] _iterations = new string[16];
[IterationSetup]
public void IterationSetup()
{
_list = new List<string>();
_ordered = new OrderedHashSet<string>();
_hashSet = new HashSet<string>();
}
[IterationCleanup]
public void IterationCleanup()
{
_list = null;
_ordered = null;
_hashSet = null;
for (var i = 0; i < _iterations.Length; i++)
{
_iterations[i] = i.ToString();
}
}
[Benchmark]
public int UsingList()
{
for (int i = 0; i < iterations / 2; i++)
var list = new List<string>();
for (int i = 0; i < _iterations.Length / 2; i++)
{
AddIfNotPresent(_list, i.ToString());
AddIfNotPresent(list, _iterations[i]);
}
for (int i = 0; i < iterations; i++)
for (int i = 0; i < _iterations.Length; i++)
{
AddIfNotPresent(_list, i.ToString());
AddIfNotPresent(list, _iterations[i]);
}
for (int i = 0; i < iterations * 2; i++)
for (int i = 0; i < list.Count; i++)
{
AddIfNotPresent(_list, i.ToString());
list[i].ToString();
}
for (int i = 0; i < _list.Count; i++)
{
_list[i].ToString();
}
return _list.Count;
return list.Count;
}
private static int AddIfNotPresent<T>(List<T> list, T item)
@ -71,53 +57,74 @@ namespace Benchmarks
[Benchmark]
public int UsingOrderedHashSet()
{
for (int i = 0; i < iterations / 2; i++)
var ordered = new OrderedHashSet<string>();
for (int i = 0; i < _iterations.Length / 2; i++)
{
_ordered.GetOrAdd(i.ToString());
ordered.GetOrAdd(_iterations[i]).ToString();
}
for (int i = 0; i < iterations; i++)
for (int i = 0; i < _iterations.Length; i++)
{
_ordered.GetOrAdd(i.ToString());
ordered.GetOrAdd(_iterations[i]).ToString();
}
for (int i = 0; i < iterations * 2; i++)
{
_ordered.GetOrAdd(i.ToString());
}
foreach (var str in _ordered)
foreach (var str in ordered)
{
str.ToString();
}
return _ordered.Count;
return ordered.Count;
}
[Benchmark]
public int UsingPooledOrderedHashSet()
{
var ordered = new PooledOrderedHashSet<string>();
for (int i = 0; i < _iterations.Length / 2; i++)
{
ordered.GetOrAdd(_iterations[i]).ToString();
}
for (int i = 0; i < _iterations.Length; i++)
{
ordered.GetOrAdd(_iterations[i]).ToString();
}
foreach (var str in ordered)
{
str.ToString();
}
return ordered.Count;
}
[Benchmark]
public int UsingHashSet()
{
for (int i = 0; i < iterations / 2; i++)
var hashSet = new HashSet<(string, int)>(new OrderedStringComparer());
for (int i = 0; i < _iterations.Length / 2; i++)
{
_hashSet.Add(i.ToString());
hashSet.Add((_iterations[i], i));
}
for (int i = 0; i < iterations; i++)
for (int i = 0; i < _iterations.Length; i++)
{
_hashSet.Add(i.ToString());
hashSet.Add((_iterations[i], i));
}
for (int i = 0; i < iterations * 2; i++)
{
_hashSet.Add(i.ToString());
}
foreach (var str in _hashSet)
foreach (var str in hashSet)
{
str.ToString();
}
return _hashSet.Count;
return hashSet.Count;
}
private class OrderedStringComparer : EqualityComparer<(string, int)>
{
public override bool Equals((string, int) x, (string, int) y) => x.Item1.Equals(y.Item1, System.StringComparison.Ordinal);
public override int GetHashCode((string, int) obj) => obj.Item1.GetHashCode();
}
}
}