diff --git a/Benchmark/Benchmark.csproj b/Benchmark/Benchmark.csproj new file mode 100644 index 0000000..9752129 --- /dev/null +++ b/Benchmark/Benchmark.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0;net6.0;net7.0 + enable + enable + + + + + + + + + + + + diff --git a/Benchmark/BenchmarkFlatHashMap.cs b/Benchmark/BenchmarkFlatHashMap.cs new file mode 100644 index 0000000..200fdcc --- /dev/null +++ b/Benchmark/BenchmarkFlatHashMap.cs @@ -0,0 +1,77 @@ +using BenchmarkDotNet.Attributes; +using BetterCollections; + +namespace Benchmark; + +[MemoryDiagnoser] +[RPlotExporter] +public class BenchmarkFlatHashMap_Add1000 +{ + [Benchmark] + public FlatHashMap FlatHashMap() + { + var map = new FlatHashMap(); + for (var i = 0; i < 1000; i++) + { + map.Add(i, i); + } + return map; + } + + [Benchmark(Baseline = true)] + public Dictionary Dictionary() + { + var map = new Dictionary(); + for (var i = 0; i < 1000; i++) + { + map.Add(i, i); + } + return map; + } +} + +[MemoryDiagnoser] +[RPlotExporter] +public class BenchmarkFlatHashMap_Get1000 +{ + private FlatHashMap flatHashMap = new(); + private Dictionary dictionary = new(); + + [GlobalSetup] + public void Setup() + { + flatHashMap = new FlatHashMap(); + for (var i = 0; i < 1000; i++) + { + flatHashMap.Add(i, i); + } + + dictionary = new Dictionary(); + for (var i = 0; i < 1000; i++) + { + dictionary.Add(i, i); + } + } + + [Benchmark] + public int FlatHashMap() + { + var a = 0; + for (var i = 0; i < 1000; i++) + { + a += flatHashMap[i]; + } + return a; + } + + [Benchmark(Baseline = true)] + public int Dictionary() + { + var a = 0; + for (var i = 0; i < 1000; i++) + { + a += dictionary[i]; + } + return a; + } +} diff --git a/Benchmark/Program.cs b/Benchmark/Program.cs new file mode 100644 index 0000000..b986d99 --- /dev/null +++ b/Benchmark/Program.cs @@ -0,0 +1,3 @@ +using BenchmarkDotNet.Running; + +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); diff --git a/BetterCollections.sln b/BetterCollections.sln index 5de0d9f..51889ad 100644 --- a/BetterCollections.sln +++ b/BetterCollections.sln @@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterCollections", "Better EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{95336675-5EC6-470C-A4A9-A0D44B28C987}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "Benchmark\Benchmark.csproj", "{745D3703-B07D-4B10-95D5-A96AD07E6BE9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {95336675-5EC6-470C-A4A9-A0D44B28C987}.Debug|Any CPU.Build.0 = Debug|Any CPU {95336675-5EC6-470C-A4A9-A0D44B28C987}.Release|Any CPU.ActiveCfg = Release|Any CPU {95336675-5EC6-470C-A4A9-A0D44B28C987}.Release|Any CPU.Build.0 = Release|Any CPU + {745D3703-B07D-4B10-95D5-A96AD07E6BE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {745D3703-B07D-4B10-95D5-A96AD07E6BE9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {745D3703-B07D-4B10-95D5-A96AD07E6BE9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {745D3703-B07D-4B10-95D5-A96AD07E6BE9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/BetterCollections/Exceptions/UnexpectedConcurrentException.cs b/BetterCollections/Exceptions/UnexpectedConcurrentException.cs new file mode 100644 index 0000000..d85359f --- /dev/null +++ b/BetterCollections/Exceptions/UnexpectedConcurrentException.cs @@ -0,0 +1,12 @@ +using System; + +namespace BetterCollections.Exceptions; + +public class UnexpectedConcurrentException : Exception +{ + public UnexpectedConcurrentException() : this( + "A concurrent call occurred on a container that does not allow concurrency, and the internal state may have been corrupted") { } + + public UnexpectedConcurrentException(string message) : base(message) { } + public UnexpectedConcurrentException(string message, Exception inner) : base(message, inner) { } +} diff --git a/BetterCollections/Misc/IRefGet.cs b/BetterCollections/Misc/IRefGet.cs new file mode 100644 index 0000000..21d8176 --- /dev/null +++ b/BetterCollections/Misc/IRefGet.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace BetterCollections.Misc; + +public interface IGetter +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Get(in S source); +} + +public readonly struct IdentityGetter : IGetter +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Get(in T source) => source; +} + +public readonly struct KeyValueGetter : IGetter, (K Key, V Value)> +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public KeyValuePair Get(in (K Key, V Value) source) + => new(source.Key, source.Value); +} + +public readonly struct KeyGetter : IGetter, IGetter> +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public K Get(in (K Key, V Value) source) + => source.Key; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public K Get(in KeyValuePair source) + => source.Key; +} + +public readonly struct ValueGetter : IGetter, IGetter> +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public V Get(in (K Key, V Value) source) + => source.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public V Get(in KeyValuePair source) + => source.Value; +} diff --git a/BetterCollections/Misc/Utils.cs b/BetterCollections/Misc/Utils.cs index a20c8d4..a61d7dc 100644 --- a/BetterCollections/Misc/Utils.cs +++ b/BetterCollections/Misc/Utils.cs @@ -138,4 +138,18 @@ public static string ToBinaryString(this ulong value) return Regex.Replace(a, ".{8}(?!$)", "$0_"); #endif } + + /// + /// Evaluate whether a given integral value is a power of 2. + /// + /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPow2(long value) => (value & (value - 1)) == 0 && value > 0; + + /// + /// Evaluate whether a given integral value is a power of 2. + /// + /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPow2(ulong value) => (value & (value - 1)) == 0 && value > 0; }