17

在回答这个问题时(并阅读了类似问题的答案),我认为我知道 Python 如何缓存正则表达式。

但后来我想我会测试它,比较两种情况:

  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 之间是否发生了变化?文档似乎没有暗示这一点。

4

1 回答 1

27

代码更改。

在 Python 2.7 中,缓存是一个简单的字典;如果其中存储了多个_MAXCACHE项目,则在存储新项目之前清除整个缓存。缓存查找只需要构建一个简单的键并测试字典,请参见2.7 的实现_compile()

在 Python 3.x 中,缓存已被@functools.lru_cache(maxsize=500, typed=True)装饰器取代。这个装饰器做更多的工作,包括一个线程锁、调整缓存 LRU 队列和维护缓存统计信息(可通过 访问re._compile.cache_info())。请参阅 和 的3.3.0实现_compile()functools.lru_cache()

其他人也注意到了同样的减速,并在 Python bugtracker 中提交了问题 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 回答