与 CPython 相比,Numba 和 Cython 并没有显着提高性能,也许我使用不正确?

Posted

技术标签:

【中文标题】与 CPython 相比,Numba 和 Cython 并没有显着提高性能,也许我使用不正确?【英文标题】:Numba and Cython aren't improving the performance compared to CPython significantly, maybe I am using it incorrectly? 【发布时间】:2014-06-26 08:37:51 【问题描述】:

大编辑:

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

为了清楚起见,我将删除旧结果并用更新的结果替换它。问题还是一样:我是否正确使用了 Cython 和 Numba,可以对代码进行哪些改进? (我有一个更新、更简单的临时 IPython 笔记本,其中包含所有代码和结果 here)

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) 

赛通

%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)

麻木

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)

【问题讨论】:

对于您的第一个示例,我不希望 numba 产生重大收益,因为无论如何您都是在 numpy 中进行所有计算。 从 Numba 页面上的示例中,我希望它可能会加速在 numpy 结构上使用 Python 代码循环的代码,但是您的示例除了调用已经用 C 编写的 numpy 函数之外什么都不做. 我对 Numba 了解不多,但我猜您将无法加快您的第一个示例的速度。第二个示例缺乏加速有点令人惊讶,我们将看看是否有更了解 Numba 的人回复。 顺便说一句,很棒的存储库。 谢谢,但 np.linalg.lstsq 实际上更慢!最快的方法是在 Cython 中实现“经典”方法。我已经完成了基准 Cython vs. numpy (np.linalg.lstsq) vs. scipy (scipy.stats.linregress) here @SebastianRaschka:是的,也许我不清楚。 “你的第一个例子”是指py_mat_lstsqrnumba_mat_lstsqr 之间的比较(这并不让我感到惊讶)。但我所说的“你的第二个例子”是numba_lstsqrpy_lstsqr 之间的比较(这让我很惊讶)。谷歌搜索,我看到有人说 Numba 无法推断某些函数中的类型,因此没有加速,但我对 Numba 的了解还不够,不知道这里发生了什么,或者如何改进它。 【参考方案1】:

以下是我认为 Numba 正在发生的事情:

Numba 适用于 Numpy 数组。没有其他的。其他一切都与Numba无关。

zip 返回 Numba 无法看到的任意项的迭代器。因此,Numba 无法进行太多编译。

使用for i in range(...) 遍历索引可能会产生更好的结果并允许更强大的类型推断。

【讨论】:

谢谢!我将重写它并在本周末重做基准测试。我会告诉你结果的! zip 没有做那么多......我现在替换了它,但真正的问题是我作为输入传递的 numpy 数组。 据我了解,并不是说 Numba 无法看到 Numpy 之外的任何东西,而是它处理此类事情的方式并没有改进。查看 Numba - Tell Those C++ Bullies to Get Lost 了解很多 Numba 的精彩解释。【参考方案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

【讨论】:

以上是关于与 CPython 相比,Numba 和 Cython 并没有显着提高性能,也许我使用不正确?的主要内容,如果未能解决你的问题,请参考以下文章

与 Python+Numba LLVM/JIT 编译的代码相比,Julia 的性能

与纯 Python 代码相比,Numba njit 编译器会导致计算不同的数字?

如何使 numba @jit 使用所有 cpu 内核(并行化 numba @jit)

一行代码实现Python运行性能增强百倍,性能发动机numba模块介绍

一行代码实现Python运行性能增强百倍,性能发动机numba模块介绍

Numba 可以与 Tensorflow 一起使用吗?