这是我对 cProfile 的实验,看看我是否可以确认以上@yuvi 的测量结果。
代码:par_profile.py
import cProfile as profile
import functools
path = u'/a/b/c'
lam = lambda f: f.lower().startswith(u'distantlod')
par = functools.partial(lam, path)
startsWith = path.lower().startswith
par2 = lambda: startsWith(u'distantlod')
if __name__ == '__main__':
profile.run("for _ in range(1000000): par()")
profile.run("for _ in range(1000000): par2()")
输出
$ python par_profile.py
3000003 function calls in 0.536 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.148 0.148 0.536 0.536 <string>:1(<module>)
1000000 0.242 0.000 0.388 0.000 par_profile.py:7(<lambda>)
1 0.000 0.000 0.536 0.536 {built-in method exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1000000 0.054 0.000 0.054 0.000 {method 'lower' of 'str' objects}
1000000 0.092 0.000 0.092 0.000 {method 'startswith' of 'str' objects}
2000003 function calls in 0.347 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.130 0.130 0.347 0.347 <string>:1(<module>)
1000000 0.126 0.000 0.218 0.000 par_profile.py:11(<lambda>)
1 0.000 0.000 0.347 0.347 {built-in method exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1000000 0.092 0.000 0.092 0.000 {method 'startswith' of 'str' objects}
首先,我的机器上的这些测量值看起来与@yuvi 的数字相符:
-
par 大约是 540 纳秒
-
par2 大约是 350 纳秒
所以我同意par2 看起来快了大约 200 ns。
如果您尝试比较 lambda 和 partial,这似乎不是一个公平的测试 - par2 的调用次数较少,因为它不调用 lower,而 par 调用。
为了说明原因,startsWith 可以重写为:
lower_path = path.lower()
startsWith = lower_path.startswith
...所以par2 只是一个包含单个startswith 的lambda,而par 是一个包含lower 和startswith 调用的部分。
因此它们不等价,因此par 速度较慢。
为什么?
问题是“为什么 f.lower().startswith 没有内联 - 是什么禁止语言内联它?”
首先,这个 Python 本身并不禁止这种内联 - 是不同的 Python 实现做出决定,就我上面的测试而言,它是 cpython 3。
其次,partial 的工作不是内联函数,它只是...
“冻结”函数参数和/或关键字的某些部分,从而生成具有简化签名的新对象 (doc)
相反,如果您正在寻找可以在 Python 中进行内联的东西,那么我会查看 Numba's jit 之类的内容或使用 PyPy 进行一些实验(如果您发现一些有趣的改进,请在此处回复)。
如果您找不到任何可以执行您正在寻找的内联的东西,那么也许这是一个新的 Python 模块的好案例!