17

この質問に答えるとき(そして同様の質問に対するこの答えを読んだとき)、私はPythonが正規表現をキャッシュする方法を知っていると思いました。

しかし、2つのシナリオを比較して、テストすると思いました。

  1. 単純な正規表現の単一のコンパイル、次にそのコンパイルされた正規表現の10個のアプリケーション。
  2. コンパイルされていない正規表現の10個のアプリケーション(正規表現を1回コンパイルしてからキャッシュし、キャッシュで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の間で正規表現のキャッシュが変更されましたか?ドキュメントはそれを示唆していないようです。

4

1 に答える 1

27

コード変更されました。

Python 2.7では、キャッシュは単純な辞書です。複数の_MAXCACHEアイテムが保存されている場合は、新しいアイテムを保存する前にキャッシュ全体がクリアされます。キャッシュルックアップは、単純なキーを作成して辞書をテストするだけです。2.7の実装を参照してください。_compile()

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

他の人も同じ速度低下に気づき、Pythonバグトラッカーに問題16389を提出しました。3.4の方がはるかに高速になると思います。lru_cache実装が改善されるか、モジュールreが再びカスタムキャッシュに移動します。

更新:リビジョン4b4dddd670d0(hg)/ 0f606a6(git)では、キャッシュの変更は3.1で見つかった単純なバージョンに戻されました。Pythonバージョン3.2.4および3.3.1には、そのリビジョンが含まれています。

それ以降、Python 3.7では、パターンキャッシュが通常のカスタムFIFOキャッシュ実装dictに更新されました(挿入順序に依存し、LRUとは異なり、キャッシュに既に存在するアイテムが削除時に使用された最近の項目は考慮されません)。

于 2013-02-07T17:16:59.423 に答える