Python 3.x 在Cython脚本中使用memset而不是np.zero以提高速度

Python 3.x 在Cython脚本中使用memset而不是np.zero以提高速度,python-3.x,cython,memset,typed-memory-views,Python 3.x,Cython,Memset,Typed Memory Views,我开始研究Fortran库(BLAS/LAPACK)的SciPy接口,正如大家在这里看到的:我提出了一个解决方案,但不得不使用numpy.zeros,这实际上扼杀了直接调用Fortran代码所带来的任何速度提升。问题是Fortran代码需要一个0值的输出矩阵(它在内存中的矩阵上操作)来匹配Numpy版本(np.outer) 所以我有点困惑,因为Python中的1000x1000零矩阵只需要8us(使用%timeit,或0.008ms),那么为什么添加Cython代码会杀死运行时,注意到我也在me

我开始研究Fortran库(BLAS/LAPACK)的SciPy接口,正如大家在这里看到的:我提出了一个解决方案,但不得不使用
numpy.zeros
,这实际上扼杀了直接调用Fortran代码所带来的任何速度提升。问题是Fortran代码需要一个0值的输出矩阵(它在内存中的矩阵上操作)来匹配Numpy版本(
np.outer

所以我有点困惑,因为Python中的1000x1000零矩阵只需要8us(使用%timeit,或0.008ms),那么为什么添加Cython代码会杀死运行时,注意到我也在memoryview上创建它?(基本上是从3ms到8ms,大约1000 x 1000的矩阵乘法)。然后在上下搜索之后,我在别处找到了一个解决方案,使用
memset
作为最快的数组更改机制。因此,我将引用文章中的全部代码重写为较新的
memoryview
格式,并得到类似的内容(Cython
cyblas.pyx
文件):

因此,这修复了我重置输出矩阵值的问题,但仅重置到第125行,除此之外,此问题仍然存在(已解决,请参阅下文)。我认为将
memset
length参数设置为M*N将清除内存中的1000*1000,但显然不是


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

[更新-修复程序是:它需要#字节,而不仅仅是
M*N
的数组大小,即
M*N*变量_字节
作为长度输入。之前的结果显示了这一逻辑:第125行是它停止的位置*8字节=1000行,因此回答了这个问题。但指标仍然不太好:100个循环,每个循环最好3:5.41毫秒(cython)100个循环,最好为3:3.95毫秒/循环(numpy),但仍然解决了问题。 对上述代码的更改是添加:
cdef variable_bytes=np.dtype(REAL).itemsize#在定义REAL后添加以获取memset的字节,在本例中为8字节

然后,当您调用memset时:
memset(&_output[0,0],0,M*N*variable_bytes)#提供与np.zeros函数相同的输出
现在我能看到的唯一加速的地方是在大矩阵上使用
prange
OpenMP语句,但还有待观察。

[更新-修复程序是:它需要#字节,而不仅仅是
M*N
的数组大小,即
M*N*变量_字节
作为长度输入。之前的结果显示了这一逻辑:第125行是它停止的位置*8字节=1000行,因此回答了这个问题。但指标仍然不太好:100个循环,每个循环最好3:5.41毫秒(cython)100个循环,最好为3:3.95毫秒/循环(numpy),但仍然解决了问题。 对上述代码的更改是添加:
cdef variable_bytes=np.dtype(REAL).itemsize#在定义REAL后添加以获取memset的字节,在本例中为8字节

然后在调用memset时:
memset(&_输出[0,0],0,M*N*变量字节)#给出了与np.zeros函数相同的输出
现在我能看到的唯一加速的地方是在大型矩阵上使用
prange
OpenMP语句,但还没有看到。

Nope…
memset
将适用于任何大小。你100%确定M和N是你想要的吗?你打印出来并检查了吗?@Coldspeed我刚刚添加了打印输出,它显示M和N都是1000,M*N是1000000,正如预期的那样因为这等于输出数组中的#,所以还有一个问题。你正在向函数传递一个0的数组,那么你怎么知道它只在第125行工作?@Coldspeed,因为在Spyder中运行发布的脚本后,我可以同时调出两个数组(
cy_outer
np_outer
,它们精确匹配到第124x1000行)。此后,您可以看出元素没有重置为0。我之所以知道这一点,是因为我发布的原始代码不断将先前的计算添加到新的计算中。此
memset
例程显示0在输出数组中的某一点上。这可能与我的C连续数组与F连续数组有关吗?还是与d有关oes
memset
只能在某个方向上清除吗?对不起,我想我应该用更多的C++Hmm,不能说因为我以前没有通过ctypes使用过
memset
。你可以尝试一件事:do
cy_outer=np.ascontiguousarray(np.zeros((a.shape[0],b.shape[0]))
而不是你现在所拥有的,看看你是否仍然面临这个问题。不确定这会有多大的不同,因为
np.zero
内部调用
malloc
+
memset
。不…
memset
可以处理任何大小的问题。你100%确定M和N是你想要的吗?你打印出来了吗eck?@Coldspeed我刚刚添加了打印输出,它显示M和N都是1000,M*N是1000000,正如预期的那样。(100010001000000)因为这等于输出数组中的#,所以还有一个问题。你正在向函数传递一个0的数组,那么你怎么知道它只在第125行工作?@Coldspeed,因为在Spyder中运行发布的脚本后,我可以同时调出两个数组(
cy_outer
np_outer
,它们精确匹配到第124x1000行)。此后,您可以看出元素没有重置为0。我之所以知道这一点,是因为我发布的原始代码不断将先前的计算添加到新的计算中。此
memset
例程显示0在输出数组中的某一点上。这可能与我的C连续数组与F连续数组有关吗?还是与d有关oes
memset
只能在某个方向上清除吗?对不起,我想我应该用更多的C++Hmm,不能说因为我以前没有通过ctypes使用过
memset
。你可以尝试一件事:do
cy_outer=np.ascontiguousarray(np.zeros((a.shape[0],b.shape[0]))
而不是你现在拥有的,看看你是否仍然面临这个问题。否
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)
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)