为了在Python中使用快速循环,最独立于平台和Python版本的方法是什么?
我正在用Python编写一个科学应用程序,其核心是一个处理器密集型循环。我希望尽可能地优化这一点,尽量减少给最终用户带来的不便,他们可能会将其作为Python脚本的未编译集合使用,并将使用Windows、Mac和(主要是Ubuntu)Linux 它目前是用Python编写的,带有少许NumPy代码,我已经包含了下面的代码为了在Python中使用快速循环,最独立于平台和Python版本的方法是什么?,python,optimization,Python,Optimization,我正在用Python编写一个科学应用程序,其核心是一个处理器密集型循环。我希望尽可能地优化这一点,尽量减少给最终用户带来的不便,他们可能会将其作为Python脚本的未编译集合使用,并将使用Windows、Mac和(主要是Ubuntu)Linux 它目前是用Python编写的,带有少许NumPy代码,我已经包含了下面的代码 是否有一种解决方案可以相当快地完成,而不需要编译?这似乎是保持平台独立性的最简单方法 如果使用像Pyrex这样需要编译的东西,有没有一种简单的方法可以捆绑许多模块,让Pytho
Python不适用于高性能计算。用C编写核心循环并从Python调用它。Numpy确实使用了一些本地优化来处理数组。您可以使用Numpy array with来获得一些加速。您的python代码可以通过使用生成器表达式替换循环,并通过使用itertools.izip并行迭代所有三个序列来删除mom_i[i]、relative[i]和r_unit[i]的所有查找,从而稍微加速 i、 e.更换
B = zeros(3,float)
for i in range(len(relative)):
#work out the dipole field and add it to the estimate so far
B += A*(3*dot(mom_i[i],r_unit[i])*r_unit[i] - mom_i[i]) / sqrt(dot(relative[i],relative[i]))**3
return B
与:
这也是更可读的IMHO,因为核心方程不是到处都有[i]的
然而,我怀疑,与使用Cython之类的编译语言来完成整个函数相比,这只会给您带来边际收益。一个简单但显著的加速是将乘法取在总和之外。您只需在返回时将B与它相乘:
for i in range(len(relative)):
#work out the dipole field and add it to the estimate so far
B += (3*dot(mom_i[i],r_unit[i])*r_unit[i] - mom_i[i]) / sqrt(dot(relative[i],relative[i]))**3
return A*B
这使得使用20000个随机偶极子的速度提高了约8%
除此之外,我建议使用Cython(通常推荐使用Pyrex)或使用Scipy编织。请参阅,以获取一些示例,并比较各种加速Numpy/Scipy的方法
如果你想尝试做类似的事情,我建议你先看看Scipy
很高兴看到另一位物理学家也如此。这里的人不多
编辑:
我决定把这当作一个挑战来发展一些赛昂技能,并且比Psyco优化版提高了10倍。如果你想看我的代码,请告诉我
Edit2:
好吧,回到过去,在我的Cython版本中找到了让事情变慢的原因。现在的速度已经超过了100倍。如果你想要或者需要比Ray的加速版多出2倍左右的系数,请告诉我,我会发布我的代码
Cython源代码:
下面是我鼓起的Cython代码:
import numpy as np
cimport numpy as np
cimport cython
cdef extern from "math.h":
double sqrt(double theta)
ctypedef np.float64_t dtype_t
@cython.boundscheck(False)
@cython.wraparound(False)
def calculate_dipole_cython(np.ndarray[dtype_t,ndim=2,mode="c"] mu,
np.ndarray[dtype_t,ndim=2,mode="c"] r_i,
np.ndarray[dtype_t,ndim=2,mode="c"] mom_i):
cdef Py_ssize_t i
cdef np.ndarray[dtype_t,ndim=1,mode="c"] tmp = np.empty(3,np.float64)
cdef np.ndarray[dtype_t,ndim=1,mode="c"] relative = np.empty(3,np.float64)
cdef double A = 1e-7
cdef double C, D, F
cdef np.ndarray[dtype_t,ndim=1,mode="c"] B = np.zeros(3,np.float64)
for i in xrange(r_i.shape[0]):
relative[0] = mu[0,0] - r_i[i,0]
relative[1] = mu[0,1] - r_i[i,1]
relative[2] = mu[0,2] - r_i[i,2]
C = relative[0]*relative[0] + relative[1]*relative[1] + relative[2]*relative[2]
C = 1.0/sqrt(C)
D = C**3
tmp[0] = relative[0]*C
F = mom_i[i,0]*tmp[0]
tmp[1] = relative[1]*C
F += mom_i[i,1]*tmp[1]
tmp[2] = relative[2]*C
F += mom_i[i,2]*tmp[2]
F *= 3
B[0] += (F*tmp[0] - mom_i[i,0])*D
B[1] += (F*tmp[1] - mom_i[i,1])*D
B[2] += (F*tmp[2] - mom_i[i,2])*D
return A*B
我想我已经对它进行了一些优化,但是你可能可以从中得到更多。您仍然可以将np.zero和np.empty替换为来自Numpy C API的直接调用,但这不会有多大区别。目前,这段代码比您已有的Numpy优化代码提高了2-3倍。但是,您需要正确地传递数字。数组需要采用C格式(这是Numpy数组的默认格式,但在Numpy中,C格式数组的转置是Fortran格式的数组)
例如,要从运行代码,您需要将np.random.random((3,N))
s替换为np.random.random((N,3))
。而且`
r_test_fast = reshape_vector(r_test)
需要改成
r_test_fast = np.array(np.matrix(r_test))
最后一行可以变得更简单/更快,但在我看来,这是过早的优化
如果您以前没有使用过Cython,并且不知道如何编译此文件,请告诉我,我很乐意提供帮助
最后,我建议您看看。我用它作为优化的指南。下一步是尝试使用利用SSE2指令集的BLAS函数,尝试使用SSE API,或者尝试使用更多与SSE2接口的Numpy C API。此外,您还可以研究并行化。如果您消除循环并使用Numpy的矢量化操作,您可以让它运行得更快、更快。将数据放入形状为(3,N)的numpy数组中,然后尝试以下操作:
import numpy as np
N = 20000
mu = np.random.random((3,1))
r_i = np.random.random((3,N))
mom_i = np.random.random((3,N))
def unit_vectors(r):
return r / np.sqrt((r*r).sum(0))
def calculate_dipole(mu, r_i, mom_i):
relative = mu - r_i
r_unit = unit_vectors(relative)
A = 1e-7
num = A*(3*np.sum(mom_i*r_unit, 0)*r_unit - mom_i)
den = np.sqrt(np.sum(relative*relative, 0))**3
B = np.sum(num/den, 1)
return B
对我来说,这比使用for循环快50倍。使用20000个不同的偶极子,我得到的加速比不到1%。我确实说过我期望边际增益。是的,我只是决定看看它会有多大的增益。谢谢!这太神奇了,对我来说速度提高了100倍我已经问过了,感谢您的任何意见。:)肯定有兴趣看看你的赛昂人。:)
r_test_fast = np.array(np.matrix(r_test))
import numpy as np
N = 20000
mu = np.random.random((3,1))
r_i = np.random.random((3,N))
mom_i = np.random.random((3,N))
def unit_vectors(r):
return r / np.sqrt((r*r).sum(0))
def calculate_dipole(mu, r_i, mom_i):
relative = mu - r_i
r_unit = unit_vectors(relative)
A = 1e-7
num = A*(3*np.sum(mom_i*r_unit, 0)*r_unit - mom_i)
den = np.sqrt(np.sum(relative*relative, 0))**3
B = np.sum(num/den, 1)
return B