Python:使用仅依赖循环索引的函数调用来提高for循环的性能

时间:2018-08-05 19:36:49

标签: python python-3.x performance vectorization

我对用python编写代码感到内as,就好像它是Fortran一样。我正在重写自己已经在Fortran中编写的长代码的许多部分,因为我想显着扩展代码,并且在python中扩展以进行概念验证非常容易。但是,如果可以加快速度,我将只使用python,而实际上我并不希望一遍又一遍地对程序进行曲柄设置。一旦一个想法被证明可行,我将继续讨论下一个问题。这就是为什么我希望在python中工作。不幸的是,目前,要用python编写代码要花几周的时间。即使在随后的for循环上加快一个数量级,也将使其成为可行的测试平台。

针对R语言也提出了类似的问题

Improving loop performance with function call inside

但是令人惊讶的是我没有看到适用于python的代码。这个很相似,但是for循环中的函数具有依赖关系,而我的则没有。

Improve performance of a for loop in Python (possibly with numpy or numba)

一个巨大的瓶颈是一个for循环

简单代码

import numpy as np

part  = 3 # a random index of an array, fixed here for example purposes
nmol = 1000
energy = np.zeros((nmol),dtype=np.float_)

for i in range(nmol):
    energy[i] = np.where( part != i,function(part,i),0.0) # if i = part, energy = 0.0

加快功能本身是另一个独立的问题。必须有一种使用numpy或其他方法来同时运行所有调用的方法

例如,让我们说

def function(i,j):
    for k in range(100000): # this loop is simply to make the time about a second or 2
        ener = (i + j) * (i * j) # entirely arbitrary and not my real problem
    return ener

实际上,我的函数调用了一些取决于part和“ i”的函数。

完整的工作示例是:

import numpy as np
import time as time

def function(i,j):
    for k in range(10000): # this loop is simply to make the time about a second or 2
        ener = (i + j) * (i * j) # entirely arbitrary and not my real problem
    return ener

part  = 3 # a random index of an array, fixed here for example purposes
nmol = 1000
energy = np.zeros((nmol),dtype=np.float_)

start = time.time()
for i in range(nmol):
    energy[i] = np.where( part != i,function(part,i),0.0) # if i = part, energy = 0.0

end = time.time()
print('time: ', end-start)

我正在使用Python 3.6版。至关重要的是,在循环索引中,“ i”不能与索引“ part”交互。

2 个答案:

答案 0 :(得分:1)

我很惊讶能找到一个答案-我不是故意回答自己的问题...我只是一段时间以来一直在思考这个问题。

创建从0到nmol的整数数组,而不是从索引0到nmol的for循环。只需通过传递整数数组来调用函数。因此,数组输入接收数组输出。我修改了函数,使其不需要常量“ part”

此矢量化解决方案比for循环快〜27倍,这为我提供了所需的数量级。

随着nmol大小的数组长度变大,速度增加,反之亦然。

code-runner.executorMapByFileExtension

答案 1 :(得分:1)

根据您的矢量化解决方案,您可以使用pythran

来提高速度

原始代码:

import numpy as np

def function(i, part):
    for k in range(10000):
        ener = (i + part) + (i * part)
    return ener

和相关基准:

python -m timeit -s 'import numpy as np; part  = 3; nmol = 1000; part_list = np.arange(0,nmol,1); part_list = np.delete(part_list, part); from a import function' 'function(part_list, part)'
10 loops, best of 3: 37.3 msec per loop

然后添加一个pythran export评论

import numpy as np

#pythran export function(int64[], int64)
def function(i, part):
    for k in range(10000):
        ener = (i + part) + (i * part)
    return ener

并使用以下命令编译模块:

pythran a.py

带来额外的提升:

python -m timeit -s 'import numpy as np; part  = 3; nmol = 1000; part_list = np.arange(0,nmol,1); part_list = np.delete(part_list, part); from a import function' 'function(part_list, part)'
1000000 loops, best of 3: 1.53 usec per loop