我在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优化标记。
我的问题如下:
我非常有兴趣了解在这种情况下速度差异的来源。
编辑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
。