为什么我的Numpy测试代码比Matlab慢2倍

2024-05-16 21:57:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我在Python中开发了一个基于Fresnel系数的反射率求解器,但在Python+numy中的性能比Matlab慢2倍,所以遇到了一些障碍。我将问题代码提炼成一个简单的示例,以显示在每种情况下执行的操作:

测试用例的Python代码:

import numpy as np
import time

def compare_fn(i):
    a = np.random.rand(400)
    vec = np.random.rand(400)
    t = time.time()
    for j in xrange(i):
        a = (2.3 + a * np.exp(2j*vec))/(1 + (2.3 * a * np.exp(2j*vec)))
    print (time.time()-t)
    return a

a = compare_fn(200000)

Output: 10.7989997864

等效Matlab代码:

^{pr2}$

a = compare_fn(200000); Elapsed time is 5.644673 seconds.

我被这个难住了。我已经安装了MKL(Python学术许可证)。如果有任何帮助,我将非常感谢您通过我的示例来确定问题,以及如何使用Numpy实现同等的性能(如果不是更好的话)。在

一般来说,我不能并行化循环,因为求解多层膜的菲涅耳系数涉及递归计算,可以用上面的循环形式表示。在


Tags: 代码import示例timenprandomcomparefn
3条回答

下面的内容类似于unutbu的已删除答案,对于您的示例输入,在我的系统上运行速度快3倍。如果像这样在Matlab中实现它,它可能也会运行得更快,但这是另一回事。为了能够使用ipython的%timeit功能,我将您的原始函数重写为:

def fn(a, vec, i):
    for j in xrange(i):
        a = (2.3 + a * np.exp(2j*vec))/(1 + (2.3 * a * np.exp(2j*vec)))
    return a

我通过从循环中删除指数计算对其进行了优化:

^{pr2}$

采用两种方法进行试驾:

In [2]: a = np.random.rand(400)

In [3]: vec = np.random.rand(400)

In [9]: np.allclose(fn(a, vec, 100), fn_bis(a, vec, 100))
Out[9]: True

In [10]: %timeit fn(a, vec, 100)
100 loops, best of 3: 8.43 ms per loop

In [11]: %timeit fn_bis(a, vec, 100)
100 loops, best of 3: 2.57 ms per loop

In [12]: %timeit fn(a, vec, 200000)
1 loops, best of 3: 16.9 s per loop

In [13]: %timeit fn_bis(a, vec, 200000)
1 loops, best of 3: 5.25 s per loop

我不知道numpypy对你现在所做的是否足够,但你可以试试。在

http://buildbot.pypy.org/numpy-status/latest.html

我已经做了大量的实验,试图确定Matlab和Python/Numpy之间速度差异的来源。一些主要发现是:

  1. Matlab现在有了一个JIT编译器,它在涉及循环的情况下提供了显著的好处。关闭它会将性能降低2倍,使其速度与本机Python+numy代码相似。在

    功能加速关闭

    a=比较(200000)

    运行时间为9.098062秒。

  2. 然后我开始使用Numba和Cython探索优化示例函数的选项,看看我能做得更好。对我来说,一个重要的发现是,显式循环计算上的Numba JIT优化比Numpy数组上的本机向量化数学操作更快。我不太明白为什么会这样,但我已经在下面列出了我的示例代码和测试时间。我也和赛顿一起玩过(我不是专家),虽然速度也更快,但Numba还是比Cython快2倍,所以我最终还是和Numba一起参加了测试。

这是3个等价函数的代码。第一个是Numba优化函数,它有一个显式循环来执行元素计算。第二个函数是Python+Numpy函数,它依赖于Numpy向量化来执行计算。第三个函数尝试使用Numba来优化矢量化的Numpy代码(但是没有改进,正如您在结果中看到的那样)。最后,我包含了Cython代码,尽管我只测试了一个案例。在

import numpy as np
import numba as nb

@nb.jit(nb.complex128[:](nb.int16, nb.int16))
def compare_fn_jit(i, j):
    a = np.asarray(np.random.rand(j), dtype=np.complex128)
    vec = np.random.rand(j)
    exp_term = np.exp(2j*vec)

    for k in xrange(i):
        for l in xrange(j):
            a[l] = (2.3 + a[l] * exp_term[l])/(1 + (2.3 * a[l] * exp_term[l]))
    return a

def compare_fn(i, j):
    a = np.asarray(np.random.rand(j), dtype=np.complex128)
    vec = np.random.rand(j)
    exp_term = np.exp(2j*vec)
    for k in xrange(i):
        a = (2.3 + a * exp_term)/(1 + (2.3 * a * exp_term))
    return a

compare_fn_jit2 = nb.jit(nb.complex128[:](nb.int16, nb.int16))(compare_fn)


import numpy as np
cimport numpy as np
cimport cython
@cython.boundscheck(False)
def compare_fn_cython(int i, int j):
    cdef int k, l
    cdef np.ndarray[np.complex128_t, ndim=1] a, vec, exp_term
    a = np.asarray(np.random.rand(j), dtype=np.complex128)
    vec = np.asarray(np.random.rand(j), dtype=np.complex128)
    exp_term = np.exp(2j*vec)

    for k in xrange(i):
        for l in xrange(j):
            a[l] = (2.3 + a[l] * exp_term[l])/(1 + (2.3 * a[l] * exp_term[l]))
    return a

计时结果:

i.单个外循环的计时-演示矢量化计算的效率

%timeit -n 1 -r 10 compare_fn_jit(1,1000000) 1 loops, best of 10: 352 ms per loop

%timeit -n 1 -r 10 compare_fn(1,1000000) 1 loops, best of 10: 498 ms per loop

%timeit -n 1 -r 10 compare_fn_jit2(1,1000000) 1 loops, best of 10: 497 ms per loop

%timeit -n 1 -r 10 compare_fn_cython(1,1000000) 1 loops, best of 10: 424 ms per loop

二。在短数组上计算大循环的极端情况下的计时(预计Numpy+Python的性能较差)

%timeit -n 1 -r 5 compare_fn_jit(1000000,40) 1 loops, best of 5: 1.44 s per loop

%timeit -n 1 -r 5 compare_fn(1000000,40) 1 loops, best of 5: 28.2 s per loop

%timeit -n 1 -r 5 compare_fn_jit2(1000000,40) 1 loops, best of 5: 29 s per loop

iii.测试上述两种情况之间的中间位置

%timeit -n 1 -r 5 compare_fn_jit(100000,400) 1 loops, best of 5: 1.4 s per loop

%timeit -n 1 -r 5 compare_fn(100000,400) 1 loops, best of 5: 5.26 s per loop

%timeit -n 1 -r 5 compare_fn_jit2(100000,400) 1 loops, best of 5: 5.34 s per loop

正如您所看到的,使用Numba可以提高效率1.5X-30X的系数。与Cython相比,它的高效性和易用性给我留下了深刻的印象。在

相关问题 更多 >