【发布时间】:2013-08-18 06:26:01
【问题描述】:
我有一个程序需要编译数千个大型正则表达式,所有这些都将被多次使用。问题是,re.compile() 他们需要很长时间(根据cProfiler,113 秒)。 (顺便说一句,实际上使用所有这些正则表达式进行搜索
如果我不预编译,它只会将问题推迟到我实际搜索的时候,因为re.search(expr, text) 隐式编译expr。实际上,情况更糟,因为re 每次我使用它们时都会重新编译整个正则表达式列表。
我尝试使用multiprocessing,但这实际上会减慢速度。这是一个小测试来演示:
## rgxparallel.py ##
import re
import multiprocessing as mp
def serial_compile(strings):
return [re.compile(s) for s in strings]
def parallel_compile(strings):
print("Using {} processors.".format(mp.cpu_count()))
pool = mp.Pool()
result = pool.map(re.compile, strings)
pool.close()
return result
l = map(str, xrange(100000))
还有我的测试脚本:
#!/bin/sh
python -m timeit -n 1 -s "import rgxparallel as r" "r.serial_compile(r.l)"
python -m timeit -n 1 -s "import rgxparallel as r" "r.parallel_compile(r.l)"
# Output:
# 1 loops, best of 3: 6.49 sec per loop
# Using 4 processors.
# Using 4 processors.
# Using 4 processors.
# 1 loops, best of 3: 9.81 sec per loop
我猜并行版本是:
- 并行编译和酸洗正则表达式,约 2 秒
- 在串行中,取消酸洗,因此重新编译它们,大约 6.5 秒
加上启动和停止进程的开销,multiprocessing 在 4 个处理器上比串行的要慢 25% 以上。
我还尝试将正则表达式列表划分为 4 个子列表,并 pool.map-ing 子列表,而不是单个表达式。这稍微提升了性能,但我仍然无法比串行慢约 25%。
有没有比串口编译更快的方法?
编辑: 修正了正则表达式编译的运行时间。
我也尝试使用threading,但由于 GIL,只使用了一个处理器。它比multiprocessing 略好(130 秒对 136 秒),但仍然比串行(113 秒)慢。
编辑 2: 我意识到有些正则表达式可能会重复,所以我添加了一个 dict 来缓存它们。这缩短了约 30 秒。不过,我仍然对并行化感兴趣。目标机器有 8 个处理器,这会将编译时间减少到大约 15 秒。
【问题讨论】:
-
为什么你有这么多大的正则表达式,却只用它们做这么少的搜索?你能简化它们,也许用普通的旧字符串操作替换它们,或者完全避免运行其中的一些?
-
搜索时间是对整个列表的一次使用。单个列表搜索的时间很短,这一点非常重要,因为用户(和我的雇主)会期待近乎即时的响应。我尝试尽可能地简化,这是在不削减主要功能的情况下我能得到的最好的。 (实际的搜索词列表约为 200,000 项;我的代码尽可能切换到简单的字符串函数,但仍留下约 5,000 个正则表达式。)
-
您是否尝试过使用线程?每个 cpu 1 个线程和正则表达式在它们之间划分?正则表达式是在 C 中实现的,因此尽管有 GIL,但您应该获得不错的并行度。
-
我应该链接那个xkcd.com/1171 =)
-
我本来打算试试的,但我被线程文档中的这个警告推迟了(我正在使用 CPython):在 CPython 中,由于全局解释器锁,只有一个线程可以执行Python 代码一次(即使某些面向性能的库可能会克服这个限制)。如果您希望您的应用程序更好地利用多核机器的计算资源,建议您使用多处理。但是,如果您想同时运行多个 I/O 密集型任务,线程仍然是一个合适的模型。
标签: python regex parallel-processing multiprocessing