在Cython脚本中使用memset而不是np.zeros来提高速度

时间:2017-07-01 22:42:43

标签: python-3.x cython memset typed-memory-views

我开始研究Fortran库的SciPy接口(BLAS / LAPACK),正如我们在这里看到的那样:Calling BLAS / LAPACK directly using the SciPy interface and Cython并提出了一个解决方案,但不得不求助于使用false直接调用Fortran代码可以获得任何速度提升。问题是Fortran代码需要一个0值输出矩阵(它在内存中的矩阵上运行)以匹配Numpy版本(numpy.zeros)。

所以我有点困惑,因为Python中的1000x1000零矩阵只需要8us(使用%timeit,或者0.008ms),那么为什么添加Cython代码会杀死运行时,注意我也是在内存视图上创建它? (基本上它在1000 x 1000矩阵乘法的3ms到8ms左右)。然后在搜索SO之后我在其他地方找到了使用np.outer作为最快的数组更改机制的解决方案。所以我将引用的帖子中的整个代码重新编写为较新的memset格式,并得到类似的内容(Cython memoryview文件):

cyblas.pyx

TEST SCRIPT

import cython
import numpy as np
cimport numpy as np
from libc.string cimport memset #faster than np.zeros

REAL = np.float64
ctypedef np.float64_t REAL_t
ctypedef np.uint64_t  INT_t

cdef int ONE = 1
cdef REAL_t ONEF = <REAL_t>1.0

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cpdef outer_prod(double[::1] _x, double[::1] _y, double[:, ::1] _output):

    cdef int M = _y.shape[0]
    cdef int N = _x.shape[0]
    memset(&_output[0,0], 0, M*N)

    with nogil:
        dger(&M, &N, &ONEF, &_y[0], &ONE, &_x[0], &ONE, &_output[0,0], &M)

所以这解决了我重置输出矩阵值的问题,但仅限于行125,超出此问题仍然存在(解决见下文)。我认为将import numpy as np; from cyblas import outer_prod; a=np.random.randint(0,100, 1000); b=np.random.randint(0,100, 1000); a=a.astype(np.float64) b=b.astype(np.float64) cy_outer=np.zeros((a.shape[0],b.shape[0])); np_outer=np.zeros((a.shape[0],b.shape[0])); %timeit outer_prod(a,b, cy_outer) %timeit np.outer(a,b, np_outer) 长度参数设置为M * N会在内存中清除1000 * 1000,但显然不是。

有没有人知道如何使用memset将整个输出矩阵重置为0?非常感谢。

1 个答案:

答案 0 :(得分:1)

[更新 - 修复:它需要#bytes而不仅仅是M*N的数组大小,即M*N*variable_bytes作为长度输入。这里的逻辑由先前的结果看出:第125行是停止* 8字节= 1000行的地方,因此回答了问题。仍然指标不是很好:100个循环,最好的3:每循环5.41毫秒(cython)100循环,最佳3:3.95毫秒每循环(numpy)但仍然解决。 对上述代码的更改是添加:cdef variable_bytes = np.dtype(REAL).itemsize #added to get bytes for memset, after REAL is defined, in this case 8 bytes 然后当你调用memset时:memset(&_output[0,0], 0, M*N*variable_bytes) # gives the same output as np.zeros function现在唯一可以加快速度的地方就是在大型矩阵上使用prange OpenMP语句,但还有待观察。