2013-02-07 11 views
16

this questionと答えると(そして同様の質問にthis answerを読んだ)、私はPythonが正規表現をどのようにキャッシュしているかを知っていると思っていました。Python 3では、コンパイルされず、繰り返し使用される正規表現が非常に遅いのはなぜですか?

は、しかし、私は、私は2つのシナリオを比較し、それをテストしようと思いました:

  1. 簡単な正規表現の単一のコンパイル、コンパイル済み正規表現の後、10のアプリケーション。
  2. 10個のコンパイルされていない正規表現のアプリケーション(ここで正規表現は一度コンパイルしてからキャッシュに入れてから、キャッシュで9回調べなければならないため、パフォーマンスは少し悪化すると予想していました)。

しかし、結果は(Pythonの3.3で)ずらした:

>>> import timeit 
>>> timeit.timeit(setup="import re", 
... stmt='r=re.compile(r"\w+")\nfor i in range(10):\n r.search(" jkdhf ")') 
18.547793477671938 
>>> timeit.timeit(setup="import re", 
... stmt='for i in range(10):\n re.search(r"\w+"," jkdhf ")') 
106.47892003890324 

5.7倍以上に遅いです! Python 2.7では、まだ2.5倍の増加がありますが、これは予想以上のものです。

Python 2と3の間でregexesのキャッシュが変更されていますか? The docsはそれを示唆していないようです。

+0

ええ、なぜあなたは 'timeit'をそのように使っていますか?なぜ 'stmt = 're.search(...)'' /'stmt='r.search(...) ''' re.compile'を 'setup'に追加するのですか? – delnan

+2

ファンシーな 'functools.lru_cache'がここで問題になります。それはあなたの後のキャッシュの変更です。また、http://bugs.python.org/issue16389を参照してください。 – mmgp

+0

@delnan:私はタイミングの一部である正規表現の編集をしたかったのです。 –

答えて

24

コードにはが変更されています。

Python 2.7では、キャッシュは単純な辞書です。 _MAXCACHE個以上の項目が格納されている場合は、新しい項目を格納する前にキャッシュ全体がクリアされます。キャッシュルックアップは単純なキーを作成し、辞書をテストするだけです。2.7 implementation of _compile()

Python 3.xでは、キャッシュは@functools.lru_cache(maxsize=500, typed=True) decoratorに置き換えられました。このデコレータはであり、それ以上の作業はです。スレッドロック、キャッシュLRUキューの調整、およびキャッシュ統計の維持(re._compile.cache_info()からアクセス可能)が含まれます。 3.3 implementation of _compile()およびfunctools.lru_cache()を参照してください。

他の人は同じ減速に気付き、issue 16389をPython bugtrackerに提出しました。私は3.4がもっと速くなることを期待しています。 lru_cacheの実装が改善されるか、reモジュールがカスタムキャッシュに再度移動します。

アップデート:revision 4b4dddd670d0を使用すると、キャッシュの変更が3.1の単純なバージョンに戻りました。 Pythonのバージョン3.2.4と3.3.1にはそのリビジョンが含まれています。

+0

ハァッ。ありがとう。 strptimeのregexps用のキャッシュがlru_cacheを使用しない理由がわかりました。私はlru_cacheが理論的にlruがワイプと再起動よりも優れているので改善できることを願っています。 –

関連する問題