Skip to content

Commit 4dbf1be

Browse files
committed
FIx: use hasher instead of object.GetHashCode()
1 parent 594d2e4 commit 4dbf1be

File tree

8 files changed

+469
-73
lines changed

8 files changed

+469
-73
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,13 @@ Faster.Map allows for **custom hashing algorithms** to optimize performance base
105105
To specify a custom hash function, you can use **BlitzMap** or **DenseMap** with an explicitly defined hash:
106106

107107
```csharp
108-
var map = new BlitzMap<int, string, XXHash3>();
108+
var map = new BlitzMap<int, string, XxHash3StringHasher>();
109109
map.Insert(1, "Value One");
110110
map.Insert(2, "Value Two");
111111
```
112112

113+
Different Hasher implementations can be found in Faster.Map.Hasher
114+
113115
Using **custom hashing** can significantly reduce collisions and improve lookup times, especially for large datasets.
114116

115117
## Tested on platforms:

benchmarks/Faster.Map.Benchmark/GetBenchmark.cs

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class GetBenchmark
2929

3030
#region Properties
3131

32-
[Params(/*0.1, 0.2, 0.3, 0.4, 0.5,*/ 0.6 /*, 0.7, 0.8*/)]
32+
[Params(/*0.1, 0.2, 0.3, 0.4, 0.5,*/ 0.6/*, 0.7, 0.8*/)]
3333
public static double LoadFactor { get; set; }
3434

3535
[Params(134_217_728)]
@@ -43,7 +43,7 @@ public class GetBenchmark
4343
[GlobalSetup]
4444
public void Setup()
4545
{
46-
var rnd = new FastRandom(6);
46+
var rnd = new FastRandom(3);
4747
var uni = new HashSet<uint>((int)Length * 2);
4848
while (uni.Count < (uint)(Length * LoadFactor))
4949
{
@@ -64,8 +64,8 @@ public void Setup()
6464

6565
foreach (var key in keys)
6666
{
67-
//_dictionary.Add(key, key);
68-
//_denseMap.Emplace(key, key);
67+
_dictionary.Add(key, key);
68+
_denseMap.Emplace(key, key);
6969
_blitz.Insert(key, key);
7070
_robinHoodMap.Emplace(key, key);
7171
}
@@ -81,34 +81,34 @@ public void BlitzMap()
8181
}
8282
}
8383

84-
//[Benchmark]
85-
//public void DenseMap()
86-
//{
87-
// for (int i = 0; i < keys.Length; i++)
88-
// {
89-
// var key = keys[i];
90-
// _denseMap.Get(key, out var _);
91-
// }
92-
//}
93-
94-
//[Benchmark]
95-
//public void Dictionary()
96-
//{
97-
// for (int i = 0; i < keys.Length; i++)
98-
// {
99-
// var key = keys[i];
100-
// _dictionary.TryGetValue(key, out var _);
101-
// }
102-
//}
103-
104-
//[Benchmark]
105-
//public void RobinhoodMap()
106-
//{
107-
// for (int i = 0; i < keys.Length; i++)
108-
// {
109-
// var key = keys[i];
110-
// _robinHoodMap.Get(key, out var _);
111-
// }
112-
//}
84+
[Benchmark]
85+
public void DenseMap()
86+
{
87+
for (int i = 0; i < keys.Length; i++)
88+
{
89+
var key = keys[i];
90+
_denseMap.Get(key, out var _);
91+
}
92+
}
93+
94+
[Benchmark]
95+
public void Dictionary()
96+
{
97+
for (int i = 0; i < keys.Length; i++)
98+
{
99+
var key = keys[i];
100+
_dictionary.TryGetValue(key, out var _);
101+
}
102+
}
103+
104+
[Benchmark]
105+
public void RobinhoodMap()
106+
{
107+
for (int i = 0; i < keys.Length; i++)
108+
{
109+
var key = keys[i];
110+
_robinHoodMap.Get(key, out var _);
111+
}
112+
}
113113
}
114114
}

benchmarks/Faster.Map.Benchmark/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ static void Main(string[] args)
1010
//BenchmarkRunner.Run<AddBenchmark>();
1111
// BenchmarkRunner.Run<EnumerableBenchmark>();
1212
//BenchmarkRunner.Run<UpdateBenchmark>();
13-
BenchmarkRunner.Run<LargeStringCustomHasherBenchmark>(/*new DebugInProcessConfig()*/);
13+
BenchmarkRunner.Run<GetBenchmark>(/*new DebugInProcessConfig()*/);
1414
}
1515
}
1616
}

