我一直在尝试进行模式匹配的课程。我的班级看起来像这样:
class Matcher(object):
def __init__(self, pattern):
self._re = re.compile(pattern)
def match(self, value):
return self._re.match(value)
总而言之,我的脚本需要大约45秒才能运行。作为实验,我将代码更改为:
class Matcher(object):
def __init__(self, pattern):
self._re = re.compile(pattern)
self.match = self._re.match
此脚本的运行耗时37秒。无论我重复这个过程多少次,我都会看到同样显着的性能提升。通过cProfile运行它会显示如下:
ncalls tottime percall cumtime percall filename:lineno(function)
46100979 14.356 0.000 14.356 0.000 {method 'match' of '_sre.SRE_Pattern' objects}
44839409 9.287 0.000 20.031 0.000 matcher.py:266(match)
为什么地球上的匹配方法在运行时增加了9.2秒? 最令人沮丧的部分是我试图重新创建一个简单的案例而无法这样做。我在这里缺少什么?我的简单测试用例有一个错误!现在它模仿我所看到的行为:
import re
import sys
import time
class X(object):
def __init__(self):
self._re = re.compile('.*a')
def match(self, value):
return self._re.match(value)
class Y(object):
def __init__(self):
self._re = re.compile('ba')
self.match = self._re.match
inp = 'ba'
x = X()
y = Y()
sys.stdout.write("Testing with a method...")
sys.stdout.flush()
start = time.time()
for i in range(100000000):
m = x.match(inp)
end = time.time()
sys.stdout.write("Done: "+str(end-start)+"\n")
sys.stdout.write("Testing with an attribute...")
sys.stdout.flush()
start = time.time()
for i in range(100000000):
m = y.match(inp)
end = time.time()
sys.stdout.write("Done: "+str(end-start)+"\n")
输出:
$ python speedtest.py
Testing with a method...Done: 50.6646981239
Testing with an attribute...Done: 35.5526258945
作为参考,使用pyp时两者都 更快,但在使用属性而不是方法运行时仍然显示出显着的增益:
$ pypy speedtest.py
Testing with a method...Done: 6.15996003151
Testing with an attribute...Done: 3.57215714455
答案 0 :(得分:8)
这可能主要是附加函数调用的开销。调用Python函数在性能上是相对昂贵的,因为需要设置额外的堆栈帧等。这是一个证明类似性能的简单示例:
>>> timeit.timeit("f()", "g = (lambda: 1); f = lambda: g()")
0.2858083918486847
>>> timeit.timeit("f()", "f = lambda: 1")
0.13749289364989004
在方法中进行两次额外的属性查询还需要额外费用:在_re
上查找self
,然后在match
对象上查找_re
。但是,这可能是一个较小的组件,因为字典查找在Python中非常快。 (上面的timeit
示例显示了相当差的性能,即使在双调用版本中只有一个额外的名称查找。)
答案 1 :(得分:0)
第一个版本每次都是额外的函数调用。这会产生一些开销。