Skip to content

Instantly share code, notes, and snippets.

@Epicguru
Created March 31, 2025 00:15
Show Gist options
  • Save Epicguru/1c816350caa433679c9d08e58af4aa8f to your computer and use it in GitHub Desktop.
Save Epicguru/1c816350caa433679c9d08e58af4aa8f to your computer and use it in GitHub Desktop.
using BenchmarkDotNet.Attributes;
namespace Testbed;
// Measure garbage collection and performance of HashCode.Combine.
[MemoryDiagnoser]
public class HashBenchmark
{
[Benchmark(Baseline = true)]
public void RealImplementation()
{
var hs = new Dictionary<UnityStruct, int>(400);
// Produces 80 hash collisions
for (int x = 0; x < 20; ++x)
{
for (int y = 0; y < 20; ++y)
{
var vector = new UnityStruct(x, y, 0);
hs[vector] = x;
}
}
var random = new Random(0);
for (int i = 0; i < 1000; i++)
{
hs.TryGetValue(new UnityStruct(random.Next(0, 20), random.Next(0, 20), 0), out _);
hs.ContainsKey(new UnityStruct(random.Next(0, 20), random.Next(0, 20), 0));
hs.TryAdd(new UnityStruct(random.Next(0, 200), random.Next(0, 200), 0), 5);
}
}
[Benchmark]
public void AlternativeHash()
{
var hs = new Dictionary<AlternativeStruct, int>(400);
// Produces 0 hash collisions
for (int x = 0; x < 20; ++x)
{
for (int y = 0; y < 20; ++y)
{
var vector = new AlternativeStruct(x, y, 0);
hs[vector] = x;
}
}
var random = new Random(0);
for (int i = 0; i < 1000; i++)
{
hs.TryGetValue(new AlternativeStruct(random.Next(0, 20), random.Next(0, 20), 0), out _);
hs.ContainsKey(new AlternativeStruct(random.Next(0, 20), random.Next(0, 20), 0));
hs.TryAdd(new AlternativeStruct(random.Next(0, 200), random.Next(0, 200), 0), 5);
}
}
[Benchmark]
public void FixedHash()
{
var hs = new Dictionary<FixedHashStruct, int>(400);
// Produces 400 hash collisions
for (int x = 0; x < 20; ++x)
{
for (int y = 0; y < 20; ++y)
{
var vector = new FixedHashStruct(x, y, 0);
hs[vector] = x;
}
}
var random = new Random(0);
for (int i = 0; i < 1000; i++)
{
hs.TryGetValue(new FixedHashStruct(random.Next(0, 20), random.Next(0, 20), 0), out _);
hs.ContainsKey(new FixedHashStruct(random.Next(0, 20), random.Next(0, 20), 0));
hs.TryAdd(new FixedHashStruct(random.Next(0, 200), random.Next(0, 200), 0), 5);
}
}
}
public struct UnityStruct : IEquatable<UnityStruct>
{
public int X;
public int Y;
public int Z;
public UnityStruct(int x, int y, int z)
{
X = x;
Y = y;
Z = z;
}
public readonly override int GetHashCode()
{
var yHash = Y.GetHashCode();
var zHash = Z.GetHashCode();
return X.GetHashCode() ^ (yHash << 4) ^ (yHash >> 28) ^ (zHash >> 4) ^ (zHash << 28);
}
public bool Equals(UnityStruct other) => X == other.X && Y == other.Y && Z == other.Z;
public override bool Equals(object? obj) => obj is UnityStruct other && Equals(other);
}
public struct AlternativeStruct : IEquatable<AlternativeStruct>
{
public int X;
public int Y;
public int Z;
public AlternativeStruct(int x, int y, int z)
{
X = x;
Y = y;
Z = z;
}
public readonly override int GetHashCode()
{
return HashCode.Combine(X, Y, Z);
}
public bool Equals(AlternativeStruct other) => X == other.X && Y == other.Y && Z == other.Z;
public override bool Equals(object? obj) => obj is AlternativeStruct other && Equals(other);
}
public struct FixedHashStruct : IEquatable<FixedHashStruct>
{
public int X;
public int Y;
public int Z;
public FixedHashStruct(int x, int y, int z)
{
X = x;
Y = y;
Z = z;
}
public readonly override int GetHashCode() => 1234;
public bool Equals(FixedHashStruct other) => X == other.X && Y == other.Y && Z == other.Z;
public override bool Equals(object? obj) => obj is FixedHashStruct other && Equals(other);
}
@Epicguru
Copy link
Author

Results on .NET 9:

| Method             | Mean        | Error     | StdDev    | Ratio  | RatioSD | Gen0   | Gen1   | Allocated | Alloc Ratio |
|------------------- |------------:|----------:|----------:|-------:|--------:|-------:|-------:|----------:|------------:|
| RealImplementation |    29.30 us |  0.526 us |  0.492 us |   1.00 |    0.02 | 1.8311 | 0.4272 |  90.24 KB |        1.00 |
| AlternativeHash    |    38.36 us |  0.656 us |  0.582 us |   1.31 |    0.03 | 1.8311 | 0.4272 |  90.24 KB |        1.00 |
| FixedHash          | 3,022.60 us | 24.142 us | 22.582 us | 103.17 |    1.84 |      - |      - |  90.24 KB |        1.00 |

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment