纯python上的Numba与numpython上的Numpa

2024-06-16 10:58:21 发布

您现在位置:Python中文网/ 问答频道 /正文

使用numba比使用纯python生成的程序快得多:

现在看来,纯python上的numba甚至(大多数时候)比numpy python快,例如https://jakevdp.github.io/blog/2015/02/24/optimizing-python-with-numpy-and-numba/。在

根据https://murillogroupmsu.com/julia-set-speed-comparison/,在纯python代码中使用numba比在使用numpy的python代码上使用更快。这是真的吗?为什么?在

https://stackoverflow.com/a/25952400/4533188中,解释了为什么纯python上的numba比numpy python快:numba看到更多的代码,并且有更多的方法来优化代码,而numpy只看到一小部分。在

这能回答我的问题吗?我是否妨碍numba在使用numpy时完全优化我的代码,因为numba被迫使用numpy例程,而不是找到一个更优化的方法?我曾希望麻木能意识到这一点,如果没有好处的话,就不要使用numpy程序。然后它会使用numpy例程,只是这是一个改进(毕竟numpy已经过很好的测试)。毕竟"Support for NumPy arrays is a key focus of Numba development and is currently undergoing extensive refactorization and improvement."


Tags: and代码httpscommunity程序numpycomwww
2条回答

在我回答具体问题之前,让我们先弄清楚一些事情:

  • 对于这个答案,我只考虑nopython代码,对象模式代码通常比纯Python/NumPy等效代码慢。在
  • 对于这个答案,我将忽略numba GPU的功能-很难比较GPU上运行的代码和CPU上运行的代码。在
  • 在numba函数中调用NumPy函数时,实际上并不是在调用NumPy函数。numba支持的一切都在numba中重新实现。这适用于NumPy函数,也适用于numba中的Python数据类型!因此,numba函数内部和外部Python/NumPy之间的实现细节可能不同,因为它们是完全不同的函数/类型。在
  • Numba生成用LLVM编译的代码。Numba不是魔术,它只是一个优化编译器的包装,它内置了一些优化!在

It seems established by now, that numba on pure python is even (most of the time) faster than numpy-python

不,麻木通常比纽比慢。这取决于你想做什么手术和怎么做。如果您处理非常小的数组,或者如果唯一的替代方法是手动迭代数组,Numba的速度确实更快。在

numba used on pure python code is faster than used on python code that uses numpy. Is that generally true and why?

这取决于代码-可能有更多的情况下,纽比击败了麻木。然而,诀窍是在没有相应的NumPy函数的地方应用numba,或者在需要链接很多NumPy函数或使用不理想的NumPy函数的地方。诀窍在于知道numba实现何时可能更快,然后最好不要在numba中使用NumPy函数,因为这样会得到NumPy函数的所有缺点。然而,需要经验来了解何时应用numba,以及如何应用numba-很容易意外地编写一个非常慢的numba函数。在

Do I hinder numba to fully optimize my code when using numpy, because numba is forced to use the numpy routines instead of finding an even more optimal way?

是的。在

I had hoped that numba would realise this and not use the numpy routines if it is non-beneficial.

不,麻木现在不是这样工作的。Numba只是为LLVM创建代码来编译。也许这是numba将来的特色(谁知道呢)。目前,如果您自己编写循环和操作,并且避免在numba函数内部调用NumPy函数,numba的性能最好。在

有一些库使用表达式树并可能优化非有益的NumPy函数调用,但这些库通常不允许快速的手动迭代。例如,numexpr可以优化多个链式NumPy函数调用。目前,它要么是快速手动迭代(cython/numba)要么是使用表达式树(expression trees,numexpr)优化链式NumPy调用。也许在一个图书馆里连两个都不可能-我不知道。在


Numba和Cython在处理小数组和快速手动迭代时非常棒。NumPy/SciPy非常棒,因为它们有很多复杂的功能,可以在开箱即用的情况下完成各种任务。Numexpr非常适合链接多个NumPy函数调用。在某些情况下,Python比这些工具都快。在

以我的经验,如果你把不同的工具组合起来,你就可以充分利用它们。不要只局限于一种工具。在

According to https://murillogroupmsu.com/julia-set-speed-comparison/ numba used on pure python code is faster than used on python code that uses numpy. Is that generally true and why?

In https://stackoverflow.com/a/25952400/4533188 it is explained why numba on pure python is faster than numpy-python: numba sees more code and has more ways to optimize the code than numpy which only sees a small portion.

Numba只是用它自己的实现替换numpy函数。它们可以更快/更慢,结果也可能不同。问题在于这种替换是如何发生的。经常会涉及不必要的临时数组和循环,它们可以被融合在一起。在

循环融合和移除临时阵列不是一件容易的事。如果为并行目标(循环融合效果更好)或单线程目标编译,则行为也会有所不同。在

[编辑] 在并行加速器中完成的优化Section 1.10.4. Diagnostics(如循环融合)也可以通过设置parallel=Truenb.parfor.sequential_parfor_lowering = True来启用。1

示例

#only for single-threaded numpy test
import os
os.environ["OMP_NUM_THREADS"] = "1"

import numba as nb
import numpy as np

a=np.random.rand(100_000_000)
b=np.random.rand(100_000_000)
c=np.random.rand(100_000_000)
d=np.random.rand(100_000_000)

#Numpy version
#every expression is evaluated on its own 
#the summation algorithm (Pairwise summation) isn't equivalent to the algorithm I used below
def Test_np(a,b,c,d):
    return np.sum(a+b*2.+c*3.+d*4.)

#The same code, but for Numba (results and performance differ)
@nb.njit(fastmath=False,parallel=True)
def Test_np_nb(a,b,c,d):
    return np.sum(a+b*2.+c*3.+d*4.)

#the summation isn't fused, aprox. the behaiviour of Test_np_nb for 
#single threaded target
@nb.njit(fastmath=False,parallel=True)
def Test_np_nb_eq(a,b,c,d):
    TMP=np.empty(a.shape[0])
    for i in nb.prange(a.shape[0]):
        TMP[i]=a[i]+b[i]*2.+c[i]*3.+d[i]*4.

    res=0.
    for i in nb.prange(a.shape[0]):
        res+=TMP[i]

    return res

#The usual way someone would implement this in Numba
@nb.njit(fastmath=False,parallel=True)
def Test_nb(a,b,c,d):
    res=0.
    for i in nb.prange(a.shape[0]):
        res+=a[i]+b[i]*2.+c[i]*3.+d[i]*4.
    return res

计时

^{pr2}$

结果

#single-threaded
res_1=Test_nb(a,b,c,d)
499977967.27572954
res_2=Test_np(a,b,c,d)
499977967.2756622
res_3=Test_np_nb(a,b,c,d)
499977967.2756614
res_4=Test_np_nb_eq(a,b,c,d)
499977967.2756614

#multi-threaded
res_1=Test_nb(a,b,c,d)
499977967.27572465
res_2=Test_np(a,b,c,d)
499977967.2756622
res_3=Test_np_nb(a,b,c,d)
499977967.27572465
res_4=Test_np_nb_eq(a,b,c,d)
499977967.27572465

结论

这取决于用例什么是最好的使用。有些算法可以很容易地用Numpy写成几行,而另一些算法则很难或不可能以矢量化的方式实现。在

我还特意用了一个求和的例子。一次完成这一切很容易,而且速度也快得多,但是如果我想要最精确的结果,我肯定会使用一个已经在Numpy中实现的更复杂的算法。当然,你也可以在Numba做同样的事情,但那将是更多的工作要做。在

相关问题 更多 >