【问题标题】:Does python's partial inline calls?python的部分内联调用吗?
【发布时间】:2015-10-17 11:38:06
【问题描述】:

par

path = u'/a/b/c'
lam = lambda f: f.lower().startswith(u'distantlod')
par = functools.partial(lam, path)

还有这个par

path = u'/a/b/c'
startsWith = path.lower().startswith
par = lambda: startsWith(u'distantlod')

等价?如果不是为什么?

【问题讨论】:

  • @yuvi:它是相关的,但不回答这个特定问题 - 也相关的是:stackoverflow.com/q/17388438/281545
  • 您能否澄清一下您如何定义“等效”?
  • @jamesc:就性能而言(内联是什么)。 par 在两种情况下都是一个函数,在调用 par() 时执行相同的操作 - 但在第二种情况下,path.lower().startswith 在调用中内联(为我们节省了两个点)。在第一种情况下,python 似乎可以做到这一点 - 它是否做到了,如果没有,为什么(或者我错过了什么)?
  • 如果你想比较性能,你可以随时timeit

标签: python python-2.7 inline functools


【解决方案1】:

这是我对 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。

如果您尝试比较 lambdapartial,这似乎不是一个公平的测试 - par2 的调用次数较少,因为它不调用 lower,而 par 调用。

为了说明原因,startsWith 可以重写为:

lower_path = path.lower()
startsWith = lower_path.startswith

...所以par2 只是一个包含单个startswithlambda,而par 是一个包含lowerstartswith 调用的部分。

因此它们不等价,因此par 速度较慢。

为什么?

问题是“为什么 f.lower().startswith 没有内联 - 是什么禁止语言内联它?”

首先,这个 Python 本身并不禁止这种内联 - 是不同的 Python 实现做出决定,就我上面的测试而言,它是 cpython 3。

其次,partial 的工作不是内联函数,它只是...

“冻结”函数参数和/或关键字的某些部分,从而生成具有简化签名的新对象 (doc)

相反,如果您正在寻找可以在 Python 中进行内联的东西,那么我会查看 Numba's jit 之类的内容或使用 PyPy 进行一些实验(如果您发现一些有趣的改进,请在此处回复)。

如果您找不到任何可以执行您正在寻找的内联的东西,那么也许这是一个新的 Python 模块的好案例!

【讨论】:

  • 谢谢 - 我要问的是 为什么 f.lower().startswith 没有内联 - 是什么禁止语言内联它?
  • 已更新以包含我对原因的回答。希望对您有所帮助。
猜你喜欢
  • 1970-01-01
  • 2011-06-04
  • 1970-01-01
  • 1970-01-01
  • 2019-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多