src/BlitzMap.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -717,8 +717,8 @@ public void Copy(BlitzMap<TKey, TValue> other)
717717
[MethodImpl(MethodImplOptions.AggressiveInlining)]
718718
private uint FindLastBucket(ref Bucket bucketBase, ref Entry entryBase, uint target)
719719
{
720-
// Directly calculate the hash code and index without intermediate variables
721-
var index = (uint)Unsafe.Add(ref entryBase, target).Key.GetHashCode() & _mask;
720+
var key = Unsafe.Add(ref entryBase, target).Key;
721+
var index = _hasher.ComputeHash(key) & _mask;
722722

723723
// Pointer to the initial bucket
724724
ref var bucket = ref Unsafe.Add(ref bucketBase, index);
@@ -856,7 +856,7 @@ private uint FindEmptyBucket(ref Bucket bucketBase, uint index, uint cint)
856856
var bucket = index;
857857

858858
// Check if the bucket is empty
859-
if (Unsafe.Add(ref bucketBase, bucket).Signature == INACTIVE ||
859+
if (Unsafe.Add(ref bucketBase, ++bucket).Signature == INACTIVE ||
860860
Unsafe.Add(ref bucketBase, ++bucket).Signature == INACTIVE)
861861
{
862862
return bucket;

src/DenseMap.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
namespace Faster.Map;
1414

15-
1615
/// <summary>
1716
/// A specialized implementation of <see cref="DenseMap{TKey, TValue, THasher}"/> that
1817
/// defaults to using the <see cref="GoldenRatioHasher{TKey}"/> for efficient hashing.
@@ -197,7 +196,7 @@ public IEnumerable<TValue> Values
197196
#endregion
198197

199198
#region Fields
200-
private const uint ElementsInGroupMinusOne = 15;
199+
201200
private const sbyte _emptyBucket = -127;
202201
private const sbyte _tombstone = -126;
203202
private static readonly Vector128<sbyte> _emptyBucketVector = Vector128.Create(_emptyBucket);

src/Faster.Map.csproj

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,21 @@
99
<PackageReadmeFile>README.md</PackageReadmeFile>
1010
<CopyRight>MIT</CopyRight>
1111
<PackageReleaseNotes>
12-
✨ What’s New?
13-
🏎️ Incredible Hashing Speed with Faster.Hash
14-
15-
Optimized Performance: Harnesses hardware-accelerated AES instructions for lightning-fast hashing.
16-
Deterministic and Reliable: Ensures consistent and collision-resistant hashes for uint, ulong, and arbitrary byte spans.
17-
Highly Flexible: Hash support for various data types:
18-
HashU64(uint source): Quickly hash 32-bit integers.
19-
HashU64(ulong source): Hash 64-bit integers with efficiency.
20-
HashU64(ReadOnlyspan): Process byte arrays with incredible speed.
21-
22-
🗂️ Map Enhancements
23-
24-
Seamlessly integrates with Faster.Hash to deliver high-speed key lookups and optimized map operations.
25-
Improved handling of large datasets with reduced memory overhead and faster hash-based operations.
26-
27-
🛡️ Secure Hashing with HashU64Secure
28-
29-
Generate stronger hashes with additional randomness and encryption rounds for scenarios requiring enhanced data integrity.
30-
12+
Adding blitzmap a new highly optimized hashmap
3113
</PackageReleaseNotes>
3214
<PackageProjectUrl>https://github.com/Wsm2110/Faster.Map</PackageProjectUrl>
33-
<AssemblyVersion>6.1.4</AssemblyVersion>
34-
<FileVersion>6.1.4</FileVersion>
15+
<AssemblyVersion>7.0.1</AssemblyVersion>
16+
<FileVersion>7.0.1</FileVersion>
3517
<Title>Fastest .net hashmap</Title>
36-
<Version>6.1.4</Version>
18+
<Version>7.0.1</Version>
3719
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
3820
<Description>
39-
Incredibly fast (concurrent) hashmap
21+
Incredibly fast hashmap
4022
</Description>
4123
<RepositoryUrl>https://github.com/Wsm2110/Faster.Map</RepositoryUrl>
4224
<RepositoryType>git</RepositoryType>
4325
<PackageLicenseExpression>MIT</PackageLicenseExpression>
44-
<PackageTags>Hashmap Hashtable Dictionary Faster.map performance, concurrency, simd, concurrenthashmap concurrentdictionary</PackageTags>
26+
<PackageTags>Hashmap, Hashtable, Dictionary, Faster.map, performance, concurrency, simd, concurrenthashmap concurrentdictionary</PackageTags>
4527
</PropertyGroup>
4628

4729
<PropertyGroup>

src/Hasher/DefaultHasher.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public uint ComputeHash(ulong x)
7272
/// Uses bit shifts and multiplications with high-entropy constants
7373
/// to produce a well-distributed hash.
7474
/// </summary>
75-
internal readonly struct DefaultUintHasher : IHasher<uint>
75+
public readonly struct DefaultUintHasher : IHasher<uint>
7676
{
7777
/// <summary>
7878
/// Computes a high-quality hash for a given <see cref="uint"/> value.
@@ -82,16 +82,25 @@ public uint ComputeHash(ulong x)
8282
[MethodImpl(MethodImplOptions.AggressiveInlining)]
8383
public uint ComputeHash(uint x)
8484
{
85-
// Initial XOR shift to mix high and low bits
85+
//For a comprehensive understanding of the XMX mixer and its applications, you can refer to Jon Maiga's detailed article: "The construct of a bit mixer."
8686
x ^= x >> 15;
87-
// Multiply by a high-entropy constant for mixing
88-
x *= 0x2C1B3C6D;
89-
// Another XOR shift to spread the bits
87+
x *= 0x85ebca6b;
9088
x ^= x >> 13;
91-
// Another multiplication with a different high-entropy constant
92-
x *= 0x297A2D39;
93-
// Final XOR shift for additional diffusion
94-
x ^= x >> 15;
89+
x *= 0xc2b2ae35;
90+
x ^= x >> 16;
9591
return x;
92+
93+
94+
//// Initial XOR shift to mix high and low bits
95+
//x ^= x >> 15;
96+
//// Multiply by a high-entropy constant for mixing
97+
//x *= 0x2C1B3C6D;
98+
//// Another XOR shift to spread the bits
99+
//x ^= x >> 13;
100+
//// Another multiplication with a different high-entropy constant
101+
//x *= 0x297A2D39;
102+
//// Final XOR shift for additional diffusion
103+
//x ^= x >> 15;
104+
//return x;
96105
}
97106
}

0 commit comments

Comments
 (0)