Skip to content

Commit

Permalink
Add GxHasher - Blazingly Fast hash
Browse files Browse the repository at this point in the history
  • Loading branch information
Wsm2110 committed Nov 16, 2024
1 parent 22df8a2 commit dbf2b26
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 60 deletions.
84 changes: 41 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ map.Remove(1);

### Custom Hasher

By default, Faster.Map provides four built-in hashers, each optimized for different performance characteristics:

DefaultHasher
GxHasher
XxHash3StringHasher
XxHash3Hasher

You can provide your own `IHasher<TKey>` implementation to customize hash computation.

#### Step 1: Implement a Custom Hasher
Expand Down Expand Up @@ -234,49 +241,40 @@ BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3880/23H2/2023Update/SunValley3
| RobinhoodMap | 1000000 | 14.54652 ms | 0.24280 ms | 0.27960 ms | 14.55285 ms | 736 B |
| Dictionary | 1000000 | 19.09788 ms | 0.37338 ms | 0.39952 ms | 19.06965 ms | 736 B |


### Add and resize

| Method | Length | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated |
|--------------|----------|-------------:|-----------:|-------------:|-------------:|----------:|----------:|----------:|------------:|
| **DenseMap** | **1000** | **0.08942 ms** | **0.00234 ms** | **0.00675 ms** | **0.08880 ms** | **-** | **-** | **-** | **37.75 KB** |
| RobinhoodMap | 1000 | 0.09152 ms | 0.00414 ms | 0.01181 ms | 0.08890 ms | - | - | - | 37.51 KB |
| Dictionary | 1000 | 0.06777 ms | 0.00519 ms | 0.01522 ms | 0.06260 ms | - | - | - | 72.09 KB |
| **DenseMap** | **10000** | **0.22937 ms** | **0.00458 ms** | **0.01061 ms** | **0.22780 ms** | **-** | **-** | **-** | **290.31 KB** |
| RobinhoodMap | 10000 | 0.34865 ms | 0.00692 ms | 0.00769 ms | 0.34860 ms | - | - | - | 578.18 KB |
| Dictionary | 10000 | 0.39528 ms | 0.01529 ms | 0.04484 ms | 0.37380 ms | - | - | - | 657.93 KB |
| **DenseMap** | **100000** | **1.29055 ms** | **0.01850 ms** | **0.01545 ms** | **1.29280 ms** | **-** | **-** | **-** | **2306.88 KB** |
| RobinhoodMap | 100000 | 2.34004 ms | 0.03486 ms | 0.05428 ms | 2.32295 ms | - | - | - | 4610.78 KB |
| Dictionary | 100000 | 3.32307 ms | 0.06625 ms | 0.11776 ms | 3.29280 ms | - | - | - | 5896.77 KB |
| **DenseMap** | **400000** | **5.04851 ms** | **0.09965 ms** | **0.25364 ms** | **5.05355 ms** | **-** | **-** | **-** | **9219.25 KB** |
| RobinhoodMap | 400000 | 9.31596 ms | 0.18625 ms | 0.30602 ms | 9.23980 ms | - | - | - | 18435.23 KB |
| Dictionary | 400000 | 11.52668 ms | 0.40434 ms | 1.12042 ms | 11.20910 ms | - | - | - | 25374.59 KB |
| **DenseMap** | **900000** | **12.38025 ms** | **0.22981 ms** | **0.62521 ms** | **12.22785 ms** | **-** | **-** | **-** | **18435.44 KB** |
| RobinhoodMap | 900000 | 22.62544 ms | 0.69841 ms | 2.01507 ms | 21.66310 ms | - | - | - | 36867.18 KB |
| Dictionary | 900000 | 31.53625 ms | 0.85001 ms | 2.43885 ms | 31.31300 ms | 1000.0000 | 1000.0000 | 1000.0000 | 52626.84 KB |
| **DenseMap** | **1000000** | **21.15451 ms** | **0.40257 ms** | **0.43075 ms** | **21.11780 ms** | **-** | **-** | **-** | **36867.63 KB** |
| RobinhoodMap | 1000000 | 26.03638 ms | 0.41907 ms | 0.37149 ms | 26.13220 ms | - | - | - | 36867.46 KB |
| Dictionary | 1000000 | 34.51489 ms | 0.68455 ms | 0.89011 ms | 34.59730 ms | 1000.0000 | 1000.0000 | 1000.0000 | 52626.84 KB |

