我开始研究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
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?非常感谢。
答案 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语句,但还有待观察。