Numba和Cython相比CPython性能提升不明显,可能是我使用不当?

16 投票
2 回答
3190 浏览
提问于 2025-04-18 05:53

大编辑:

================

为了更清楚,我把旧的结果删掉,换成了更新的结果。问题还是一样的:我在使用Cython和Numba的时候对不对?代码有什么改进的地方吗?(我有一个更新的、比较简单的临时IPython笔记本,里面有所有的代码和结果,在这里

1)

我觉得我明白了为什么最开始Cython、Numba和CPython之间没有区别:是因为我给它们输入的是

numpy数组:

x = np.asarray([x_i*np.random.randint(8,12)/10 for x_i in range(n)])

而不是列表:

x = [x_i*random.randint(8,12)/10 for x_i in range(n)]

使用Numpy数组作为数据输入的基准测试

这里输入图片描述

使用Python列表作为输入的基准测试

这里输入图片描述

2)

我把zip()函数换成了显式的循环,但这并没有太大区别。代码如下:

CPython

def py_lstsqr(x, y):
    """ Computes the least-squares solution to a linear matrix equation. """
    len_x = len(x)
    x_avg = sum(x)/len_x
    y_avg = sum(y)/len(y)
    var_x = 0
    cov_xy = 0
    for i in range(len_x):
        temp = (x[i] - x_avg)
        var_x += temp**2
        cov_xy += temp*(y[i] - y_avg)
    slope = cov_xy / var_x
    y_interc = y_avg - slope*x_avg
    return (slope, y_interc) 

Cython

%load_ext cythonmagic

%%cython
def cy_lstsqr(x, y):
    """ Computes the least-squares solution to a linear matrix equation. """
    cdef double x_avg, y_avg, var_x, cov_xy,\
         slope, y_interc, x_i, y_i
    cdef int len_x
    len_x = len(x)
    x_avg = sum(x)/len_x
    y_avg = sum(y)/len(y)
    var_x = 0
    cov_xy = 0
    for i in range(len_x):
        temp = (x[i] - x_avg)
        var_x += temp**2
        cov_xy += temp*(y[i] - y_avg)
    slope = cov_xy / var_x
    y_interc = y_avg - slope*x_avg
    return (slope, y_interc)

Numba

from numba import jit

@jit
def numba_lstsqr(x, y):
    """ Computes the least-squares solution to a linear matrix equation. """
    len_x = len(x)
    x_avg = sum(x)/len_x
    y_avg = sum(y)/len(y)
    var_x = 0
    cov_xy = 0
    for i in range(len_x):
        temp = (x[i] - x_avg)
        var_x += temp**2
        cov_xy += temp*(y[i] - y_avg)
    slope = cov_xy / var_x
    y_interc = y_avg - slope*x_avg
    return (slope, y_interc)

2 个回答

2

使用内置的 sum() 函数可能会导致一些问题。

下面是一个可以在 Numba 中运行得更快的线性回归代码:

@numba.jit
def ols(x, y):
    """Simple OLS for two data sets."""
    M = x.size

    x_sum = 0.
    y_sum = 0.
    x_sq_sum = 0.
    x_y_sum = 0.

    for i in range(M):
        x_sum += x[i]
        y_sum += y[i]
        x_sq_sum += x[i] ** 2
        x_y_sum += x[i] * y[i]

    slope = (M * x_y_sum - x_sum * y_sum) / (M * x_sq_sum - x_sum**2)
    intercept = (y_sum - slope * x_sum) / M

    return slope, intercept
3

我觉得Numba的情况是这样的:

Numba只对Numpy数组有效,其他的东西都不行。其他任何东西和Numba都没有关系。

zip会返回一个可以遍历的任意项目的集合,而Numba无法理解这些内容。所以Numba在这方面的编译能力就有限了。

如果用for i in range(...)来遍历索引,效果可能会好很多,并且能让Numba更好地推断数据类型。

撰写回答