我一直在玩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
中提到的标志。
答案 0 :(得分:3)
看起来NumPy使用可用于min
和max
的SSE指令,这意味着他们可能会比Cython更大程度地利用您的硬件。
以下是SSE中NumPy的min
和max
缩减实施的源代码: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值得一读。