Python代码在函数中真的运行得更快吗?

8 投票
2 回答
1399 浏览
提问于 2025-04-17 18:22

我看到一个评论,引发了我对这个问题的思考:为什么Python代码在函数中运行得更快?

我开始思考这个问题,并决定用timeit库自己试试看,结果却非常不同:

(注意: 为了让测试速度更快,我把10**8改成了10**7)

>>> from timeit import repeat
>>> setup = """
def main():
    for i in xrange(10**7):
        pass
"""
>>> stmt = """
for i in xrange(10**7):
    pass
"""
>>> min(repeat('main()', setup, repeat=7, number=10))
1.4399558753975725
>>> min(repeat(stmt, repeat=7, number=10))
1.4410973942722194
>>> 1.4410973942722194 / 1.4399558753975725
1.000792745732109
  • 我使用timeit的方式对吗?
  • 为什么我的结果相差不到0.1%,而另一个问题的结果差了将近250%?
  • 这是否只在使用CPython编译版本的Python(比如Cython)时才有区别?
  • 最后:Python代码在函数中真的更快吗,还是说这只是和你测试的方式有关?

2 个回答

1

这主要是关于编译器优化算法的事情。当进行即时编译(Just-in-time compilation)时,如果代码是放在函数里的,就更容易找到那些经常使用的代码块。

效率的提升其实取决于你在做什么样的任务。在你给的例子中,你并没有进行特别复杂的计算,所以通过优化来提高效率的机会就比较少。

不过,正如其他人提到的,CPython并不进行即时编译。但是,当代码被编译时,C语言的编译器通常会执行得更快。

你可以看看这个关于GCC编译器的文档:http://gcc.gnu.org/onlinedocs/gcc/Inline.html

11

你测试中的问题在于 timeit 是如何处理你提供的 stmt 代码的。实际上,它是按照下面的模板来编译的:

template = """
def inner(_it, _timer):
    %(setup)s
    _t0 = _timer()
    for _i in _it:
        %(stmt)s
    _t1 = _timer()
    return _t1 - _t0
"""

所以 stmt 实际上是在一个函数里面运行的,使用的是 fastlocals 数组(也就是 STORE_FAST)。

这里有一个测试,比较了你问题中的函数 f_opt 和一个没有优化的编译 stmt,这个未优化的代码是在函数 f_no_opt 中执行的:

>>> code = compile(stmt, '<string>', 'exec')
>>> f_no_opt = types.FunctionType(code, globals())

>>> t_no_opt = min(timeit.repeat(f_no_opt, repeat=10, number=10))
>>> t_opt = min(timeit.repeat(f_opt, repeat=10, number=10))
>>> t_opt / t_no_opt
0.4931101445632647

撰写回答