【发布时间】:2023-04-08 23:50:02
【问题描述】:
几年前,有人posted 在Active State Recipes 上进行比较,三个python/NumPy 函数;它们中的每一个都接受相同的参数并返回相同的结果,即距离矩阵。
其中两个取自已发布的资源;它们都是——或者在我看来它们都是——惯用的 numpy 代码。创建距离矩阵所需的重复计算由 numpy 优雅的索引语法驱动。这是其中之一:
from numpy.matlib import repmat, repeat
def calcDistanceMatrixFastEuclidean(points):
numPoints = len(points)
distMat = sqrt(sum((repmat(points, numPoints, 1) -
repeat(points, numPoints, axis=0))**2, axis=1))
return distMat.reshape((numPoints,numPoints))
第三个使用单个循环创建了距离矩阵(考虑到只有 1,000 个 2D 点的距离矩阵有 100 万个条目,这显然是很多循环)。乍一看,这个函数在我看来就像我在学习 NumPy 时编写的代码,我会先编写 Python 代码然后逐行翻译来编写 NumPy 代码。
在 Active State 发布几个月后,比较这三者的性能测试结果在 NumPy 邮件列表上的 thread 中发布和讨论。
带有循环的函数实际上明显优于其他两个:
from numpy import mat, zeros, newaxis
def calcDistanceMatrixFastEuclidean2(nDimPoints):
nDimPoints = array(nDimPoints)
n,m = nDimPoints.shape
delta = zeros((n,n),'d')
for d in xrange(m):
data = nDimPoints[:,d]
delta += (data - data[:,newaxis])**2
return sqrt(delta)
线程中的一位参与者 (Keir Mierle) 提出了这可能是真的原因:
我怀疑这会更快的原因是 它具有更好的局部性,完全完成了一个计算 在进入下一个之前的相对较小的工作集。一个衬垫 必须反复将可能很大的 MxN 数组拉入处理器。
根据发帖者本人的说法,他的言论只是怀疑,似乎没有进一步讨论。
关于如何解释这些结果的任何其他想法?
特别是,是否有一个有用的规则——关于何时循环和何时索引——可以从这个例子中提取出来作为编写 numpy 代码的指导?
对于那些不熟悉 NumPy 或者没有看过代码的人来说,这种比较并不是基于边缘情况——如果是这样的话,我肯定不会那么感兴趣。相反,这种比较涉及一个在矩阵计算中执行常见任务的函数(即,在给定两个前项的情况下创建一个结果数组);此外,每个函数又由最常见的 numpy 内置函数组成。
【问题讨论】:
标签: python performance memory-management numpy