Python本身是一种慢跑语言,所以对于计算场景,最好的优化方法就是优化代码编写。您可以使用现有的科学计算库:例如Numpy和Scipy。但是如果你想实现一个新的算法而不用使用低级语言(比如CPython、Rust等)来实现扩展怎么办?对于一些特定的计算场景,尤其是数组,Numba可以显着地让你的代码运行得更快。在使用的时候,我们有时需要对原来的代码进行调整,有时则不需要做任何改动。真正起作用的时候,效果会非常明显。在本文中,我们将探讨:为什么仅靠Numpy有时是不够的使用Numba的基础知识Numba如何影响您的代码在非常高的级别上的工作方式假设您想将一个非常大的数组转换为升序:它是容易理解,元素按值从小到大排列,如:[1,2,1,3,3,5,4,6]→[1,2,2,3,3,5,5,6]这是一个简单的就地转换:defmonotonically_increasing(a):max_value=0foriinrange(len(a)):ifa[i]>max_value:max_value=a[i]a[i]=max_valueNumpy运行速度很快,因为它可以在不调用python自带的解释器的情况下完成所有计算。但是对于上面的场景(python中的循环),就会暴露出一个问题:我们将失去Numpy独有的性能优势。使用上述函数转换具有1000万个元素的Numpy数组需要2.5秒才能在我的计算机上运行。那么,是否有可能优化得更快?使用Numba加速Numba是一个为python构建的即时编译器,专为Numpy数组循环计算场景而设计。显然,这正是我们所需要的。让我们在原始函数中添加两行代码:fromnumbaimportnjit@njitdefmonotonically_increasing(a):max_value=0foriinrange(len(a)):ifa[i]>max_value:max_value=a[i]a[i]=max_value再次运行,发现只需要0.19秒。在完全复用旧代码逻辑的前提下,效果还不错。其实Numpy还有一个专门的函数可以解决这个场景(但是会修改原函数的代码逻辑):`numpy.maximum.accumulate`。通过使用它,函数的运行时间减少到0.03秒。Numba简介在Numpy或Scipy中查找目标函数,快速解决常见的计算问题。但是,如果该功能不存在怎么办?(就像刚才的numpy.maximum.accumulate)。在这种情况下,如果你想加快代码运行速度。可以选择其他底层编程语言来实现扩展,但这也意味着切换编程语言,这将使模块构建和整个系统变得更加复杂。使用Numba你可以做到:用python和编译速度更快的解释器运行相同的代码简单快速的迭代算法Numba首先解析代码,然后根据数据的输入类型实时编译它们。比如当输入是u64数组和浮点数数组时,编译出来的结果是不一样的。Numba也可以在非CPU计算场景生效:比如你可以在GPU上运行代码。诚然,上面的例子只是Numba的一个最小应用,官方文档中有很多功能可以选择。Numba的一些缺点需要耗时的代码编译。Numba修饰的函数在第一次被调用时,需要一定的时间才能生成相应的机器码。例如,我们可以使用IPython的%time命令来计算运行一个Numba装饰函数需要多长时间:In[1]:fromnumbaimportnjitIn[2]:@njit...:defadd(a,b):a+bIn[3]:%timeadd(1,2)CPUtimes:user320ms,sys:117ms,total:437msWalltime:207msIn[4]:%timeadd(1,2)CPU时间:用户17μs,sys:0ns,总计:17μsWall时间:24.3μsIn[5]:%timeadd(1,2)CPU时间:用户8μs,sys:2μs,总计:10μsWall时间:13.6μs可以看到,函数在第一次调用后运行的非常慢(注意单位是毫秒而不是微秒),这是因为编译生成机器码需要时间。但是,函数后面的运行速度会有明显提升。当输入数据的类型改变时,这个时间成本会再次消耗。例如,我们将输入类型改为浮点数:In[8]:%timeadd(1.5,2.5)CPUtimes:user40.3ms,sys:1.14ms,total:41.5msWalltime:41msIn[9]:%timeadd(1.5,2.5)CPUtimes:user16μs,sys:3μs,total:19μsWalltime:26μs计算两个数的和当然不需要启用Numba,这里用这个case是因为它更容易看出编译所需的时间成本。与python和Numpy的不同实现Numba在功能上可以说是实现了python的一个子集,也可以说是实现了NumpyAPI的一个子集,这会导致一些潜在的问题:(1)会有一些不支持python和Numpy的特性(2)由于Numba重新实现了NumpyAPI,使用时可能会出现以下情况:由于使用的算法不同,可能由于bug导致两者性能不一致结果(3)另外,当Numba编译失败时,它暴露的错误信息可能难以理解。Numba与其他选项的比较仅使用Numpy和Scipy:它可以使python代码以其他语言编译器的速度运行,但是,它不适用于某些循环计算场景直接使用低级语言编写代码:这意味着你可以优化所有的代码语句,但是你需要放弃python,换一种语言来使用Numba:你可以优化python循环计算的场景,但是对于python语言本身和NumpyAPI的一些特性会有所限制。结论Numba最好的地方在于它非常易于尝试。因此,每当你有一个慢速的for循环来做一些数学运算时,试试Numba:运气好的话,它只需要两行代码就可以显着加快你的代码速度。
