Python是否优化了循环中的函数调用?

时间:2011-08-30 12:54:25

标签: python optimization compiler-optimization

说,我有一个代码,它从循环中调用一些函数数百万次,我希望代码快速:

def outer_function(file):
    for line in file:
        inner_function(line)

def inner_function(line):
    # do something
    pass

它不一定是文件处理,它可以是例如从函数绘制线调用的函数绘制点。这个想法是逻辑上这两者必须分开,但从性能的角度来看,它们应该尽快一起行动。

Python是否会自动检测并优化此类内容?如果没有 - 有没有办法给它一个线索呢?可能使用一些额外的外部优化器?...

5 个答案:

答案 0 :(得分:15)

Python没有内联函数调用,因为它具有动态特性。从理论上讲,inner_function可以做一些将名称inner_function重新绑定到其他东西的东西 - Python在编译时无法知道这可能发生。例如:

def func1():
    global inner_func
    inner_func = func2
    print 1

def func2():
    print 2

inner_func = func1

for i in range(5):
    inner_func()

打印:

1
2
2
2
2
你可能认为这太可怕了。然后,再想一想 - Python的功能性和动态性是其最吸引人的特性之一。 Python允许的很多东西都是以性能为代价的,在大多数情况下这是可以接受的。

也就是说,您可以使用byteplay之类的工具或者类似工具来破解某些东西 - 将内部函数反汇编为字节码并将其插入外部函数,然后重新组合。第二个想法,如果你的代码对性能至关重要,足以保证这样的攻击,那么只需用C语言重写它.Python有很好的FFI选项。


这与官方的CPython实现都有关。运行时JITting解释器(如PyPy或可悲的已解散的Unladen Swallow)理论上可以检测正常情况并执行内联。唉,我对PyPy不太熟悉,知道它是否会这样做,但它绝对可以。

答案 1 :(得分:13)

哪个 Python? PyPy的JIT编译器将 - 在几百或几十之后(取决于每次迭代执行多少操作码)迭代左右 - 开始跟踪执行,忘记Python函数调用,并将收集的信息编译成一块优化的机器代码,可能没有任何逻辑的残余,使函数调用本身发生。跟踪是线性的,JIT的后端甚至不知道有一个函数调用,它只是看到两个函数的指令在执行时混合在一起。 (这是完美的情况,例如,当循环中存在分支或者所有迭代都采用相同的分支。某些代码不适合这种JIT编译并且在它们产生大量加速之前快速使跟踪无效,尽管这是相当的罕见的。)

现在,CPython,许多人在谈到“Python”或Python解释器时的意思并不那么聪明。它是一个简单的字节码VM,可以尽职尽责地执行与每次迭代中反复调用函数相关的逻辑。但话又说回来,如果表现 重要,你为什么还要使用翻译呢?考虑在本机代码中编写热循环(例如,作为C扩展或在Cython中),如果将这种开销保持在尽可能低的水平那么重要。

除非你每次迭代只进行一点点的数字运算,否则不会有任何大的改进。

答案 2 :(得分:5)

如果用“Python”表示CPython,那就是常用的实现,没有。

如果用“Python”你碰巧意味着Python语言的任何实现,是的。 PyPy可以优化很多,我相信它的方法JIT应该处理这样的情况。

答案 3 :(得分:3)

CPython(“标准”python实现)不进行这种优化。

但请注意,如果您正在计算函数调用的CPU周期,那么可能是您的问题CPython不是正确的工具。如果您100%确定要使用的算法已经是最好的算法(这是最重要的),并且您的计算实际上是CPU绑定的,那么选项就是:

  • 使用PyPy代替CPython
  • 使用Cython
  • 编写C ++模块并将其与sip
  • 连接
  • 如果可能,请使用numpy simd approach
  • 实施您的算法
  • 如果可能,使用例如PyCuda
  • 在GPU硬件上移动计算

答案 4 :(得分:1)

调用函数来调用pass语句显然会带来相当高的(∞)开销。您的真实程序是否遭受过度开销取决于内部函数的大小。如果它真的只是设置一个像素,那么我建议使用不同的方法,使用以C或C ++等本机语言编码的绘图原语。

有一些(有点实验性的)用于Python的JIT编译器可以优化函数调用,但主流Python不会这样做。