我想使用类型化的memoryview优化函数,但是我不是参数类型。它可以是一个numpy数组,甚至是一个标量。那我该如何使用键入的memoryview?

最佳答案

这种问题的问题在于Python是动态类型的,因此在选择采用哪种代码路径时,您总是会失去速度。但是,原则上您可以使各个代码路径变得非常快。一种可能会为您带来良好结果的方法是:

  • 定义一个“实现”功能,该功能在1D memoryview上运行。
  • 定义可在任何python对象上运行的包装函数。
  • 如果传递了1D内存视图,则调用实现函数;
  • 如果传递了标量,则制作一个1x1数组并调用实现函数;
  • 如果它传递了一个Multi-D数组,则将其展平以用于实现函数,或者遍历行,为每一行调用实现函数。

  • 快速实施如下。这假定您要对输入数组的每个元素应用一个函数(并希望输出数组的大小相同)。我选择的说明性函数只是将1加上每个值。我认为这在合理的地方也使用了numpy(而不只是键入的memoryviews):
    cimport cython
    import numpy as np
    import numbers
    
    @cython.boundscheck(False)
    cdef double[:] _plus_one_impl(double[:] x):
      cdef int n
      cdef double[:] output
    
      output = x.copy()
      for n in range(x.shape[0]):
        output[n] = x[n]+1
      return output
    
    def plus_one(x):
      if isinstance(x,numbers.Real): # check if it's a number
        return _plus_one_impl(np.array([x]))[0]
      else:
        try:
          return _plus_one_impl(x)
        except ValueError: # this gets thrown if conversion fails
          if len(x.shape)<2:
            raise ValueError('x could not be converted to double [:]')
          output = np.empty_like(x) # output is all numpy, whatever the input is
          for n in range(x.shape[0]): # this loop isn't typed, so is likely to be pretty slow
            output[n,...] = plus_one(x[n,...])
          return output
    

    在某些情况下(例如二维尺寸较短的2D数组),此代码可能最终会变慢。

    但是,我的真正建议是研究numpy ufuncs,它们为有效地实现这种事情提供了一个接口。 (请参阅http://docs.scipy.org/doc/numpy-dev/user/c-info.ufunc-tutorial.html)。不幸的是,它们比Cython还复杂。

    09-16 19:32