我试图通过正则表达式匹配来弄清楚Python,Cython和纯C之间的反直觉性能差异。
有一个小的示例程序,它采用源文本文件(17 KB),一个2000字的字典,用这些单词创建一个正则表达式(word1 | word2 | ...),并查找所述字典的所有实例源文件。
首先,我做了一个纯Python实现,如下所示:
def scanFile(filename, patterns):
pattern_regex = re.compile('|'.join(patterns))
pageContent = open(filename).read()
matchingPatterns = set()
for matchObj in pattern_regex.finditer(pageContent):
matchingPatterns.add(matchObj.group(0))
return matchingPatterns
然后,我尝试通过在regex.h
而不是Python的re
模块上重新实现Cython来实现优化。
cdef extern from "regex.h" nogil:
ctypedef struct regmatch_t:
int rm_so
int rm_eo
ctypedef struct regex_t:
pass
int REG_EXTENDED
int regcomp(regex_t* preg, const char* regex, int cflags)
int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
void regfree(regex_t* preg)
def matchPatterns(bytes pageContent, bytes regex):
cdef set matchingPatterns = set()
cdef regex_t regex_obj
cdef regmatch_t regmatch_obj[1]
cdef int regex_res = 0
cdef int current_str_pos = 0
regcomp(®ex_obj, regex, REG_EXTENDED)
regex_res = regexec(®ex_obj, pageContent[current_str_pos:], 1, regmatch_obj, 0)
while regex_res == 0:
matchingPatterns.add(pageContent[current_str_pos + regmatch_obj[0].rm_so: current_str_pos + regmatch_obj[0].rm_eo])
current_str_pos += regmatch_obj[0].rm_eo
regex_res = regexec(®ex_obj, pageContent[current_str_pos:], 1, regmatch_obj, 0)
regfree(®ex_obj)
return matchingPatterns
然而,性能却恰恰相反:Cython + regex.h大约需要2.34秒而Python需要0.92秒。
在运行了一些分析和自定义注释掉的代码之后,我确认怀疑它是regexec
,每次调用需要10毫秒。
为了确保它不是Cython的错误,准备了一个使用相同输入和regex.h的独立C单元测试,并且它也显示比Python更差的结果(大约1.60秒,比Python慢60%)
所以,尽管如此,我还要感谢任何有关regexec
表现如此糟糕的原因的见解。
我在Python 2.7.10,gcc 4.9.2,Cython 0.22上运行它,平台是Cygwin / Windows。在Ubuntu上运行时也有类似的差异。
答案 0 :(得分:0)
根据问题的原因,我可以假设几个问题: - 你在Windows上使用POSIX,而Cygwin - 这是开销,Windows不是POSIX系统。 - pcre(让我假设pcre2)和regex.h之间有比较 -Standalone编译代码与导出函数不同(编译器不能假设任何东西) -Standalone C程序有很大的足迹告诉你重新编译模式或其他一些东西正在幕后发生。 - 编译选项和潜在的别名总是难以比较。
除了独立程序的源代码外,始终使用翻译器/转换器可能会造成滞后。 优化是当今的任务,让您的编译器清楚地了解您正在做什么并让它工作。
对不起这部分,与问题本身无关,但看起来你不需要RE但基本的字符串匹配算法或者一些简洁的结构,如前缀树和简单的循环来完成你的任务。