Python/Numpy 数组操作的性能如何随着数组维度的增加而扩展?

计算科学 Python 表现 麻木的
2021-12-17 21:34:31

Python/Numpy 数组如何随着数组维度的增加而扩展?

这是基于我在为这个问题对 Python 代码进行基准测试时注意到的一些行为:如何使用 numpy 切片表达这个复杂的表达式

问题主要涉及索引以填充数组。我发现在 Python 循环中使用(不是很好)Cython 和 Numpy 版本的优势因所涉及数组的大小而异。Numpy 和 Cython 都在一定程度上体验到了越来越大的性能优势(大致在ñ=500对于 Cython 和ñ=2000对于我笔记本电脑上的 Numpy),之后它们的优势下降(Cython 功能仍然是最快的)。

这个硬件定义了吗?在处理大型阵列方面,对于性能得到提升的代码,应该遵循哪些最佳实践?

相对于矢量化和 Cython 实现的循环代码的执行时间图

这个问题(Why is not my Matrix-Vector Multiplication Scaling?)可能是相关的,但我有兴趣了解更多关于 Python 中处理数组的不同方式如何相对于彼此进行缩放。

1个回答

我不知道这个基准测试是如何完成的,但可能是浮点数,在 Python 中默认为双精度数。尺寸分别对应于416ķ. 这些是(有点旧的)AMD CPU 的 L1 和 L2 缓存大小的合理值。可以肯定的是,我做了自己的基准测试:

def timeit(size):
     t0 = time.time()
     for _ in xrange(10):
         np.random.random(size)
     return time.time() - t0

sizes = np.logspace(1, 6, 40)
times = [timeit(s) for s in sizes]

这个基准有一些问题,首先,我没有禁用垃圾收集,我正在计算总和,不是最好的时间,但请耐心等待。

它所花费的时间与数组的大小成正比,但是围绕大小的斜率会发生变化8000. 这是一个数组64ķ,即 i5(我的电脑)的 L1 缓存的大小。

是否应该担心缓存大小?作为一般规则,我说不。在 Python 中对其进行优化意味着使代码更加复杂,从而获得可疑的性能提升。不要忘记 Python 对象会增加一些难以跟踪和预测的开销。我只能想到两种情况,这是一个相关因素:

  • 大型数组上的基本操作(如计算多项式),受内存带宽限制。使用Numexpr或(如果数据更大)Pytables它们经过优化以在其他优化中考虑缓存大小。
  • 性能关键代码:如果你想压缩每一微秒,你不应该首先使用 Python。编写矢量化Cython并让编译器做它最擅长的事情可能是最轻松的方式。

在评论中,Evert 提到了 CArray。请注意,即使可以工作,开发也已停止,并且已作为独立项目被放弃。该功能将包含在Blaze中,这是一个正在进行的项目,旨在制作“新一代 Numpy”。