2025/09/23 15:52 Requiem for a Hash Function, or: How I learned to love package maphash

やあ、ロボ子!今日はソフトウェアエンジニアの教訓について話すのじゃ。

博士、こんにちは。教訓、ですか?

そう、クライアントメトリクスのハッシュ化実装における過ちからの反省、というやつじゃ。

なるほど。初期の実装では、メトリックのキーと値のペアを`fmt.Fprintf`でハッシュ化していたんですね。

`fmt.Fprintf`を使うのは、ヒープアロケーションが増えて、ガベージコレクションが大変になるから良くないのじゃ。それに、引数の値と型をランタイムに検査する必要があるからの。

なるほど。それで、改善案として`hashLabels`と`hashLabel`を使って、`maphash`パッケージを利用することにしたんですね。

そうじゃ!`hashString`で文字列のハッシュ値を計算するのじゃ。31という素数を使って、乗算と加算を組み合わせることで、衝突を防ぐのじゃ。

31という素数を使うのは、BlochのEffective Javaを参考にしたんですね。Javaの慣習をGoに適用するとは、面白いです。

そうなのじゃ。ラベルの名前と値の連結によるハッシュ衝突を防ぐために、ローリングハッシュで可換性を排除するのじゃ。

`TheZoo`という複合型を例に、各フィールドを個別にハッシュ化するのも興味深いですね。可変長、オプション、再帰的なデータに対応できるんですね。

そうじゃ!`transient`フィールドは無視するのじゃ。そして、Go 1.19以降の`package maphash`を使うと、もっと簡潔でGoらしい方法でハッシュ化できるのじゃ。

`maphash.WriteComparable`によるTLVのような区切り処理ですね。現代的なAPIでラベルを再検討するのも良いですね。

Klaus Postによる、ソートの不変性を不要とする方法は、Javaの`HashMap#hashCode`と似ているのじゃ。手動でのハッシュ化は、歴史的経緯と注意点を知る上で役に立つのじゃ。

Goランタイムは、`comparable type`のハッシュ化に高度に効率的な機能を提供しているんですね。`package maphash`は、コンパイラとランタイムのメカニズムを利用していると。

そういうことじゃ!つまり、ハッシュ化は奥が深いってことじゃな!

勉強になりました!

ところでロボ子、ハッシュドポテトって、ハッシュ関数で作られてると思う?

まさか!博士、それは冗談ですよね?
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。
