在使用pyre2
(https://github.com/axiak/pyre2)时,遇到了性能问题(匹配时间)。
我有三个程序:
使用内置 re 模块的纯 Python:https ://gist.github.com/1873402
使用 Pyre2 的 Python:https ://gist.github.com/1873402 。(大部分代码与no.1程序相同。除了使用内置re时,它将utf-8字符串解码为unicode,使用pyre2时不需要)
使用 re2 的 C/C++:https ://gist.github.com/1873417
我测量了两次:正则表达式预编译时间和匹配时间。
1号节目:1.65s 1.25s
2号程序:0.04s 1.8s
3号程序:0.02s 0.8s
它们都由相同的正则表达式和输入提供。(所有正则表达式都支持re2
)
然后我按照有关 Cython 分析的文档进行操作。得到以下结果:
ncalls tottime percall cumtime percall filename:lineno(function) 652884 16.477 0.000 25.349 0.000 re2.pyx:394(_search) 9479 6.059 0.001 41.806 0.004 export_plain.py:60(匹配) 652884 4.243 0.000 33.602 0.000 {“re2.Pattern”对象的“搜索”方法} 652884 4.010 0.000 29.359 0.000 re2.pyx:442(搜索) 652884 3.056 0.000 3.056 0.000 re2.pyx:114(__init__) 652953 2.145 0.000 2.145 0.000 {实例} 652884 2.002 0.000 2.002 0.000 re2.pyx:123(__dealloc__) 652953 1.911 0.000 1.911 0.000 re2.pyx:75(unicode_to_bytestring) 652953 1.902 0.000 1.902 0.000 re2.pyx:86(pystring_to_bytestring) 1 0.330 0.330 42.492 42.492 export_plain.py:98(export_fname) 9479 0.173 0.000 0.173 0.000 {内置方法子} 10000 0.120 0.000 0.120 0.000 {“str”对象的“拆分”方法} 8967 0.063 0.000 0.099 0.000 re2.pyx:801(获取) 10069 0.061 0.000 0.061 0.000 {方法'str'对象的'strip'} 69 0.043 0.001 0.146 0.002 re2.pyx:806(prepare_pattern) 9036 0.038 0.000 0.038 0.000 re2.pyx:788(__next) 69 0.022 0.000 0.169 0.002 re2.pyx:905(_compile) 1 0.005 0.005 0.177 0.177 export_plain.py:36(加载) 69 0.002 0.000 0.003 0.000 re2.pyx:784(__init__) 69 0.001 0.000 0.170 0.002 re2.pyx:763(编译) 38 0.001 0.000 0.001 0.000 {“文件”对象的“写入”方法} 69 0.001 0.000 0.171 0.002 {re2.compile} 1 0.001 0.001 42.669 42.669 export_plain.py:160(主要) 3 0.000 0.000 0.000 0.000 {开} 69 0.000 0.000 0.000 0.000 {“列表”对象的“附加”方法} 19 0.000 0.000 0.000 0.000 {“str”对象的“join”方法} 1 0.000 0.000 0.000 0.000 通用路径.py:38(isdir) 1 0.000 0.000 42.669 42.669 export_plain.py:153(run_re2_test) 1 0.000 0.000 0.000 0.000 {posix.stat} 4 0.000 0.000 0.000 0.000 {时间.时间} 1 0.000 0.000 0.000 0.000 posixpath.py:59(加入) 1 0.000 0.000 42.670 42.670 :1() 1 0.000 0.000 0.000 0.000 {“unicode”对象的“编码”方法} 3 0.000 0.000 0.000 0.000 {“str”对象的“rfind”方法} 2 0.000 0.000 0.000 0.000 posixpath.py:109(基本名称) 1 0.000 0.000 0.000 0.000 posixpath.py:117(目录名) 1 0.000 0.000 0.000 0.000 stat.py:40(S_ISDIR) 2 0.000 0.000 0.000 0.000 {len} 1 0.000 0.000 0.000 0.000 {“列表”对象的“扩展”方法} 1 0.000 0.000 0.000 0.000 {“str”对象的“startswith”方法} 1 0.000 0.000 0.000 0.000 {“str”对象的方法“endswith”} 1 0.000 0.000 0.000 0.000 stat.py:24(S_IFMT) 1 0.000 0.000 0.000 0.000 {“文件”对象的方法“__enter__”} 1 0.000 0.000 0.000 0.000 {方法'禁用''_lsprof.Profiler'对象}
看起来_search
函数 (re2.pyx:393) 占用了太多时间。但我不知道这与纯 C 版本之间有何不同。
PS:Pyre2 修订:提交543f228
re2 修订:变更集:79:0c439a6bd795
我猜实际Match
函数(re2.pyx:424)大部分时间都在这个函数中。
然后我将 Match 函数重构为 cdef 函数_my_match
,以便我可以在配置文件结果中看到它,还将StringPiece
分配重构为 cdef 函数_alloc_sp
。(修改细节:https ://gist.github.com/1873993 )重新配置它,然后得到:
2012 年 2 月 20 日星期一 20:52:47 Profile.prof 3975043 次函数调用在 28.265 CPU 秒内 排序:内部时间 ncalls tottime percall cumtime percall filename:lineno(function) 652884 10.060 0.000 20.230 0.000 re2.pyx:452(搜索) 652884 4.131 0.000 28.201 0.000 {“re2.Pattern”对象的“搜索”方法} 652884 3.647 0.000 3.647 0.000 re2.pyx:394(_my_match) 9479 3.037 0.000 31.238 0.003 export_plain.py:62(匹配) 652884 2.901 0.000 2.901 0.000 re2.pyx:443(_alloc_sp) 652953 1.814 0.000 1.814 0.000 re2.pyx:86(pystring_to_bytestring) 652953 1.808 0.000 1.808 0.000 re2.pyx:75(unicode_to_bytestring) 1 0.332 0.332 31.926 31.926 export_plain.py:96(export_fname) 9479 0.169 0.000 0.169 0.000 {内置方法子} 10000 0.122 0.000 0.122 0.000 {“str”对象的“拆分”方法} 8967 0.065 0.000 0.099 0.000 re2.pyx:849(获取) 10069 0.064 0.000 0.064 0.000 {方法'str'对象的'strip'} 69 0.042 0.001 0.142 0.002 re2.pyx:854(prepare_pattern) 9036 0.035 0.000 0.035 0.000 re2.pyx:836(__next) 69 0.023 0.000 0.166 0.002 re2.pyx:953(_compile) 1 0.003 0.003 32.103 32.103 export_plain.py:158(主要) 1 0.003 0.003 0.174 0.174 export_plain.py:36(加载) 69 0.002 0.000 0.168 0.002 re2.pyx:811(编译) 38 0.001 0.000 0.001 0.000 {“文件”对象的“写入”方法} 69 0.001 0.000 0.169 0.002 {re2.compile} 69 0.001 0.000 0.001 0.000 re2.pyx:832(__init__) 1 0.001 0.001 32.104 32.104 export_plain.py:151(run_re2_test) 1 0.000 0.000 32.105 32.105 :1() 2 0.000 0.000 0.000 0.000 {len} 3 0.000 0.000 0.000 0.000 {开} 1 0.000 0.000 0.000 0.000 {“列表”对象的“扩展”方法} 69 0.000 0.000 0.000 0.000 {实例} 69 0.000 0.000 0.000 0.000 {“列表”对象的“附加”方法} 19 0.000 0.000 0.000 0.000 {“str”对象的“join”方法} 4 0.000 0.000 0.000 0.000 {时间.时间} 1 0.000 0.000 0.000 0.000 {“unicode”对象的“编码”方法} 1 0.000 0.000 0.000 0.000 posixpath.py:59(加入) 1 0.000 0.000 0.000 0.000 {posix.stat} 1 0.000 0.000 0.000 0.000 通用路径.py:38(isdir) 2 0.000 0.000 0.000 0.000 posixpath.py:109(基本名称) 3 0.000 0.000 0.000 0.000 {“str”对象的“rfind”方法} 1 0.000 0.000 0.000 0.000 posixpath.py:117(目录名) 1 0.000 0.000 0.000 0.000 stat.py:40(S_ISDIR) 1 0.000 0.000 0.000 0.000 {“str”对象的“startswith”方法} 1 0.000 0.000 0.000 0.000 {“str”对象的方法“endswith”} 1 0.000 0.000 0.000 0.000 {“文件”对象的方法“__enter__”} 1 0.000 0.000 0.000 0.000 stat.py:24(S_IFMT) 1 0.000 0.000 0.000 0.000 {方法'禁用''_lsprof.Profiler'对象}
但是search
仍然占用了很多时间(总共 10.060)。
任何人都可以弄清楚是什么问题?