### Get string benchmark using XXHash3StringHasher

| Method | Length | Mean | Error | StdDev | Allocated |
|--------------|----------|-------------:|-----------:|-----------:|----------:|
| **DenseMap** | **10000** | **0.06328 ms** | **0.00119 ms** | **0.00117 ms** | **-** |
| RobinhoodMap | 10000 | 0.11138 ms | 0.00089 ms | 0.00083 ms | - |
| Dictionary | 10000 | 0.14030 ms | 0.00101 ms | 0.00084 ms | - |
| **DenseMap** | **100000** | **0.89303 ms** | **0.00092 ms** | **0.00081 ms** | **1 B** |
| RobinhoodMap | 100000 | 1.74953 ms | 0.00542 ms | 0.00507 ms | 1 B |
| Dictionary | 100000 | 1.82837 ms | 0.00251 ms | 0.00222 ms | 1 B |
| **DenseMap** | **400000** | **5.69521 ms** | **0.08819 ms** | **0.07364 ms** | **6 B** |
| RobinhoodMap | 400000 | 17.48812 ms | 0.11409 ms | 0.10672 ms | 23 B |
| Dictionary | 400000 | 8.90541 ms | 0.10513 ms | 0.09319 ms | 12 B |
| **DenseMap** | **900000** | **25.03465 ms** | **0.15671 ms** | **0.12235 ms** | **23 B** |
| RobinhoodMap | 900000 | 52.43294 ms | 0.71473 ms | 0.63359 ms | 74 B |
| Dictionary | 900000 | 29.69477 ms | 0.19468 ms | 0.15200 ms | 23 B |
| **DenseMap** | **1000000** | **33.72319 ms** | **0.28449 ms** | **0.25219 ms** | **49 B** |
| RobinhoodMap | 1000000 | 62.24546 ms | 0.39693 ms | 0.37129 ms | 92 B |
| Dictionary | 1000000 | 34.00727 ms | 0.10688 ms | 0.08925 ms | 49 B |
### Get String Benchmark

