web-dev-qa-db-ja.com

Python 3.4と比較してPython 3.5ではstr.translateがはるかに速いのはなぜですか?

Python 3.4のtext.translate()を使用して、指定された文字列から不要な文字を削除しようとしていました。

最小限のコードは次のとおりです。

import sys 
s = 'abcde12345@#@$#%$'
mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$')
print(s.translate(mapper))

期待どおりに動作します。ただし、Python 3.4とPython 3.5で同じプログラムを実行すると、大きな違いが生じます。

タイミングを計算するコードは

python3 -m timeit -s "import sys;s = 'abcde12345@#@$#%$'*1000 ; mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$'); "   "s.translate(mapper)"

Python 3.4プログラムは1.3msを取りますが、Pythonの同じプログラムは3.5は26.4μsのみを取ります。

Python 3.5と比較してPython 3.4と比較して高速になった点は何ですか?

116
Bhargav Rao

TL; DR- ISSUE 21118


長い物語

Josh Rosenbergは、str.translate()関数がbytes.translateと比較して非常に遅いことを発見し、 issue を上げて、次のように述べました。

Python 3では、str.translate()は通常、最適化ではなくパフォーマンスの悲観化です。

str.translate()が遅かったのはなぜですか?

str.translate()が非常に遅い主な理由は、ルックアップがPython辞書にあったためです。

maketransを使用すると、この問題が悪化しました。 bytes を使用する同様のアプローチでは、256項目のC配列を作成して、高速なテーブル検索を行います。したがって、より高いレベルのPython dictを使用すると、Python 3.4のstr.translate()が非常に遅くなります。

今何が起きたの?

最初のアプローチは、小さなパッチ translate_writer を追加することでしたが、速度の向上はそれほど快適ではありませんでした。すぐに別のパッチ fast_translate がテストされ、最大55%の高速化という非常に素晴らしい結果が得られました。

ファイルからわかる主な変更点は、Python辞書検索がCレベルの検索に変更されたことです。

速度はbytesとほぼ同じになりました

                                unpatched           patched

str.translate                   4.55125927699919    0.7898181750006188
str.translate from bytes trans  1.8910855210015143  0.779950579000797

ここでの小さな注意点は、パフォーマンスの向上がASCII文字列でのみ顕著であることです。

J.F. Sebastianが下記の comment で言及しているように、3.5以前では、翻訳はASCIIと非ASCIIの両方のケースで同じように機能していました。ただし、3.5からASCIIの場合ははるかに高速です。

以前のASCIIと非ASCIIはほぼ同じでしたが、今ではパフォーマンスに大きな変化が見られます。

この answer に見られるように、71.6μsから2.33μsに改善することができます。

次のコードはこれを示しています

python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
100000 loops, best of 3: 2.3 usec per loop
python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 117 usec per loop

python3 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 91.2 usec per loop
python3 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
10000 loops, best of 3: 101 usec per loop

結果の表:

         Python 3.4    Python 3.5  
Ascii     91.2          2.3 
Unicode   101           117
150
Bhargav Rao