python - Python中嵌套函数的性能开销
问题描述
在 Python 3.9 中,嵌套函数比普通函数慢得惊人,在我的示例中约为 10%。
from timeit import timeit
def f():
return 0
def factory():
def g():
return 0
return g
g = factory()
print(timeit("f()", globals=globals()))
#> 0.074835498
print(timeit("g()", globals=globals()))
#> 0.08470309999999998
dis.dis
显示相同的字节码,我发现的唯一区别是函数内部标志。的确,dis.show_code
显示g
有标志NESTED
而f
没有。
但是,可以删除标志,并且它g
的速度与f
.
import inspect
g.__code__ = g.__code__.replace(co_flags=g.__code__.co_flags ^ inspect.CO_NESTED)
print(timeit("f()", globals=globals()))
#> 0.07321161100000001
print(timeit("g()", globals=globals()))
#> 0.07439838800000001
我试图查看 CPython 代码以了解CO_NESTED
标志如何影响函数执行,但我一无所获。相对于CO_NESTED
标志的这种性能差异有什么解释吗?
编辑:删除CO_NESTED
标志似乎也对函数执行没有影响,除了开销,即使它已经捕获了变量。
import inspect
global_var = 40
def factory():
captured_var = 2
def g():
return global_var + captured_var
return g
g = factory()
assert g() == 42
g.__code__ = g.__code__.replace(co_flags=g.__code__.co_flags ^ inspect.CO_NESTED)
assert g() == 42 # function still works as expected
解决方案
我可能错了,但我认为差异来自这样一个事实,它g
可能会引用本地变量factory
,因此需要访问两个范围以进行任何变量查找:globals
以及factory
. 保护这个额外的范围(或从factory
and合并范围globals
)很可能是您观察到的开销的原因。一个很好的提示是,如果你嵌套了另一层函数:
def factory():
def ff():
def g():
return 0
return g
return ff()
g = factory() # please note that it is equivalent from the perspective of time measurement
时间:
print(timeit("f()", globals=globals(), number=100000000))
# > 6.792911
print(timeit("g()", globals=globals(), number=100000000))
# > 7.8184555
在你的第一个计时案例中,我得到 +5.7%(你的数字是 +13.5%),在我的第二个例子中:+15.1%。
推荐阅读
- php - add_action 没有在另一个函数中被调用
- android - 如何从无法访问 lifeCycleOwner 的存储库中获取 LiveData 的价值以进行观察?
- javascript - 等待承诺链有什么问题?
- java - 人们是否不正确地使用了 Streams/Collectors 的 collectAndThen 方法?
- machine-learning - 计算训练中迭代的时期数?
- java - 获取包 org.assertj.core.api 不存在,即使我在使用 JDK 1.8 时在我的 pom 中指定版本 3.6.2
- airflow - 导入缺失的模块
- java - 请求处理后 Spring Boot 应用程序的清理方法
- python - 如何将ffmpeg的-progress选项输出重定向到stderr?
- javascript - Flask WTForm 作为对 AJAX 调用的响应