Cython Memoryview中的数组广播

4

我在Cython中创建了一个类型化的内存视图,并希望将其乘以一个标量:

import numpy as np
import math
cimport numpy as np

def foo():
    N = 10
    cdef np.double_t [:, :] A = np.ones(shape=(N,N),dtype=np.double_)
    cdef int i,j
    cdef double pi = math.pi
    for i in range(N):
        for j in range(N):
            A[i,j] *= pi
    return A

def bar():
    N = 10
    cdef np.double_t [:, :] A = np.ones(shape=(N,N),dtype=np.double_)
    cdef double pi = math.pi
    A *= pi
    return A

函数foo()可以完成这个任务,但不是很方便/易读。

然而,在函数bar()中的代码行A *= pi无法编译:Invalid operand types for '*' (double_t[:, :]; double)

是否有一种方法在Cython内存视图上执行此类广播操作?

1个回答

4
不,内存视图并不会这样做。内存视图只是一种快速访问数组单个元素的方法,它没有真正的概念可用于数组上可以执行的数学运算。
在您的“bar”函数中,任何尝试类型化它的操作可能实际上会使它变得更糟(即它将花费额外的时间来检查类型,但最终仍需使用普通调用Numpy函数完成工作)。
有许多(不完全令人满意的)方法从内存视图中获取一个Numpy数组:
  1. np.asarray(memview) - this should be done without copying (provided you aren't using the esoteric indirect memory layout). It might be worth adding an assertion to check that no copy was made though.

  2. memview.base - be slightly careful with this. If the memoryview is a result of slicing then .base will be the original unsliced object.

  3. Keep a parallel numpy array and memoryview variable:

     Anp = np.array(...)
     cdef double[:] Amview = Anp
    

    because the memoryview is a view of some memory, modifications to the array will be reflected in the memoryview and vice-versa. (Reassigning the array variable, e.g. Anp = something_else, won't be reflected though).


简而言之,memoryviews 的主要功能是快速访问单个元素。如果您不需要这样做,那么您可能不需要使用 memoryviews。


谢谢您的建议!您是正确的,最好的方法可能是使用有类型的内存视图进行快速索引,否则使用经典的NumPy函数。 - David
抱歉,我不明白。我也有同样的问题,即我们能否在Cython中使用memoryview进行类似于numpy的广播。看起来 @DavidW 在这个答案中回答了是否可以,所以memoryview只应用于快速索引的目的,在Cython内部应该使用numpy函数进行广播,对吗?如果是这样,那么在Cython中使用numpy函数会导致Python开销吗? - avocado
另外,如果我们在memoryview上使用numpy函数(例如np.sum),那么这个cdef函数就不能是nogil的,对吗? - avocado
1
@avocado Numpy函数会导致Python开销,并且需要GIL。这是方便的代价。 - DavidW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接