web-dev-qa-db-ja.com

Pythonでの非常に大きな辞書のパフォーマンスの改善

最初に空の辞書を初期化し、forループで辞書に要素を追加すると(約110,000キー、各キーの値はリストで、ループでも増加します)、速度は次のように低下​​します。 forループに進みます。

問題は、初期化時にディクショナリがキーの数を認識せず、非常にスマートな処理を行っていないため、ストレージの衝突が非常に頻繁に発生し、速度が低下するためと思われます。

キーの数とそれらのキーを正確に知っている場合、pythonで辞書(またはハッシュテーブル)をより効率的に動作させる方法はありますか?キーを知っていれば、ハッシュ関数をスマートに設計し(完全なハッシュ?)、事前にスペースを割り当てることができることを漠然と覚えています。

52
szli

キーの数とそれらのキーを正確に知っている場合、pythonで辞書(またはハッシュテーブル)をより効率的に動作させる方法はありますか?キーを知っていれば、ハッシュ関数をスマートに設計し(完全なハッシュ?)、事前にスペースを割り当てることができることを漠然と覚えています。

Pythonは、辞書の「成長段階」を高速化するための事前サイズ設定オプションを公開していません。また、辞書の「配置」を直接制御することもできません。

ただし、キーが常に事前にわかっている場合は、キーを set に保存して、 dict.fromkeys() を使用して設定します。そのクラスメソッドは 設定されたサイズに基づいて辞書を事前にサイズ調整するように最適化されています であり、__ hash __()を新たに呼び出すことなく辞書を作成できます:

_>>> keys = {'red', 'green', 'blue', 'yellow', 'orange', 'pink', 'black'}
>>> d = dict.fromkeys(keys)  # dict is pre-sized to 32 empty slots
_

衝突を減らすことが目的であれば、辞書の挿入順序で実験を実行して、パイルアップを最小限に抑えることができます。 (KnuthのTAOCPの BrentのアルゴリズムDのバリエーション を見て、これがどのように行われるかを理解してください)。

辞書用の純粋なPythonモデル( this one など)を装備することにより、代替挿入順序の加重平均プローブ数をカウントできます。たとえば、dict.fromkeys([11100, 22200, 44400, 33300])を挿入すると、ルックアップごとに平均1.75プローブが挿入されます。これは、dict.fromkeys([33300, 22200, 11100, 44400])のルックアップごとの2.25平均プローブを上回ります。

もう1つの「秘rick」は、完全に読み込まれた辞書を 新しいキーを追加せずにサイズを大きくする sにだまして、空きを増やすことです。

_ d = dict.fromkeys(['red', 'green', 'blue', 'yellow', 'orange'])
 d.update(dict(d))     # This makes room for additional keys
                       # and makes the set collision-free.
_

最後に、すべての衝突を排除する目的で、キーに独自のカスタム__hash __()を導入できます(おそらく gperf )。

109