Python:通过内部函数调用(仅依赖于循环索引)提高for循环的性能
我对用python编写代码感到内疚,就好像它是Fortran一样。我正在重写我自己用Fortran编写的一段长代码的许多部分,因为我想显著地扩展代码,而且用python进行概念验证工作要容易得多。但是,如果速度足够快的话,我将简单地使用python,我实际上对一遍又一遍地启动程序并不感兴趣。一旦一个想法被证明有效,我就进入下一个问题。这就是我希望使用python的原因。不幸的是,现在,用python编写的程序需要几个星期才能运行。即使在下面的for循环上进行一个数量级的加速,也会使它成为一个可行的测试平台 对于R语言也提出了类似的问题 但令人惊讶的是,我没有看到python的。这个类似,但是for循环中的函数有依赖项,而我的函数没有 一个巨大的瓶颈是单for循环 简单代码 加速功能本身是另一个独立的问题。必须有一种方法可以使用numpy或其他方法同时运行所有调用 比如说Python:通过内部函数调用(仅依赖于循环索引)提高for循环的性能,python,python-3.x,performance,vectorization,Python,Python 3.x,Performance,Vectorization,我对用python编写代码感到内疚,就好像它是Fortran一样。我正在重写我自己用Fortran编写的一段长代码的许多部分,因为我想显著地扩展代码,而且用python进行概念验证工作要容易得多。但是,如果速度足够快的话,我将简单地使用python,我实际上对一遍又一遍地启动程序并不感兴趣。一旦一个想法被证明有效,我就进入下一个问题。这就是我希望使用python的原因。不幸的是,现在,用python编写的程序需要几个星期才能运行。即使在下面的for循环上进行一个数量级的加速,也会使它成为一个可行
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”的函数
完整的工作示例是:
我使用的是Python版本3.6。循环中的索引“i”不能与索引“part”交互,这一点非常重要。我很惊讶我是一个能够找到答案的人——我不是故意回答我自己的问题。。。我只是一个人想了一会儿 创建一个从0到nmol的整数数组,而不是从索引0到nmol的for循环。只需通过传递整数数组来调用函数。因此,数组输入接收数组输出。我修改了函数,使其不需要常量“part” 此矢量化解决方案比for循环快约27倍,为我提供了所需的数量级 随着size nmol的阵列长度变大,速度增益增加,反之亦然
import numpy as np
import time as time
def function(i):
for k in range(10000):
ener = (i + part) + (i * part) # entirely arbitrary and not my real problem
return ener
part = 3 # a random index of an array, fixed here for example purposes
nmol = 1000
start = time.time()
part_list = np.arange(0,nmol,1)
part_list = np.delete(part_list,part) # remove the self index
energy =function(part_list) # calls the function in a vectorized form.
end = time.time()
time2 = end-start
基于矢量化解决方案,您可以使用 原始代码:
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导出
注释
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
你对多重处理持开放态度吗?如果是,您可以参考。很难看出如何提高循环的迭代速度。我不确定您是否可以使用列表理解,因为energy是一个numpy对象energy只是在每个元素中保存浮点标量。。。它可以是一个列表或数组,也可以是速度所需的任何内容:)。我愿意接受任何能让我的代码更快的东西!
函数(i,j)
中的for
循环是冗余的。你知道的,对吧?用麻木。或者,如果限制太多,使用cython。此外,julia生态系统变得越来越强大,加上(编译的)julia代码可以直接调用python代码。您不想调用的函数是否包含任何非numpy代码?否则,使用@J_H已经提到的Numba,这个问题将很容易解决。
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