优化使用numpy sin,cos,sum和abs的python代码

时间:2017-05-02 22:34:58

标签: python arrays performance numpy vectorization

我有一些python代码,目前运行速度太慢,无法使用。完成了一些速度测试后,大部分时间似乎花在执行数学运算来计算corr(参见下面的代码)。

import numpy as np
from multiprocessing import Pool
from contextlib import closing

def calc_cc(tlag):
    global phi_t, psi_t, T
    tstart = tlag-T/2
    x = np.arange(tstart, tstart+T, dtype=int)

    #bulk of time spent running line below calculating corr
    corr = np.sum(np.abs(np.cos(phi_t[tlag+x]-psi_t[x]))-np.abs(np.sin(phi_t[tlag+x]-psi_t[x])))


def main():
    global phi_t, psi_t, T

    phi_t = #some large numpy array
    psi_t = #another numpy array
    tlag = #another numpy array

    with closing(Pool(processes=5)) as p:
        corr = p.map(calc_cc,tlag)
        p.terminate()

if __name__ == "__main__":
    main()

我花了一些时间在网上寻找优化我的python代码的方法,更具体地说是数学运算,但是很多建议都是在需要时使用numpy而不是python基函数这样的包(例如使用与sum()相比,np.sum大大提高了速度。现在我已经到了使用numpy数组的点,使用numpy方法,我不知道在哪里可以找到额外的收益。我正在有效地尝试的公式解决的是:

enter image description here

我确信必须有更好的方法来做到这一点,因此,非常感谢任何有关更好解决方案的指导:)。

1 个答案:

答案 0 :(得分:2)

首先,您可以通过为三角函数as @hpaulj noted计算phi_t(...)-psi_t(...)一次来略微改进代码。但是,我看到的最大问题是您使用多处理池进行相对较轻的计算。我的猜测是,运行时的很大一部分来自使用池的开销。

如果您可以适应内存,则可以通过使用阵列广播来计算整个相关函数。通过矢量化计算,您可以最大限度地提高通过numpy获得的速度。我们的想法是使用索引phi / psi数组的二维数组:列对应于原始x索引,行对应于单个tlag转换。您自然使用的内置numpy功能可以使用广播。

这就是我的意思,完成一些虚拟数据:

import numpy as np

def calc_cc_new(tlags):
    global phi_t, psi_t, T

    tstarts = tlags - T//2
    xs = tstarts + np.arange(T)[:,None]
    dphipsi = phi_t[tlags+xs] - psi_t[xs]

    corr = np.sum(np.abs(np.cos(dphipsi)) - np.abs(np.sin(dphipsi)),axis=0)
    return corr

def main_new():
    global phi_t, psi_t, tlags

    return calc_cc_new(tlags)

N = 10000
T = 100
phi_t = np.random.rand(N)*2*np.pi
psi_t = np.random.rand(N)*2*np.pi
tlags = np.arange(T//2,3*T)

if __name__ == "__main__":
    print(main_new())

我没有检查你的多处理版本,但原版的串行版本需要8.1毫秒,而矢量化版本需要2.1毫秒(无论我是否将总和的两个术语分成单独调用{{1 }})。我检查了我的版本中返回的数组使用np.sum()检查原始数据(由于向量化,算术运算的顺序被移动,所以我们不能期望完全协议,机器精度只有一个)。使用虚拟示例,我是否将求和维度定义为第0个或第一个并不重要,但根据您的输入数组形状,这可能会导致一些性能差异。

此外,通常不鼓励使用全局变量,并且全局命名空间查找比本地命名空间慢一点(尽管我不希望这对于你的函数是CPU很重要的事情)。无论如何,如果你放弃多处理池,你可以简单地扩展你的np.allclose函数的定义,使它将所有输入作为参数,而不是全局变量。

最后,一旦你完成,就不要忘记calc_cc先决条件。