为什么cffi比numpy快这么多?

15 投票
1 回答
4337 浏览
提问于 2025-04-18 02:39

我最近在玩用Python写cffi模块,发现它们的速度让我开始怀疑自己是不是在正确使用标准Python。这让我有点想完全转向C语言!说实话,有一些很棒的Python库我自己根本无法用C实现,所以这更多的是一种假设而已。

这个例子展示了在Python中使用sum函数和numpy数组,以及它和C语言函数相比有多慢。有没有更快的Python方法来计算numpy数组的总和呢?

def cast_matrix(matrix, ffi):
    ap = ffi.new("double* [%d]" % (matrix.shape[0]))
    ptr = ffi.cast("double *", matrix.ctypes.data)
    for i in range(matrix.shape[0]):
        ap[i] = ptr + i*matrix.shape[1]                                                                
    return ap 

ffi = FFI()
ffi.cdef("""
double sum(double**, int, int);
""")
C = ffi.verify("""
double sum(double** matrix,int x, int y){
    int i, j; 
    double sum = 0.0;
    for (i=0; i<x; i++){
        for (j=0; j<y; j++){
            sum = sum + matrix[i][j];
        }
    }
    return(sum);
}
""")
m = np.ones(shape=(10,10))
print 'numpy says', m.sum()

m_p = cast_matrix(m, ffi)

sm = C.sum(m_p, m.shape[0], m.shape[1])
print 'cffi says', sm

只是为了展示这个函数是有效的:

numpy says 100.0
cffi says 100.0

现在如果我给这个简单的函数计时,我发现numpy真的很慢!我是不是在正确使用numpy?有没有更快的方法在Python中计算总和呢?

import time
n = 1000000

t0 = time.time()
for i in range(n): C.sum(m_p, m.shape[0], m.shape[1])
t1 = time.time()

print 'cffi', t1-t0

t0 = time.time()
for i in range(n): m.sum()
t1 = time.time()

print 'numpy', t1-t0

计时结果:

cffi 0.818415880203
numpy 5.61657714844

1 个回答

15

Numpy比C慢主要有两个原因:一是Python的开销(这可能和cffi类似),二是它的通用性。Numpy是为了处理各种维度的数组而设计的,支持很多不同的数据类型。你提到的cffi例子是针对一个二维的浮点数组。这样做的代价是写了好几行代码,而用.sum()只需要6个字符,却节省了不到5微秒的时间。(不过,你肯定已经知道这些了)。我只是想强调,CPU的时间是便宜的,远比开发者的时间便宜。

如果你想继续使用Numpy,并且想要提高性能,最好的选择是使用Bottleneck。它提供了一些针对一维和二维浮点数组优化的函数,速度非常快。在你的情况下,速度快了16倍,执行时间大约是0.35秒,差不多是cffi的两倍快。

对于Bottleneck没有的其他函数,你可以使用Cython。它可以帮助你用更像Python的语法来写C代码。或者说,你可以逐步将Python代码转换成C,直到你对速度满意为止。

撰写回答