8

我看到一条评论让我想到了为什么 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 代码在函数中真的更快,还是仅仅取决于你如何计时?
4

2 回答 2

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的未优化编译的测试:stmtf_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
于 2013-03-08T07:00:57.720 回答
1

它归结为编译器优化算法。在执行即时编译时,如果在函数中找到经常使用的代码块,则更容易识别它们。

效率增益实际上取决于正在执行的任务的性质。在您给出的示例中,您实际上并没有做任何计算密集型的事情,通过优化实现效率提升的机会更少。

然而,正如其他人指出的那样,CPython 不进行即时编译。然而,在编译代码时,C 编译器通常会更快地执行它们。

查看有关 GCC 编译器的文档:http: //gcc.gnu.org/onlinedocs/gcc/Inline.html

于 2013-03-08T05:51:39.373 回答