1. 導入:なぜstd::unordered_mapの「再ハッシュ」を知る必要があるのか
C++のstd::unordered_mapは、キーと値のペアを非常に高速に検索できる便利なコンテナです。しかし、要素が増えるたびに内部で「再ハッシュ」という処理が自動的に行われることをご存知でしょうか。この処理は全データを移動させるため、一度発生するとプログラムが一瞬止まるような負荷がかかります。特にゲーム開発やリアルタイムシステムでは、この「予期せぬ停止」が命取りになります。本記事では、この再ハッシュを賢く制御し、パフォーマンスを安定させる方法を解説します。
2. 基礎知識:負荷率と再ハッシュの仕組み
std::unordered_mapは、内部に「バケット」と呼ばれる箱を並べてデータを管理しています。
負荷率(load factor)とは、「現在の要素数 ÷ バケット数」で計算される値です。この値が一定を超えると、コンテナは「箱が混雑して検索が遅くなってきた」と判断し、バケット数を増やして全データを配置し直します。これが再ハッシュ(rehash)です。再ハッシュにはO(N)という大きな計算コストがかかるため、頻繁に発生させないことが重要です。
3. 実装と解決策:事前に予約して無駄な処理を防ぐ
再ハッシュを防ぐ最も効果的な方法は、あらかじめ必要な要素数をコンテナに伝えておくことです。C++には、メモリを事前に確保するreserve()関数と、どの程度の混雑度で再ハッシュするかを決めるmax_load_factor()関数があります。これらを組み合わせることで、意図しないタイミングでの再ハッシュを回避できます。
4. サンプルプログラム
以下のコードをコピーして動作を確認してみてください。事前に要素数を確保することで、追加時の再ハッシュを最小限に抑える例です。
include <iostream>
include <unordered_map>
include <string>
int main() {
// コンテナを作成
std::unordered_map<std::string, int> myMap;
// 1. 負荷率の閾値を調整する (デフォルトは1.0程度)
// 0.7に設定することで、早めにバケットを増やし衝突を減らす(検索性能重視)
myMap.max_load_factor(0.7f);
// 2. あらかじめ1000個分のメモリを確保しておく
// これにより、要素追加時の動的な再割り当てを抑制できる
myMap.reserve(1000);
// データの挿入テスト
for(int i = 0; i < 1000; ++i) {
myMap["item" + std::to_string(i)] = i;
}
std::cout <= "要素数: " << myMap.size() << std::endl;
std::cout <= "現在の負荷率: " << myMap.load_factor() << std::endl;
return 0;
}
5. 応用・注意点:現場で陥りやすい罠
現場での開発で注意すべきポイントが2つあります。
まず、reserve()の引数は「バケット数」ではなく「要素数」であるという点です。内部のバケット数は、この数よりも多くなることがあるため、必要以上にメモリを確保しすぎないように注意が必要です。
次に、リアルタイム性が極めて重要なシステムでは、std::unordered_mapの代わりに、要素数が固定された配列や、ハッシュ衝突を許容しない別のデータ構造を検討することも重要です。再ハッシュは「便利だがコストが高い」機能であることを常に意識し、プログラムの性質に合わせて調整を行いましょう。

コメント