| Method | Length | Mean | Error | StdDev | Allocated |
|------------------|-------- |-----------:|-----------:|-----------:|----------:|
| DenseMap_Default | 10000 | 0.125 ms | 0.001 ms | 0.000 ms | - |
| DenseMap_Xxhash3 | 10000 | 0.090 ms | 0.001 ms | 0.001 ms | - |
| **DenseMap_GxHash** | **10000** | **0.088 ms** | **0.000 ms** | **0.000 ms** | - |
| RobinhoodMap | 10000 | 0.109 ms | 0.001 ms | 0.001 ms | - |
| Dictionary | 10000 | 0.139 ms | 0.000 ms | 0.000 ms | - |
| DenseMap_Default | 100000 | 1.571 ms | 0.011 ms | 0.008 ms | 1 B |
| DenseMap_Xxhash3 | 100000 | 1.313 ms | 0.002 ms | 0.002 ms | 1 B |
| **DenseMap_GxHash** | **100000** | **1.230 ms** | **0.004 ms** | **0.003 ms** | 1 B |
| RobinhoodMap | 100000 | 1.801 ms | 0.007 ms | 0.006 ms | 1 B |
| Dictionary | 100000 | 1.830 ms | 0.003 ms | 0.003 ms | 1 B |
| DenseMap_Default | 400000 | 8.602 ms | 0.168 ms | 0.173 ms | 12 B |
| DenseMap_Xxhash3 | 400000 | 6.453 ms | 0.032 ms | 0.025 ms | 6 B |
| **DenseMap_GxHash** | **400000** | **6.191 ms** | **0.043 ms** | **0.041 ms** | 6 B |
| RobinhoodMap | 400000 | 17.033 ms | 0.155 ms | 0.138 ms | 23 B |
| Dictionary | 400000 | 8.841 ms | 0.061 ms | 0.051 ms | 12 B |
| DenseMap_Default | 800000 | 29.155 ms | 0.132 ms | 0.117 ms | 23 B |
| DenseMap_Xxhash3 | 800000 | 21.616 ms | 0.241 ms | 0.214 ms | 23 B |
| **DenseMap_GxHash** | **800000** | **17.977 ms** | **0.086 ms** | **0.081 ms** | 23 B |
| RobinhoodMap | 800000 | 42.700 ms | 0.187 ms | 0.156 ms | 61 B |
| Dictionary | 800000 | 25.280 ms | 0.086 ms | 0.081 ms | 23 B |
| DenseMap_Default | 900000 | 34.148 ms | 0.348 ms | 0.325 ms | 49 B |
| DenseMap_Xxhash3 | 900000 | 25.203 ms | 0.087 ms | 0.073 ms | 23 B |
| **DenseMap_GxHash** | **900000** | **21.758 ms** | **0.105 ms** | **0.087 ms** | 23 B |
| RobinhoodMap | 900000 | 51.350 ms | 0.184 ms | 0.172 ms | 74 B |
| Dictionary | 900000 | 29.715 ms | 0.076 ms | 0.063 ms | 23 B |
| DenseMap_Default | 1000000 | 42.762 ms | 0.282 ms | 0.264 ms | 61 B |
| DenseMap_Xxhash3 | 1000000 | 30.722 ms | 0.087 ms | 0.081 ms | 23 B |
| **DenseMap_GxHash** | **1000000** | **25.237 ms** | **0.070 ms** | **0.066 ms** | 23 B |
| RobinhoodMap | 1000000 | 61.434 ms | 0.364 ms | 0.322 ms | 92 B |
| Dictionary | 1000000 | 34.179 ms | 0.650 ms | 0.798 ms | 49 B |


### Add string benchmark
Expand Down
34 changes: 30 additions & 4 deletions benchmarks/Faster.Map.Benchmark/StringBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class StringBenchmark
#region Fields

private DenseMap<string, string> _denseMap;
private DenseMap<string, string> _denseMapxxHash;
private DenseMap<string, string> _denseMapGxHash;
private Dictionary<string, string> _dictionary;
private RobinhoodMap<string, string> _robinhoodMap;

Expand All @@ -25,7 +27,7 @@ public class StringBenchmark

#region Properties

[Params(10000, 100000, 400000, 900000, 1000000)]
[Params(10000, 100000, 400000, 800000, 900000, 1000000)]
public uint Length { get; set; }

#endregion
Expand All @@ -50,27 +52,51 @@ public void Setup()
uint length = BitOperations.RoundUpToPowerOf2(Length);
int dicLength = HashHelpers.GetPrime((int)Length);

_denseMap = new DenseMap<string, string>(length, 0.875, new XxHash3StringHasher());
_denseMap = new DenseMap<string, string>(length, 0.875);

_denseMapxxHash = new DenseMap<string, string>(length, 0.875, new XxHash3StringHasher());
_denseMapGxHash = new DenseMap<string, string>(length, 0.875, new GxHasher());

_dictionary = new Dictionary<string, string>(dicLength);
_robinhoodMap = new RobinhoodMap<string, string>(length);
_robinhoodMap = new RobinhoodMap<string, string>(length * 2);

foreach (var key in keys)
{
_dictionary.Add(key, key);
_denseMap.Emplace(key, key);
_denseMapxxHash.Emplace(key, key);
_denseMapGxHash.Emplace(key, key);
_robinhoodMap.Emplace(key, key);
}
}

[Benchmark]
public void DenseMap()
public void DenseMap_Default()
{
foreach (var key in keys)
{
_denseMap.Get(key, out var result);
}
}

