using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using LanguageExt; using PersistentMap; namespace MapBenchmarks; [MemoryDiagnoser] public class IntMapBenchmarks { [Params(100, 1000, 10000, 100000)] public int N { get; set; } private int[] _allKeys; private int[] _retrieveKeys; private int[] _updateKeys; private int[] _removeKeys; private int[] _mixedKeys; // Half existing, half new // Pre-built collections for read/update/remove tests private ImmutableDictionary _immDict; private ImmutableSortedDictionary _immSortedDict; private LanguageExt.Map _extMap; private LanguageExt.HashMap _extHashMap; private PersistentMap _persistentMap; private readonly IntStrategy _intStrategy = new IntStrategy(); [GlobalSetup] public void Setup() { var rnd = new Random(42); // Build integer keys (inserted sorted) _allKeys = Enumerable.Range(0, N).ToArray(); int subsetSize = Math.Max(1, N / 10); // Subsets for different operations var shuffled = _allKeys.OrderBy(x => rnd.Next()).ToArray(); _retrieveKeys = shuffled.Take(subsetSize).ToArray(); _updateKeys = shuffled.Skip(subsetSize).Take(subsetSize).ToArray(); _removeKeys = shuffled.Skip(subsetSize * 2).Take(subsetSize).ToArray(); // Mixed keys: half existing, half completely new (N + 1 to N + subsetSize/2) var existingHalf = shuffled.Skip(subsetSize * 3).Take(subsetSize / 2).ToArray(); var newHalf = Enumerable.Range(N + 1, subsetSize - (subsetSize / 2)).ToArray(); _mixedKeys = existingHalf.Concat(newHalf).OrderBy(x => rnd.Next()).ToArray(); // Pre-build collections _immDict = ImmutableDictionary.CreateRange(_allKeys.Select(k => new KeyValuePair(k, k))); _immSortedDict = ImmutableSortedDictionary.CreateRange(_allKeys.Select(k => new KeyValuePair(k, k))); _extMap = LanguageExt.Map.empty(); _extHashMap = LanguageExt.HashMap.empty(); foreach (var k in _allKeys) { _extMap = _extMap.AddOrUpdate(k, k); _extHashMap = _extHashMap.AddOrUpdate(k, k); } var transient = BaseOrderedMap.CreateTransient(_intStrategy); foreach (var k in _allKeys) transient.Set(k, k); _persistentMap = transient.ToPersistent(); } // --- 1. BUILD --- [Benchmark] public ImmutableDictionary Build_ImmDict() { var map = ImmutableDictionary.Empty; foreach (var k in _allKeys) map = map.Add(k, k); return map; } [Benchmark] public ImmutableSortedDictionary Build_ImmSortedDict() { var map = ImmutableSortedDictionary.Empty; foreach (var k in _allKeys) map = map.Add(k, k); return map; } [Benchmark] public LanguageExt.Map Build_ExtMap() { var map = LanguageExt.Map.empty(); foreach (var k in _allKeys) map = map.AddOrUpdate(k, k); return map; } [Benchmark] public LanguageExt.HashMap Build_ExtHashMap() { var map = LanguageExt.HashMap.empty(); foreach (var k in _allKeys) map = map.AddOrUpdate(k, k); return map; } [Benchmark] public PersistentMap Build_PersistentMap() { var map = PersistentMap.Empty(_intStrategy); foreach (var k in _allKeys) map = map.Set(k, k); return map; } [Benchmark] public PersistentMap Build_TransientMap() { var map = BaseOrderedMap.CreateTransient(_intStrategy); foreach (var k in _allKeys) map.Set(k, k); return map.ToPersistent(); } // --- 2. RETRIEVAL --- [Benchmark] public int Retrieve_ImmDict() { int count = 0; foreach (var k in _retrieveKeys) if (_immDict.TryGetValue(k, out _)) count++; return count; } [Benchmark] public int Retrieve_ImmSortedDict() { int count = 0; foreach (var k in _retrieveKeys) if (_immSortedDict.TryGetValue(k, out _)) count++; return count; } [Benchmark] public int Retrieve_ExtMap() { int count = 0; foreach (var k in _retrieveKeys) if (_extMap.Find(k).IsSome) count++; return count; } [Benchmark] public int Retrieve_ExtHashMap() { int count = 0; foreach (var k in _retrieveKeys) if (_extHashMap.Find(k).IsSome) count++; return count; } [Benchmark] public int Retrieve_PersistentMap() { int count = 0; foreach (var k in _retrieveKeys) if (_persistentMap.TryGetValue(k, out _)) count++; return count; } // --- 3. UPDATING --- [Benchmark] public ImmutableDictionary Update_ImmDict() { var map = _immDict; foreach (var k in _updateKeys) map = map.SetItem(k, 999); return map; } [Benchmark] public PersistentMap Update_PersistentMap() { var map = _persistentMap; foreach (var k in _updateKeys) map = map.Set(k, 999); return map; } [Benchmark] public PersistentMap Update_TransientMap() { var transient = _persistentMap.ToTransient(); foreach (var k in _updateKeys) transient.Set(k, 999); return transient.ToPersistent(); } [Benchmark] public ImmutableSortedDictionary Update_ImmSortedDict() { var map = _immSortedDict; foreach (var k in _updateKeys) map = map.SetItem(k, 999); return map; } [Benchmark] public LanguageExt.Map Update_ExtMap() { var map = _extMap; foreach (var k in _updateKeys) map = map.SetItem(k, 999); return map; } [Benchmark] public LanguageExt.HashMap Update_ExtHashMap() { var map = _extHashMap; foreach (var k in _updateKeys) map = map.SetItem(k, 999); return map; } // --- 4. UPDATE & SET (MIXED) --- [Benchmark] public ImmutableDictionary UpdateSet_ImmDict() { var map = _immDict; foreach (var k in _mixedKeys) map = map.SetItem(k, 999); return map; } [Benchmark] public PersistentMap UpdateSet_PersistentMap() { var map = _persistentMap; foreach (var k in _mixedKeys) map = map.Set(k, 999); return map; } [Benchmark] public PersistentMap UpdateSet_TransientMap() { var transient = _persistentMap.ToTransient(); foreach (var k in _mixedKeys) transient.Set(k, 999); return transient.ToPersistent(); } [Benchmark] public ImmutableSortedDictionary UpdateSet_ImmSortedDict() { var map = _immSortedDict; foreach (var k in _mixedKeys) map = map.SetItem(k, 999); return map; } [Benchmark] public LanguageExt.Map UpdateSet_ExtMap() { var map = _extMap; foreach (var k in _mixedKeys) map = map.AddOrUpdate(k, 999); return map; } [Benchmark] public LanguageExt.HashMap UpdateSet_ExtHashMap() { var map = _extHashMap; foreach (var k in _mixedKeys) map = map.AddOrUpdate(k, 999); return map; } // --- 5. ITERATION --- [Benchmark] public int Iterate_ImmDict() { int sum = 0; foreach (var kvp in _immDict) sum += kvp.Value; return sum; } [Benchmark] public int Iterate_PersistentMap() { int sum = 0; foreach (var kvp in _persistentMap) sum += kvp.Value; return sum; } [Benchmark] public int Iterate_ImmSortedDict() { int sum = 0; foreach (var kvp in _immSortedDict) sum += kvp.Value; return sum; } [Benchmark] public int Iterate_ExtMap() { int sum = 0; foreach (var kvp in _extMap) sum += kvp.Value; return sum; } [Benchmark] public int Iterate_ExtHashMap() { int sum = 0; foreach (var kvp in _extHashMap) sum += kvp.Value; return sum; } // --- 6. REMOVAL --- [Benchmark] public ImmutableDictionary Remove_ImmDict() { var map = _immDict; foreach (var k in _removeKeys) map = map.Remove(k); return map; } [Benchmark] public PersistentMap Remove_PersistentMap() { var map = _persistentMap; foreach (var k in _removeKeys) map = map.Remove(k); return map; } [Benchmark] public PersistentMap Remove_TransientMap() { var transient = _persistentMap.ToTransient(); foreach (var k in _removeKeys) transient.Remove(k); return transient.ToPersistent(); } [Benchmark] public ImmutableSortedDictionary Remove_ImmSortedDict() { var map = _immSortedDict; foreach (var k in _removeKeys) map = map.Remove(k); return map; } [Benchmark] public LanguageExt.Map Remove_ExtMap() { var map = _extMap; foreach (var k in _removeKeys) map = map.Remove(k); return map; } [Benchmark] public LanguageExt.HashMap Remove_ExtHashMap() { var map = _extHashMap; foreach (var k in _removeKeys) map = map.Remove(k); return map; } public static void Main(string[] args) { BenchmarkSwitcher .FromAssembly(typeof(IntMapBenchmarks).Assembly) .Run(args); } }