【问题标题】:Why is writing to C array this slow in Cython?为什么在 Cython 中写入 C 数组这么慢?
【发布时间】:2014-09-21 16:41:20
【问题描述】:

我有一个 Cython 类,如下面的人工示例:

cdef class Differential:

  cdef int *SX
  cdef int *X
  cdef int nmax

  def __init__(self, int nmax):

    self.nmax = nmax ## usually around 10*1000

    return

  def __cinit__(self, int nmax, *arg, **args):

    self.SX = <float *>malloc(nmax*cython.sizeof(float))

    ## assume self.X has some content.
    self.X = <float *>malloc(nmax*cython.sizeof(float)) 

    return

  def __dealloc__(self):

    free(self.SX)
    free(self.X)

    return

  @cython.wraparound(False)
  @cython.boundscheck(False)
  @cython.nonecheck(False)
  @cython.cdivision(True)
  cdef void __reject(self, float step) nogil:

    cdef unsigned int v
    cdef unsigned int k

    cdef double x
    cdef double dx

    float_array_init(self.SX,1000,0.) ## writes 0. to the 100000 first elements

    for v in range(1000):

      x = self.X[v]

      for k in range(v+1,1000):

        dx = x-self.X[k]

        # the following line is the "problem":
        self.SX[k] -= dx

    ## some more code
    # manipulate SX some more. this section has less performance impact because it
    # is not a double for-loop, so i have not included it in the example

    # update X
    for v in range(1000):
      self.X[v] += self.SX[v]

def reject(self, float step):
  self.__reject(step)

代码涉及的内容要多得多,但我已尝试将其精简到仍能说明我的代码流程的最小数量。

在我的主脚本中,我只是创建了一个Differential 实例,然后我反复调用Differential.reject()(以及中间的一些其他东西来实际更改X 的值。)

我知道我可以使用cpdef 来避免额外的包装器调用(__reject() 和reject()),但我的测试表明这没有区别。

我的问题如下:

当我注释掉 self.SX[k] -= dx 行时,代码似乎加速了大约 10 倍。这是意料之中的吗?

我知道访问内存是有代价的,但我没想到代码会这么慢。

更新

如下所示,换行

cdef double x
cdef double dx

cdef float x
cdef float dx

消除了一些强制转换操作的需要,并将代码速度提高了大约 2 倍。

【问题讨论】:

  • 发布 Cython 生成的 C 代码(相关部分)会很有用。
  • 虽然它可能只是缓存;没有那条线,双循环中使用的唯一内存区域是 self.X,所以它很可能会一直保留在缓存中。当您添加 self.SX 行时,使用的堆数量翻了一番,这可能会将其推出缓存吗?取决于 nmax 的值是多少
  • 我稍后会尝试这样做。问题是我的实际代码要复杂得多,所以剥离它有点困难。我目前的理论也是它是缓存,@BluePeppers。感谢您的意见。

标签: python optimization cython


【解决方案1】:

我发现了一个可以解释缓慢的问题,请注意,您将 xdx 创建为 double 以接收 float 值,方法是:

cdef float x
cdef float dx

我得到了 2 倍的加速,因为它避免了在 x = self.X[v] 中将浮点值转换为双精度值,然后在 self.SX[k] -= dx 中再次从双精度值转换为浮点值。

您的方法似乎没有丢失缓存,我测试了使用单个数组来存储self.Xself.SX 的值,方法是通过2*i+0 或@ 控制访问987654331@(0 代表self.X1 代表self.SX),时间相同。

【讨论】:

  • 好收获。我已经接受了这个作为答案。感谢您的测试!
猜你喜欢
  • 1970-01-01
  • 2017-07-10
  • 1970-01-01
  • 1970-01-01
  • 2014-10-26
  • 1970-01-01
  • 2012-04-30
  • 1970-01-01
  • 2012-11-19
相关资源
最近更新 更多