112 lines
No EOL
3.2 KiB
C#
112 lines
No EOL
3.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using BenchmarkDotNet.Attributes;
|
|
using BenchmarkDotNet.Running;
|
|
using LanguageExt;
|
|
using static LanguageExt.Prelude;
|
|
using PersistentMap;
|
|
|
|
|
|
[MemoryDiagnoser]
|
|
[HideColumns("Ratio", "RatioSD", "Alloc Ratio")]
|
|
public class CyclicMapBenchmarks
|
|
{
|
|
// Powers of 2 for fast bitwise masking
|
|
[Params(1024, 131072)]
|
|
public int CollectionSize { get; set; }
|
|
|
|
[Params(10, 100, 1000)]
|
|
public int N { get; set; }
|
|
|
|
// Collections
|
|
private PersistentMap<string, int, UnicodeStrategy> _persistentMap;
|
|
private ImmutableSortedDictionary<string, int> _sysSorted;
|
|
private LanguageExt.HashMap<string, int> _langExtHash;
|
|
private LanguageExt.Map<string, int> _langExtSorted;
|
|
|
|
// Lookup scaffolding
|
|
private string[] _searchKeys;
|
|
private int _index = 0;
|
|
private int _mask;
|
|
|
|
[GlobalSetup]
|
|
public void Setup()
|
|
{
|
|
_mask = CollectionSize - 1;
|
|
var random = new Random(42);
|
|
var data = new Dictionary<string, int>();
|
|
|
|
// 1. Generate Data
|
|
while (data.Count < CollectionSize)
|
|
{
|
|
string key = GenerateRandomString(random, N);
|
|
if (!data.ContainsKey(key))
|
|
{
|
|
data[key] = random.Next();
|
|
}
|
|
}
|
|
|
|
// 2. Build Collections
|
|
// PersistentMap
|
|
var builder = PersistentMap<string, int, UnicodeStrategy>.Empty(new UnicodeStrategy()).ToTransient();
|
|
foreach (var kvp in data) builder.Set(kvp.Key, kvp.Value);
|
|
_persistentMap = builder.ToPersistent();
|
|
|
|
// System
|
|
_sysSorted = data.ToImmutableSortedDictionary();
|
|
|
|
// LanguageExt
|
|
_langExtHash = toHashMap(data);
|
|
_langExtSorted = toMap(data);
|
|
|
|
// 3. Setup Cyclic Keys
|
|
_searchKeys = data.Keys.ToArray();
|
|
// Shuffle to defeat branch prediction / cache pre-fetching
|
|
Random.Shared.Shuffle(_searchKeys);
|
|
}
|
|
|
|
[Benchmark(Description = "Cyclic: PersistentMap")]
|
|
public int Lookup_Persistent()
|
|
{
|
|
var key = _searchKeys[_index++ & _mask];
|
|
_persistentMap.TryGetValue(key, out var value);
|
|
return value;
|
|
}
|
|
|
|
[Benchmark(Description = "Cyclic: Sys.Sorted")]
|
|
public int Lookup_SysSorted()
|
|
{
|
|
var key = _searchKeys[_index++ & _mask];
|
|
_sysSorted.TryGetValue(key, out var value);
|
|
return value;
|
|
}
|
|
|
|
[Benchmark(Description = "Cyclic: LangExt.HashMap")]
|
|
public int Lookup_LangExtHash()
|
|
{
|
|
var key = _searchKeys[_index++ & _mask];
|
|
// Option<T> struct return, overhead is minimal but present
|
|
return _langExtHash.Find(key).IfNone(0);
|
|
}
|
|
|
|
[Benchmark(Description = "Cyclic: LangExt.Sorted")]
|
|
public int Lookup_LangExtSorted()
|
|
{
|
|
var key = _searchKeys[_index++ & _mask];
|
|
// AVL Tree traversal
|
|
return _langExtSorted.Find(key).IfNone(0);
|
|
}
|
|
|
|
private string GenerateRandomString(Random rng, int length)
|
|
{
|
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
var buffer = new char[length];
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
buffer[i] = chars[rng.Next(chars.Length)];
|
|
}
|
|
return new string(buffer);
|
|
}
|
|
} |