Cython与numpy性能扩展

时间:2014-12-19 23:35:42

标签: python numpy cython

我一直在玩Cython以准备其他工作。我尝试了一个简单的测试用例,并注意到我的代码执行更大问题大小的方式有些奇怪。我创建了一个简单的最小/最大函数,用于计算2D float32数组的最小值和最大值,并将其与运行numpy.min(a), numpy.max(a)进行比较。对于10000个元素的数组,性能类似。对于1000000个元素的数组,cython表现得更糟。这是我的cython代码:

import numpy
cimport cython
cimport numpy

DTYPE = numpy.float32
ctypedef numpy.float32_t DTYPE_t

@cython.boundscheck(False)
@cython.wraparound(False)
def minmax_float32(numpy.ndarray[DTYPE_t, ndim=2] arr):
    cdef DTYPE_t min = arr[0, 0]
    cdef DTYPE_t max = arr[0, 0]
    cdef int row_max = arr.shape[0]
    cdef int col_max = arr.shape[1]
    cdef int x, y
    for y in range(row_max):
        for x in range(col_max):
            if arr[y, x] < min:
                min = arr[y, x]
            if arr[y, x] > max:
                max = arr[y, x]

    return min, max

这是我在ipython中完成的简单时间:

a = numpy.random.random(10000).reshape((100, 100)).astype(numpy.float32)
%timeit -r3 -n50 (numpy.min(a), numpy.max(a))
# 50 loops, best of 3: 22.2 µs per loop

%timeit -r3 -n50 minmax_float32(a)
# 50 loops, best of 3: 23.8 µs per loop

a = numpy.random.random(1000000).reshape((1000, 1000)).astype(numpy.float32)
%timeit -r3 -n50 (numpy.min(a), numpy.max(a))
# 50 loops, best of 3: 307 µs per loop

%timeit -r3 -n50 minmax_float32(a)
# 50 loops, best of 3: 1.22 ms per loop

307 / 22.2
# 13.82882882882883

1220 / 23.8
# 51.26050420168067

有没有人知道为什么cython需要更长时间才能获得更大的输入?这只是我正在玩的东西,但如果你有任何提示或技巧,我有兴趣听到它们。提前谢谢。

编辑:我在带有8GB内存的macbook 10.10上运行这些测试。使用macports中的gcc编译cython,并使用他们的教程-shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing中提到的标志。

2 个答案:

答案 0 :(得分:3)

看起来NumPy使用可用于minmax的SSE指令,这意味着他们可能会比Cython更大程度地利用您的硬件。

以下是SSE中NumPy的minmax缩减实施的源代码:https://github.com/numpy/numpy/blob/master/numpy/core/src/umath/simd.inc.src#L696。请注意,他们使用预处理器同时自动为多种数据类型和操作生成代码。

答案 1 :(得分:1)

首先为了避免混淆,使用函数名min和max作为变量名来构建永远不是一个好主意,所以调用fmin和fmax。

基本上值得记住的是numpy是高度优化的,你也可以尝试改变你的cython:

    for x in range(col_max):
        if arr[y, x] < min:
            min = arr[y, x]
        if arr[y, x] > max:
            max = arr[y, x]

为:

    for x in range(col_max):
        val = arr[y, x]
        if val < fmin:
            fmin = val
        if val > fmax:
            fmax = val

并添加定义:cdef DTYPE_t val

这会将数组索引操作的数量从4减少到1。

您也可以尝试使用:

    (fmin, fmax) = (min(fmin, val), max(fmax, val))

因为它可能会有所改善。

您还可以将x,y,row_max和row_min设置为无符号整数,并通过添加函数装饰器@cython.boundscheck(False) # turn of bounds-checking for entire function 来关闭边界检查

tutorial值得一读。