我们先看一个原始代码:
1 | import time |
看一下运算时间结果:
1 | time:8.983859300613403 |
然后我们加一行代码,再看看结果:
1 | import numba as nu |
看一下运算时间结果:
1 | time:0.16590428352355957 |
咦~好像快了不止100倍啊
我丢,厉害了我的一行代码
下面来讲解一下神奇的 numba库
如何使用numba
1 | import numba as nb |
只用1行代码即可加速,对loop有奇效
这回肯定又有人问了神马是loop,这里我讲解一下: loop就是循环的意思
然后numba 的优势就是 loop,对循环(loop)有奇效,而往往在科学计算中限制python速度的就是loop。
但要注意的是,numba对没有循环或者只有非常小循环的函数加速效果并不明显,用不用都一样。(偷偷告诉你,numba的loop甚至常常比numpy的矩阵运算还要快)
重点:
numba可以把各种具有很大loop的函数加到很快的速度,但numpy的加速只适用于numpy自带的函数。
示例:
1 | import numba as nb |
看一下输出结果:
1 | time:0.06898283958435059 # numba加速的求和函数 |
可以看出,numba甚至比号称最接近C语言速度运行的numpy还要快6倍以上。
在这里我还想说numba另一个强大的功能,矢量(vectorize)。像上面的jit一样,只要添加一行vectorize就可以让普通函数变成ufunc
1 | from math import sin |
用vectorize改写的三角函数具有了和np.sin()一样的同时处理数组每个元素的功能,而且表现也不必numpy差。当遇到numpy没有的数学函数时(比如sech),用numba矢量化不失为一个好的选择。除了math中的sin,它支持的其他函数列表可以在documentation中找到(链接见附录)。
其实numpy也有矢量化的功能,只不过比numba差远了。
更多numba的加速选项
除了上面提到的jit和vectorize,其实numba还支持很多加速类型。常见的比如
1 | @nb.jit(nopython=True,fastmath=True) 牺牲一丢丢数学精度来提高速度 |
还有一个:
如果希望JIT能针对所有类型的参数进行运算,可以使用autojit:
1 | import numba as nb |
autoit虽然可以根据参数类型动态地产生机器码函数,但是由于它需要每次检查参数类型,因此计算速度也有所降低。numba的用法很简单,基本上就是用jit和autojit这两个修饰器,和一些类型对象。下面的程序列出numba所支持的所有类型:
1 | print [obj for obj in nb.__dict__.values() if isinstance(obj, nb.minivect.minitypes.Type)] |
Numba的精度问题
精度方面,在上面我也谈到numba会自动转换数据类型以适应计算。但是在个别时候,这种自动转变类型可能会引起一些计算误差。通常这个误差是非常小的,几乎不会造成任何影响。但如果你所处理的问题会积累误差,比如求解非线性方程,那么在非常多的计算之后误差可能就是肉眼可见了。如果你发现有这样的问题,记得在jit中指定输入输出的数据类型。
numba具有C所有的数据类型,比如对上面的求和函数,只需要把@nb.jit()改为@nb.jit(nb.int64(nb.int32[:]))即可。nb.int64是说输出的数字为int64类型,nb.int32是说输入的数据类型为int32,而[:]是说输入的是数组。
参考文章:
https://zhuanlan.zhihu.com/p/60994299
https://blog.csdn.net/qq_39241986/article/details/102486661