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[1]。通过使用它,函数的运行时间减少到0.03秒。RuntimePythonforloop2560msNumbaforloop190msnp.maximum.accumulate30msNumba介绍在Numpy或Scipy中寻找目标函数可以快速解决常见的计算问题。但是,如果该功能不存在怎么办?(比如刚才的numpy.maximum.accumulate)。在这种情况下,如果你想加快代码运行速度。可以选择其他低级编程语言来实现扩展[2],但这也意味着切换编程语言,这将使模块构建和整个系统变得更加复杂。使用Numba你可以做到:用python和编译速度更快的解释器运行相同的代码简单快速的迭代算法Numba首先解析代码,然后根据数据的输入类型实时编译它们。比如当输入是u64数组和浮点数数组时,编译出来的结果是不一样的。Numba也可以在非CPU计算场景中生效:例如,您可以在GPU上运行代码[3]。诚然,上面的例子只是Numba的一个最小应用,官方文档[4]中有很多特性可以选择。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计算两个数的和当然不需要EnableNumba,这里用这个case是因为它更容易看出编译所需的时间成本。python和Numpy的实现方式不同Numba在功能上可以说是实现了python的一个子集,也可以说是实现了NumpyAPI的一个子集,这会导致一些潜在的问题:会有python和Numpy不支持某些功能。由于Numba重新实现了NumpyAPI,使用时可能会出现以下情况。由于使用的算法不同,两者的性能可能存在差异。由于错误,结果可能不一致。另外,当Numba编译失败时,它暴露的错误信息可能难以理解。Numba和其他选项的比较只使用了Numpy和Scipy:它可以让python代码以其他语言编译器的速度运行,但是对于某些循环计算场景不起作用直接使用低级语言编写代码:这意味着youcanoptimizeallcodestatements,butyouneedtoabandonpythonandonelanguagetouseNumba:你可以优化python循环计算的场景,但是对于python语言本身和NumpyAPI的一些特性,使用是有限的Epilogue的伟大之处在于Numba就是这么容易尝试。因此,每当你有一个慢速的for循环来做一些数学运算时,试试Numba:运气好的话,它只需要两行代码就可以显着加快你的代码速度。以上就是本次分享的全部内容。觉得文章还不错的话,请关注公众号:Python编程学习圈,每日干货分享,发送“J”还能收到海量学习资料,涵盖Python电子书和教程,数据库编程、Django、爬虫、云计算等。或者去编程学习网了解更多编程技术知识。
