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


116

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

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

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と比較して高速化された点は何ですか。


11
パフォーマンスについて話している間は、次のようにマッパーを生成するほうがよいdict.fromkeys(ord(c) for c in '@#$')でしょう。
トーマスK

1
@ThomasK私はこれが大きな違いを生むことを知りました。うん、あなたの方法はより良いです。
Bhargav Rao

50倍速いという意味ですか?
assylias

@assylias 1300-26.4を実行し、1300で割った。95%近くになったので、次のように書いた。私は数学が少し苦手です。すぐに数学を学びます。:)
Bhargav Rao

3
26/1300 = 2%なので、遅いバージョンがかかる時間の2%しかかかりません=> 50倍高速です。
アッシリア2015

回答:


148

TL; DR-問題21118


長い物語

Josh Rosenberg str.translate()は、関数がに比べて非常に遅いことを発見しbytes.translate問題を提起して、次のように述べています。

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

なぜstr.translate()遅いのですか?

str.translate()非常に遅くなる主な理由は、以前はPython辞書で検索を行っていたためです。

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

今何が起きたの?

最初のアプローチは小さなパッチ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文字列でのみ顕著であることです。

JFSebastianが以下のコメントで言及しているように、3.5より前の翻訳は、ASCIIと非ASCIIの両方のケースで同じように機能するように使用されていました。ただし、3.5以降のASCIIケースははるかに高速です。

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

この回答に示されているように、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

13
これは、コミットのいずれかです。github.com/python/cpython/commit/...
filmor

注:ASCIIと非ASCIIの場合は、パフォーマンスが大幅に異なる場合があります。それは55%についてではありません。あなたの答えが示すように、スピードアップは1000s%になる可能性があります
jfs

比較:python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"(ascii)とpython3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"(non-ascii)。後者ははるかに遅い(10倍)。
jfs

@JFああ、理解しました。私は3.4と3.5の両方でコードを実行しました。ASCII以外のものについては、Py3.4をより速く取得しています。偶然ですか?結果dpaste.com/15FKSDQ
Bhargavラオ

3.5より前では、asciiとnon-asciiの両方のケースはおそらくUnicodeで同じです。.translate()つまり、asciiのケースはPython 3.5のみではるかに高速です(bytes.translate()そこでのパフォーマンスは必要ありません)。
jfs
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.