1. ホーム
  2. python

[解決済み] なぜPython 3.5はPython 3.4に比べてstr.translateがずっと速いのですか?

2022-05-15 22:45:23

質問

与えられた文字列から不要な文字を削除するために 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と比べて何が改善され、より高速になったのでしょうか?

どのように解決するのですか?

TL;DR - ISSUE 21118


長い話

ジョシュ・ローゼンバーグが発見したのは str.translate() 関数と比較すると非常に遅いことがわかりました。 bytes.translate を発生させた。 問題 と述べています。

Python3では str.translate() は通常、最適化ではなく、性能の悲観化です。

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

の主な理由は str.translate() が非常に遅い主な理由は、ルックアップがPythonの辞書に使用されていたからです。

の使い方は maketrans の使用はこの問題を悪化させました。同様の手法で bytes を使うと、テーブルを高速に検索するために 256 項目の C 配列を構築します。そのため、より高レベルのPythonの dictstr.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 が言及しているように、これは コメント 3.5以前は、ASCIIケースと非ASCIIケースの両方で翻訳が同じように動作していました。しかし、3.5からはASCIIケースの方がはるかに速くなりました。

以前は ASCII と non-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