首页 > 解决方案 > 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有标志NESTEDf没有。

但是,可以删除标志,并且它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

标签: pythonpython-3.xperformanceoptimization

解决方案


我可能错了,但我认为差异来自这样一个事实,它g可能会引用本地变量factory,因此需要访问两个范围以进行任何变量查找:globals以及factory. 保护这个额外的范围(或从factoryand合并范围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%。


推荐阅读