ATLAS通过Cython vs. numpy:numpy如何更快?

时间:2017-10-05 18:55:47

标签: arrays performance numpy cython gsl

我在Cython中编写一个函数,它将被多次调用。我的大部分代码都是" Cython"语法C,使用GNU科学库(GSL)执行数值运算。

有一件事我感兴趣的是,这是一个特殊的操作,我无法匹配numpy的速度。请注意,此处的性能提升不是 重要,而为什么 numpy看起来更快的原因很有意思。

我所指的特定操作是这个(随机数是虚拟变量 - 我预计它们不会产生影响):

import numpy as np
x = np.array([1.,2.])
Q = np.random.random((1000))
W = np.random.random((1000,2))
b = np.random.random((1000))

Q.dot(np.cos(W.dot(x) + b))

计时最后一行:

%timeit Q.dot(np.cos(W.dot(x) + b))

10000 loops, best of 3: 27 µs per loop
Numpy的速度在这里并不特别让我感到惊讶。我很好奇的是为什么我的cython版本几乎慢两倍。假设变量在函数外部进行了类似的初始化,我按如下方式在cython中执行计算(编辑:本文末尾的完整工作示例 - 删除了一些代码以便整理):

gsl_blas_dgemv(CblasNoTrans,1,W,x,1,b)

for 0 <= i < n:
    b.data[i*step] = cos(b.data[i*step])

gsl_blas_ddot(Q,b,&result)

产:

10000 loops, best of 3: 52.8 µs per loop

当我单独运行该部分功能时。

Cython应该与Atlas链接(我正在我的测试中使用-L/usr/lib64/atlas -ltatlas作为标记,在单元格魔术%%cython之后),并且具有-O3优化标记。

我的问题如下:

  • 我的问题是算法吗?据我所知,W和x之间的点积的循环是必要的,以计算元素的余弦。
  • 在这种情况下,数组的行或列主要排序是否应该有显着差异?
  • 由于这类操作的后端效率更高,所以numpy更快,如果是这样,它是如何实现这一目标的呢?
  • 我觉得这是衡量表现的好方法吗?
  • 我的Cython版本编码很差吗?

我非常有兴趣了解在这种情况下速度差异的来源。

编辑1:完整的cython代码示例

%%cython -lgsl -L/usr/lib64/atlas -ltatlas -lm

import Cython.Compiler.Options as CO
CO.extra_compile_args = ["-f",'-O3',"-ffast-math","-march=native"]
from libc.string cimport memcpy
from libc.math cimport cos
from cython_gsl cimport *
cimport cython

cdef gsl_vector * gsl_vector_create(double[:] v, int n):
    cdef gsl_vector *u = gsl_vector_alloc(n)
    u.data = &v[0]
    return u

cdef gsl_matrix * gsl_matrix_create(double[:,:] A, int n, int m):
    cdef gsl_matrix * B = gsl_matrix_alloc(n,m)
    B.data = &A[0,0]
    return B

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
@cython.nonecheck(False)
def test(double [:] x_, double[:] Q_, double[:,:] W_, double[:] b_, int d, int n):

    cdef:
        gsl_vector * x = gsl_vector_create(x_,d)
        gsl_vector * Q = gsl_vector_create(Q_,n)
        gsl_matrix * W = gsl_matrix_create(W_,n,d)
        gsl_vector * b = gsl_vector_create(b_,n)
        int i


    gsl_blas_dgemv(CblasNoTrans,1,W,x,1,b)

    cdef size_t step = b.stride
    for 0 <= i < n:
        b.data[i*step] = cos(b.data[i*step])

    cdef double result
    gsl_blas_ddot(Q,b,&result)

    return result

然后在python:

x = np.array([1.,2.])
Q = np.random.random((1000))
W = np.random.random((1000,2))
b = np.random.random((1000))

%timeit Q.dot(np.cos(W.dot(x) + b))
%timeit test(x,Q,W,b,1000,2)

修改2

为了完整起见,我还尝试针对GSL的Cblas编译我的cython功能。这与上面仅使用单元魔术标志

完全相同
%%cython -lgsl -lgslcblas -lm

这几乎是针对地图集编译时的两倍:10000 loops, best of 3: 109 µs per loop

0 个答案:

没有答案