[Benchmark]
public void DenseMap_Xxhash3()
{
foreach (var key in keys)
{
_denseMapxxHash.Get(key, out var result);
}
}

[Benchmark]
public void DenseMap_GxHash()
{
foreach (var key in keys)
{
_denseMapGxHash.Get(key, out var result);
}
}

[Benchmark]
public void RobinhoodMap()
{
Expand Down
3 changes: 0 additions & 3 deletions src/DenseMap.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) 2024, Wiljan Ruizendaal. All rights reserved. <[email protected]>
// Distributed under the MIT Software License, Version 1.0.

#if NET7_0_OR_GREATER

using Faster.Map.Contracts;
using Faster.Map.Hasher;
using System;
Expand Down Expand Up @@ -1031,4 +1029,3 @@ internal struct Entry
};
}

#endif
24 changes: 14 additions & 10 deletions src/Faster.Map.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,19 @@
<CopyRight>MIT</CopyRight>
<PackageReleaseNotes>
New Features:

Add custom hasher:

- GxHash
- XX3Hash
- XX3HashString

Enhanced emplace Behavior: The emplace function now operates with improved efficiency:
Duplicate Management: When a duplicate entry is detected, emplace updates the existing entry instead of creating a new one.
New Entry Insertion: If no duplicate is found, a new entry is added.

Optimizations:

Smart Tombstone Management: Introduced an optimized approach to manage and avoid tombstones, enhancing data integrity and reducing unnecessary overhead.
</PackageReleaseNotes>
<PackageProjectUrl>https://github.com/Wsm2110/Faster.Map</PackageProjectUrl>
<AssemblyVersion>6.1.1</AssemblyVersion>
<FileVersion>6.1.1</FileVersion>
<AssemblyVersion>6.1.2</AssemblyVersion>
<FileVersion>6.1.2</FileVersion>
<Title>Fastest .net hashmap</Title>
<Version>6.1.1</Version>
<Version>6.1.2</Version>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Description>
Incredibly fast (concurrent) hashmap
Expand Down Expand Up @@ -53,6 +52,11 @@
<PackageReference Include="System.IO.Hashing" Version="9.0.0-rc.2.24473.5" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'net7.0'">
<PackageReference Include="GxHash">
<Version>2.0.0</Version>
</PackageReference>
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
Expand Down
38 changes: 38 additions & 0 deletions src/Hasher/GxHasher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#if NET8_0_OR_GREATER

using Faster.Map.Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using GxHash;

namespace Faster.Map.Hasher
{
public class GxHasher : IHasher<string>
{
// https://github.com/ogxd/gxhash-csharp/blob/main/GxHash/

// Arbitrary constants with high entropy. Hexadecimal digits of pi were used.
public const ulong ARBITRARY0 = 0x243f6a8885a308d3;
public const ulong ARBITRARY1 = 0x13198a2e03707344;
public const ulong ARBITRARY2 = 0xa4093822299f31d0;
public const ulong ARBITRARY3 = 0x082efa98ec4e6c89;
public const ulong ARBITRARY4 = 0x452821e638d01377;
public const ulong ARBITRARY5 = 0xbe5466cf34e90c6c;
public const ulong ARBITRARY6 = 0xc0ac29b7c97c50dd;
public const ulong ARBITRARY7 = 0x3f84d5b5b5470917;
public const ulong ARBITRARY8 = 0x9216d5d98979fb1b;
public const ulong ARBITRARY9 = 0xd1310ba698dfb5ac;

UInt128 _seed = (UInt128) 0x243f6a8885a308d3 * 0x3f84d5b5b5470917;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong ComputeHash(string key)
{
return GxHash.GxHash.HashU64(MemoryMarshal.AsBytes(key.AsSpan()), _seed);
}
}
}
#endif
2 changes: 2 additions & 0 deletions src/Hasher/XxHash3Hasher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public ulong ComputeHash(T key)
return XxHash3.HashToUInt64(span);
}
}


}

0 comments on commit dbf2b26

Please sign in to comment.