用Numba加速Python代码
2019年06月24日 由 sunlei 发表
849436
0
“我们不能再用Python,它太慢了。”
任何长期使用Python的人都可能曾经听过类似的声音。
说这句话的人也没有错。与许多其他编程语言相比,Python很慢。Benchmark game有一些比较不同编程语言在不同任务上的速度的可靠的基准。
解决这个速度问题的一个常见方法是用C++之类的快速语言重新编写代码,然后在上面抛出一个Python包装器。这将使您获得C++的速度,同时保持在主应用程序中轻松使用Python。
当然,这样做的挑战是,您必须用C++重新编写代码;这是一个非常耗时的过程。
Python库Numba为我们提供了一种简单的方法来解决这一挑战——无需编写任何代码,只需编写Python!
关于Numba
Numba是一个编译器库,它将Python代码转换为优化的机器码。通过这种转换,Numba可以使用Python编写的数值算法达到C代码的速度。
您也不需要对Python代码做任何花哨的操作。只需在要优化的Python函数之前添加一行代码,Numba将完成其余的工作!
我们可以使用pip安装Numba:
pip install numba
如果您的代码有很多数值运算,经常使用Numpy,并且/或者有很多循环,那么Numba应该会给您一个很好的加速。
加速Python循环
Numba最基本的用途是加速那些可怕的Python for循环。
首先,如果在Python代码中使用循环,首先检查是否可以用numpy函数替换它总是一个好主意。当然,在某些情况下numpy没有您想要的功能。
在我们的第一个例子中,我们将用Python为插入排序算法编写一个函数。该函数将接受一个未排序的列表作为输入,并返回排序后的列表作为输出。
下面的代码首先构造一个包含100,000个随机整数的列表。然后,我们连续50次对列表应用插入排序,并测量所有50个排序操作的平均速度。
100000个数字是需要排序的相当多的数字,特别是当我们的排序算法的平均复杂度为O(n²)时。在我的i7–8700K电脑上,对所有这些数字进行排序平均需要3.0104秒!
众所周知,Python循环很慢。更糟糕的是,在我们的例子中,for循环中有一个while循环。另外,因为我们的排序算法是O (n²),当我们添加更多的项目列表,我们的运行时增加成平方!
让我们用numba加快速度。
当我们看到一个函数包含用纯Python编写的循环时,这通常是numba能够提供帮助的一个好迹象。查看下面的代码,看看它是如何工作的。
我们的代码只增加了两行。第一个是导入jit修饰器的import语句。第二个问题是我们在函数上使用了jit修饰器。
将jit装饰器应用于函数向numba发出信号,表示我们希望将转换应用于机器码到函数。
nopython参数指定我们是希望Numba使用纯机器码,还是在必要时填充一些Python代码。通常应该将这个值设置为true以获得最佳性能,除非您在这时发现Numba抛出了一个错误。
就是这样!只要在函数上面添加@jit(nopython=True), Numba就会处理剩下的事情!
在我的电脑上,整理所有这些数字平均需要0.1424秒——这是21倍的速度!
加速Numpy操作
Numba的另一个亮点是加快了对Numpy的操作。这次,我们将把3个相当大的数组加在一起,大约是一个典型图像的大小,然后使用numpy.square()函数对它们进行平方。
查看下面的代码,看看在带有Numpy的Python中如何工作。
注意,每当我们对Numpy数组进行基本数组计算(如加法、相乘和平方)时,代码都会自动由Numpy在内部向量化。这就是为什么在可能的情况下,用Numpy替换纯Python代码通常会提高性能。
上面的代码在我的PC上组合数组的平均运行时间为0.002288秒。
但是即使是Numpy代码也没有Numba优化后的机器代码快。下面的代码将执行与前面相同的数组操作。这一次,我们在函数的上方添加了vectorize装饰器,向numba发出信号,它应该对我们的函数执行机器码转换。
vectorize装饰器接受两个输入。
第一个指定要操作的numpy数组的输入类型。这必须指定,因为Numba使用它将代码转换为最优版本。通过事先了解输入类型,Numba将能够准确地计算出如何最有效地存储和操作数组。
第二个输入称为“目标”。它指定要如何运行你的功能:
- cpu:用于在单个cpu线程上运行
- 并行:用于在多核多线程CPU上运行
- cuda:在GPU上运行
几乎在所有情况下,并行选项都比cpu选项快得多。cuda选项主要用于具有许多并行操作的非常大的阵列,因为在这种情况下,我们可以充分利用GPU上有这么多核心的优势。
上面的代码在我的PC上组合数组的平均运行时间为0.001196秒——大约是2倍的加速。添加一行代码也不错!
它总是这么快吗?
当应用以下这些领域中,Numba将是最有效的:
- Python代码比C代码慢的地方(通常是循环)
- 将相同操作应用于某个区域的位置(即对多个元素执行相同操作)
在这些区域之外,Numba可能不会给您提供太快的速度。因为在这种情况下,转换到较低级别代码所带来的优势已经消失了。
总的来说,值得一试。在几个python函数上面添加一行代码值得一试的——将您的代码速度提高2到21X!