fix prefixless search
This commit is contained in:
parent
280177a9cb
commit
a9b6ed9161
12 changed files with 1054 additions and 44 deletions
198
benchmarks/AgainstLanguageExt/AgainstLanguageExt.cs
Normal file
198
benchmarks/AgainstLanguageExt/AgainstLanguageExt.cs
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
using System.Collections.Immutable;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Running;
|
||||
using LanguageExt;
|
||||
using PersistentMap;
|
||||
|
||||
namespace AgainstLanguageExt;
|
||||
// Mocking your library types for the sake of the example to ensure compilation.
|
||||
// Replace these with your actual namespace imports.
|
||||
|
||||
|
||||
[MemoryDiagnoser]
|
||||
[HideColumns("Ratio", "RatioSD", "Alloc Ratio")]
|
||||
public class MapBenchmarks
|
||||
{
|
||||
[Params(10, 100, 1000)]
|
||||
public int KeyLength { get; set; } // Key length
|
||||
|
||||
[Params(1000, 100000, 1000000)]
|
||||
public int CollectionSize { get; set; }
|
||||
|
||||
// Data Source
|
||||
private KeyValuePair<string, int>[] _data;
|
||||
private string[] _searchKeys;
|
||||
|
||||
private int _index = 0;
|
||||
private int _mask;
|
||||
|
||||
// Comparison Targets (for Lookup)
|
||||
private ImmutableDictionary<string, int> _sysDict;
|
||||
private ImmutableSortedDictionary<string, int> _sysSorted;
|
||||
private LanguageExt.HashMap<string, int> _langExtHash;
|
||||
private LanguageExt.Map<string, int> _langExtSorted; // Map<K,V> is Sorted in LangExt
|
||||
private PersistentMap<string, int, UnicodeStrategy> _persistentMap;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
var random = new Random(42);
|
||||
var dict = new Dictionary<string, int>();
|
||||
|
||||
// 1. Generate Data
|
||||
while (dict.Count < CollectionSize)
|
||||
{
|
||||
string key = GenerateRandomString(random, KeyLength);
|
||||
if (!dict.ContainsKey(key))
|
||||
{
|
||||
dict[key] = random.Next();
|
||||
}
|
||||
}
|
||||
_data = dict.ToArray();
|
||||
_searchKeys = dict.Keys.ToArray();
|
||||
|
||||
// 2. Pre-build maps for the Lookup benchmarks
|
||||
_sysDict = dict.ToImmutableDictionary();
|
||||
_sysSorted = dict.ToImmutableSortedDictionary();
|
||||
|
||||
_langExtHash = Prelude.toHashMap(dict);
|
||||
_langExtSorted = Prelude.toMap(dict);
|
||||
|
||||
// Build PersistentMap for lookup test
|
||||
var trans = PersistentMap<string, int, UnicodeStrategy>.Empty(new UnicodeStrategy()).ToTransient();
|
||||
foreach (var kvp in _data)
|
||||
{
|
||||
trans.Set(kvp.Key, kvp.Value);
|
||||
}
|
||||
_persistentMap = trans.ToPersistent();
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// BUILD BENCHMARKS
|
||||
// ==========================================
|
||||
|
||||
[Benchmark(Description = "Lookup: PersistentMap (Cyclic)")]
|
||||
public int Lookup_Persistent_Cyclic()
|
||||
{
|
||||
// Fast wrap-around
|
||||
var key = _searchKeys[_index++ & _mask];
|
||||
_persistentMap.TryGetValue(key, out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Lookup: Sys.Sorted (Cyclic)")]
|
||||
public int Lookup_SysSorted_Cyclic()
|
||||
{
|
||||
var key = _searchKeys[_index++ & _mask];
|
||||
_sysSorted.TryGetValue(key, out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Build: Sys.ImmutableDict")]
|
||||
public ImmutableDictionary<string, int> Build_SysImmutable()
|
||||
{
|
||||
// Using CreateRange/ToImmutable is usually the standard 'bulk' build
|
||||
return _data.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Build: LangExt.HashMap")]
|
||||
public LanguageExt.HashMap<string, int> Build_LangExtHash()
|
||||
{
|
||||
return Prelude.toHashMap(_data);
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Build: LangExt.SortedMap")]
|
||||
public LanguageExt.Map<string, int> Build_LangExtSorted()
|
||||
{
|
||||
return Prelude.toMap(_data);
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Build: PersistentMap (Iterative)")]
|
||||
public PersistentMap<string, int, UnicodeStrategy> Build_Persistent_Iterative()
|
||||
{
|
||||
// Simulating naive immutable building (O(n log n) or worse due to copying)
|
||||
var map = PersistentMap<string, int, UnicodeStrategy>.Empty(new UnicodeStrategy());
|
||||
foreach (var item in _data)
|
||||
{
|
||||
map = map.Set(item.Key, item.Value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Build: PersistentMap (Transient)")]
|
||||
public PersistentMap<string, int, UnicodeStrategy> Build_Persistent_Transient()
|
||||
{
|
||||
// Simulating efficient mutable build -> freeze
|
||||
var trans = PersistentMap<string, int, UnicodeStrategy>.Empty(new UnicodeStrategy()).ToTransient();
|
||||
foreach (var item in _data)
|
||||
{
|
||||
trans.Set(item.Key, item.Value);
|
||||
}
|
||||
return trans.ToPersistent();
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// LOOKUP BENCHMARKS
|
||||
// ==========================================
|
||||
|
||||
[Benchmark(Baseline = true, Description = "Lookup: Sys.ImmutableDict")]
|
||||
public int Lookup_SysImmutable()
|
||||
{
|
||||
var key = _searchKeys[CollectionSize / 2];
|
||||
_sysDict.TryGetValue(key, out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Lookup: Sys.SortedDict")]
|
||||
public int Lookup_SysSorted()
|
||||
{
|
||||
var key = _searchKeys[CollectionSize / 2];
|
||||
_sysSorted.TryGetValue(key, out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Lookup: LangExt.HashMap")]
|
||||
public int Lookup_LangExtHash()
|
||||
{
|
||||
var key = _searchKeys[CollectionSize / 2];
|
||||
// LanguageExt often uses Find which returns Option, or [] operator
|
||||
// Assuming TryGetValue-like behavior or using match for fairness
|
||||
return _langExtHash.Find(key).IfNone(0);
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Lookup: LangExt.SortedMap")]
|
||||
public int Lookup_LangExtSorted()
|
||||
{
|
||||
var key = _searchKeys[CollectionSize / 2];
|
||||
return _langExtSorted.Find(key).IfNone(0);
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Lookup: PersistentMap")]
|
||||
public int Lookup_PersistentMap()
|
||||
{
|
||||
var key = _searchKeys[CollectionSize / 2];
|
||||
_persistentMap.TryGetValue(key, out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Helper
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// This scans the assembly and lets the command line (args) decide what to run
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
|
||||
}
|
||||
}
|
||||
112
benchmarks/AgainstLanguageExt/Cycicmap.cs
Normal file
112
benchmarks/AgainstLanguageExt/Cycicmap.cs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue