Wil*_*uck 3 python performance
在我的一个类中,我有许多方法都可以从相同的字典中获取值.但是,如果其中一个方法试图访问不存在的值,则必须调用另一个方法来使该值与该键相关联.
我目前实现如下,其中findCrackDepth(tonnage)为self.lowCrackDepth [tonnage]赋值.
if tonnage not in self.lowCrackDepth:
self.findCrackDepth(tonnage)
lcrack = self.lowCrackDepth[tonnage]
Run Code Online (Sandbox Code Playgroud)
但是,我也可以这样做
try:
lcrack = self.lowCrackDepth[tonnage]
except KeyError:
self.findCrackDepth(tonnage)
lcrack = self.lowCrackDepth[tonnage]
Run Code Online (Sandbox Code Playgroud)
我假设两者之间的性能差异与值在字典中的频率有关.这个差异有多大?我正在生成几百万个这样的值(在该类的许多实例中分布在许多字典中),并且每次该值不存在时,它可能有两次.
Ale*_*lli 14
这是一个微妙的问题,因为你需要注意避免"持久的副作用",性能权衡取决于缺失键的百分比.因此,请考虑dil.py如下文件:
def make(percentmissing):
global d
d = dict.fromkeys(range(100-percentmissing), 1)
def addit(d, k):
d[k] = k
def with_in():
dc = d.copy()
for k in range(100):
if k not in dc:
addit(dc, k)
lc = dc[k]
def with_ex():
dc = d.copy()
for k in range(100):
try: lc = dc[k]
except KeyError:
addit(dc, k)
lc = dc[k]
def with_ge():
dc = d.copy()
for k in range(100):
lc = dc.get(k)
if lc is None:
addit(dc, k)
lc = dc[k]
Run Code Online (Sandbox Code Playgroud)
以及一系列timeit调用,例如:
$ python -mtimeit -s'import dil; dil.make(10)' 'dil.with_in()'
10000 loops, best of 3: 28 usec per loop
$ python -mtimeit -s'import dil; dil.make(10)' 'dil.with_ex()'
10000 loops, best of 3: 41.7 usec per loop
$ python -mtimeit -s'import dil; dil.make(10)' 'dil.with_ge()'
10000 loops, best of 3: 46.6 usec per loop
Run Code Online (Sandbox Code Playgroud)
这表明,如果缺少10%的密钥,则in检查是最快的方式.
$ python -mtimeit -s'import dil; dil.make(1)' 'dil.with_in()'
10000 loops, best of 3: 24.6 usec per loop
$ python -mtimeit -s'import dil; dil.make(1)' 'dil.with_ex()'
10000 loops, best of 3: 23.4 usec per loop
$ python -mtimeit -s'import dil; dil.make(1)' 'dil.with_ge()'
10000 loops, best of 3: 42.7 usec per loop
Run Code Online (Sandbox Code Playgroud)
只有1%的丢失的钥匙,该exception方法是稍微最快的(和get方法仍然在两种情况下最慢的一个).
因此,为了获得最佳性能,除非绝大多数(99%以上)的查找成功,否则这种in方法更可取.
当然,还有另一个优雅的可能性:添加像...这样的dict子类:
class dd(dict):
def __init__(self, *a, **k):
dict.__init__(self, *a, **k)
def __missing__(self, k):
addit(self, k)
return self[k]
def with_dd():
dc = dd(d)
for k in range(100):
lc = dc[k]
Run Code Online (Sandbox Code Playgroud)
然而...:
$ python -mtimeit -s'import dil; dil.make(1)' 'dil.with_dd()'
10000 loops, best of 3: 46.1 usec per loop
$ python -mtimeit -s'import dil; dil.make(10)' 'dil.with_dd()'
10000 loops, best of 3: 55 usec per loop
Run Code Online (Sandbox Code Playgroud)
...虽然光滑,但这不是性能优胜者 - 即使采用这种get方法,也可能更慢,只需使用更好看的代码即可.(在defaultdict语义上类似于这个dd类,如果适用的话,将是性能获胜,但这是因为__missing__在这种情况下,特殊方法是在经过充分优化的C代码中实现的).