Python:并行编译正则表达式

Python:并行编译正则表达式,python,regex,parallel-processing,multiprocessing,Python,Regex,Parallel Processing,Multiprocessing,我有一个程序,我需要编译几千个大型正则表达式,所有这些都将被多次使用。问题是,cProfiler,需要花费太长的时间(113秒)才能re.compile()。(顺便说一句,编译后使用所有小于1.3秒的正则表达式进行搜索。) 如果我不预编译,它只是将问题推迟到我实际搜索时,因为re.search(expr,text)隐式编译expr。实际上,情况更糟,因为每次我使用正则表达式时,re都会重新编译整个正则表达式列表 我试过使用,但实际上这会减慢速度。下面是一个小测试来演示: ## rgxparall

我有一个程序,我需要编译几千个大型正则表达式,所有这些都将被多次使用。问题是,
cProfiler
,需要花费太长的时间(113秒)才能
re.compile()
。(顺便说一句,编译后使用所有小于1.3秒的正则表达式进行搜索。)

如果我不预编译,它只是将问题推迟到我实际搜索时,因为
re.search(expr,text)
隐式编译
expr
。实际上,情况更糟,因为每次我使用正则表达式时,
re
都会重新编译整个正则表达式列表

我试过使用,但实际上这会减慢速度。下面是一个小测试来演示:

## 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秒
  • 加上启动和停止进程的开销,4个处理器上的多处理比串行处理器慢25%以上

    我还尝试将正则表达式列表划分为4个子列表,并将子列表而不是单个表达式划分为
    pool.map
    。这给了一个小的性能提升,但我仍然无法得到比serial慢25%的速度

    有没有比串行编译更快的方法

    编辑: 更正了正则表达式编译的运行时间

    我也试过使用,但由于GIL,只使用了一个处理器。它略好于
    多处理
    (130秒对136秒),但仍然比串行(113秒)慢

    编辑2:
    我意识到一些正则表达式可能会被复制,所以我添加了一个dict来缓存它们。这缩短了约30秒。不过,我仍然对并行化感兴趣。目标机器有8个处理器,这将把编译时间减少到约15秒。

    虽然我非常喜欢python,但我认为解决方案是,用perl或C等语言来完成


    如果希望将主程序保留在python中,可以使用
    subprocess
    调用perl脚本(只需确保在尽可能少的
    子进程调用中传递尽可能多的值,以避免开销。

    有比多进程更轻的解决方案来实现任务执行的异步性,如线程和协程。虽然python2在同时运行事物的能力方面受到限制,但python3主要使用这样的asyn基本类型中的同步实现。只需使用python3运行代码,您就会看到区别:

    $ python2 --version
    Python 2.7.17
    $ python2 -m timeit -n 1 -s "import rgxparallel as r" "r.serial_compile(r.l)"
    1 loops, best of 3: 3.65 sec per loop
    $ python -m timeit -n 1 -s "import multire as r" "r.parallel_compile(r.l)"
    1 loops, best of 3: 3.95 sec per loop
    
    $ python3 --version
    Python 3.6.9
    $ python3 -m timeit -n 1 -s "import multire as r" "r.serial_compile(r.l)"
    1 loops, best of 3: 0.72 usec per loop
    $ python3 -m timeit -n 1 -s "import multire as r" "r.parallel_compile(r.l)"
    ...
    1 loops, best of 3: 4.51 msec per loop
    

    别忘了用python3版本的
    range
    更改
    xrange

    为什么你有这么多大的正则表达式,却只对它们进行很少的搜索?你能简化它们吗?或者用普通的旧字符串操作替换它们,或者根本不运行其中的一些?搜索的时间只用于搜索一次e整个列表。非常重要的是,单个列表搜索的时间要短,因为用户(和我的雇主)会期望得到近乎即时的响应。我尽可能地简化了,这是在不删掉主要功能的情况下我能得到的最好结果。(搜索词的实际列表约为200000个项目;我有代码,只要有可能就切换到简单的字符串函数,但仍有约5000个正则表达式。)你试过用线程来代替吗?每个cpu 1个线程,正则表达式在它们之间分配?正则表达式是用C实现的,所以尽管有GIL,你应该可以获得相当高的并行度。我应该链接它=)我本来想试试,但是线程文档中的这个警告让我感到不快(我使用的是CPython):在CPython中,由于全局解释器锁,一次只能有一个线程执行Python代码(即使某些面向性能的库可能会克服此限制)。如果您希望应用程序更好地利用多核计算机的计算资源,建议您使用多处理。但是,如果您希望同时运行多个I/O绑定任务,线程仍然是合适的模型。