てくメモ

trivial な notes

【C#】(int, int).GetHashCode の衝突

以下の記事によれば、-1000~1000 の範囲で(int, int).GetHashCodeを検証したところ、98.3%衝突していたということ。
C#でDictionaryのキーに2つのintを使いたい場合の性能比較 (ただしキーの範囲は[-32768, 32767])


数字が衝撃的なので、自分でも確認してみる。


結論からいえば、現状の CoreCLR だと異なる結果となった。

オンラインサンドボックスで変わり目を試すことができたので、この記事では次のサービスによる実行結果を用いる。
C# Online Compiler | .NET Fiddle


以下のコードを使う。
範囲を -512~512 としているが、これはサービスのメモリ制限の都合。

using System;
using System.Collections.Generic;
					
public class Program
{
	public static void Main()
	{
		var hashes = new HashSet<int>();
		const int start = -512, end = 512;
		int count = 0;
		for (int x = start; x <= end; x++)
		{
			for (int y = start; y <= end; y++)
			{
				var hashCode = (x, y).GetHashCode();
				if (hashes.Add(hashCode))
					count++;
			}
		}

		const int len = end - start + 1;
		const double total = len * len;
		Console.WriteLine($"count={count}, collision rate={(total - count) / total:0.000%}");
	}
}


上のコードを、compiler = .NET CORE 3.1 で実行すると以下。

count=34849, collision rate=96.683%

冒頭記事の検証環境である Unity は Mono であり .NET CORE 3.1 と等価ではないが、この部分は同様そうで、大惨事な衝突状況となった。


次に、compiler = .NET 5 で実行すると以下。

count=1050625, collision rate=0.000%

🤗

🔎「Unity CoreCLR 移行 いつ」

Unity だけではなく例えば競プロなどでも C# は環境が古かったりするので、C# のよくないところ(C# が悪いわけじゃないけど)が出ているという感じがする。


ということで、該当環境でないか確認すれば、(int, int).GetHashCodeは問題なさそう。