NumPy数组、Python列表和Fortran之间的排序性能比较

5

我长期使用Fortran进行计算物理相关工作,最近开始学习并尝试使用Python。我知道作为一种解释性语言,对于主要依赖CPU的计算工作,Python通常比Fortran慢。但我认为使用numpy会显著提高简单任务如排序的性能。

所以我的测试案例是使用冒泡排序(只是一个包含许多数组操作的测试案例,因此不需要评论算法本身的性能)来对包含10000个随机浮点数的数组/列表进行排序。我的计时结果如下(所有函数使用相同的算法):

Python3(使用numpy数组,但使用我的自己的函数而不是numpy.sort):33.115秒
Python3(使用列表):9.927秒
Fortran(gfortran):0.291秒

Python3(使用numpy.sort):0.269秒(不公平的比较,因为它使用不同的算法)

我惊讶地发现,与Python列表相比,使用numpy数组的操作速度要慢约3倍,与Fortran相比慢了约100倍。所以在这一点上,我的问题是:

  1. 为什么在这个测试案例中,使用numpy数组操作明显比Python列表更慢?
  2. 如果我需要的算法还没有在scipy/numpy中实现,并且我需要在Python框架内编写自己的函数以获得最佳性能,我应该使用哪种数据类型操作:numpy数组或列表?
  3. 如果我的应用程序是面向性能的,并且我想编写与内置numpy函数(如np.sort)等效的函数,我应该学习/使用哪些工具/框架?

你用纯Python写过数组上的冒泡排序吗? - Nils Werner
纯Python在循环和数组索引方面非常慢。这就是为什么他们发明了NumPy及其技巧。但前提是你必须留在NumPy生态系统中。 - JohanC
@NilsWerner:是的。我从其他地方复制了一个简单的冒泡排序代码,这里是代码链接:https://www.pastiebin.com/5dd24e0e1cd95 - Suman Chakrabarty
@JohanC:嗯,我正在寻找一种Pythonic的解决方案,用于那些没有现有的numpy/scipy例程,而我必须编写自己的代码,并具有类似于numpy函数的性能。那么哪种方法最有效?我会尝试像你建议的那样使用numba。我也在寻找其他建议。 - Suman Chakrabarty
@JohanC 是的,我同意平均多个时间的建议。我已经手动测试了几次,结果相似。但也许我会在不同实现之间比较一个固定的数据集,并在这里更新。 - Suman Chakrabarty
显示剩余2条评论
2个回答

8

你好像误解了NumPy加速计算的方式。

在NumPy中获得加速不是因为它使用了某种聪明的数据保存方式。或者自动将你的Python代码编译成C。

相反,NumPy使用C或Fortran实现了许多有用的算法,其中包括numpy.sort()函数。这些函数将np.ndarray作为输入,并在C/Fortran循环中对数据进行遍历。

如果你想编写快速的NumPy代码,有三种方法可以实现:

  • 将你的代码分解为NumPy操作(乘法、点积、排序、广播等)
  • 用C/Fortran编写你想要实现的算法,并编写绑定到Python的接受np.ndarrays的代码(在内部,它们是你选择的类型的连续数组)。
  • 使用Numba通过让Python即时将你的代码编译为机器代码来加速你的函数(具有一些限制)

谢谢。你能否对我的第一个问题发表评论:“为什么在这个测试案例中,使用numpy数组的操作明显比使用Python列表慢?” - Suman Chakrabarty
我不确定,你是否尝试使用分析器来查看具体是什么在拖慢你的速度? - Nils Werner
@SumanChakrabarty 我猜你可能重新创建了数组或类似的东西?如果不知道你的代码,就无法确定。 - Mayou36

5
  1. 为什么在这种测试用例下,使用NumPy数组的操作明显比Python列表慢?

NumPy数组是存储数值数据的容器。它们包含元数据(数组的类型和形状)以及数据本身的内存块。因此,对NumPy数组元素的任何操作都涉及一些开销。Python列表更适用于“纯Python”代码:读取或写入列表元素比NumPy数组快。 NumPy数组的优点来自于“整个数组操作”(即所谓的数组操作)和编译扩展。 C/C++/Fortran,Cython或Numba可以无需开销地访问NumPy数组的内容。

  1. 如果我需要实现一个在scipy/numpy中没有现成实现的算法,并且我需要在Python框架内编写最佳性能的函数,我应该使用哪种数据类型进行操作:NumPy数组还是列表?

对于数值代码,NumPy数组更好:您可以在编译扩展中“按照C方式”或“按照Fortran方式”访问它们的内容。

  1. 如果我的应用程序以性能为导向,并且我想编写与内置numpy函数(例如np.sort)相当的性能的函数,我应该学习/使用哪些工具/框架?

有很多。您可以使用NumPy C-API以C语言编写(复杂,不建议使用,但了解其存在很重要)。Cython是一种成熟的“Python-like”和“C-like”语言,可实现易于逐步进行的性能改进。Numba是一种即时解释器:“只要将代码限制在对NumPy数组的直接数字操作上,Numba就会即时将代码转换为已编译等效代码。”


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接