如果尺寸未知,请在cython中使用Typed memoryview

时间:2015-04-07 06:12:02

标签: cython memoryview

我想使用类型化的memoryview来优化函数,但我不知道参数类型是什么。它可能是一个numpy数组甚至是一个标量。那么我应该如何使用typed memoryview?

1 个答案:

答案 0 :(得分:1)

这类问题的问题在于动态键入Python,因此在选择要采用的代码路径时,您总是会失去速度。但是,原则上您可以非常快速地创建单独的代码路径。可能会给你带来好结果的方法是:

  1. 定义“实现”功能,该功能在1D内存视图上运行。
  2. 定义一个对任何python对象进行操作的包装函数。
    1. 如果它通过了1D内存视图,请调用实现函数;
    2. 如果它传递了一个标量,则创建一个1x1数组并调用实现函数;
    3. 如果它传递了一个多D数组,那么要么为实现函数展平它,要么遍历行,调用每一行的实现函数。
  3. 接下来是快速实施。这假定您希望将一个函数应用于输入数组的每个元素(并希望输出数组具有相同的大小)。我选择的说明函数只是为每个值加1。它也使用了numpy(我认为合理的地方(而不仅仅是键入的内存视图):

    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更复杂。