Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/340.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为了在Python中使用快速循环,最独立于平台和Python版本的方法是什么?_Python_Optimization - Fatal编程技术网

为了在Python中使用快速循环,最独立于平台和Python版本的方法是什么?

为了在Python中使用快速循环,最独立于平台和Python版本的方法是什么?,python,optimization,Python,Optimization,我正在用Python编写一个科学应用程序,其核心是一个处理器密集型循环。我希望尽可能地优化这一点,尽量减少给最终用户带来的不便,他们可能会将其作为Python脚本的未编译集合使用,并将使用Windows、Mac和(主要是Ubuntu)Linux 它目前是用Python编写的,带有少许NumPy代码,我已经包含了下面的代码 是否有一种解决方案可以相当快地完成,而不需要编译?这似乎是保持平台独立性的最简单方法 如果使用像Pyrex这样需要编译的东西,有没有一种简单的方法可以捆绑许多模块,让Pytho

我正在用Python编写一个科学应用程序,其核心是一个处理器密集型循环。我希望尽可能地优化这一点,尽量减少给最终用户带来的不便,他们可能会将其作为Python脚本的未编译集合使用,并将使用Windows、Mac和(主要是Ubuntu)Linux

它目前是用Python编写的,带有少许NumPy代码,我已经包含了下面的代码

  • 是否有一种解决方案可以相当快地完成,而不需要编译?这似乎是保持平台独立性的最简单方法
  • 如果使用像Pyrex这样需要编译的东西,有没有一种简单的方法可以捆绑许多模块,让Python根据检测到的操作系统和Python版本在它们之间进行选择?有没有一种简单的方法可以构建模块集合,而不需要使用每个版本的Python访问每个系统
  • 有一种方法特别适合多处理器优化吗
  • (如果你感兴趣的话,这个循环是通过将附近大量磁离子的贡献相加来计算晶体内给定点的磁场,这些磁离子被视为微小的条形磁铁。基本上,是一个巨大的磁场总和。)


    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