【问题标题】:Numpy: Single loop vectorized code slow compared to two loop iterationNumpy:与两个循环迭代相比,单循环矢量化代码慢
【发布时间】:2017-01-22 23:30:21
【问题描述】:

以下代码遍历两个数组的每个元素以计算成对欧几里得距离。

def compute_distances_two_loops(X, Y):
    num_test = X.shape[0]
    num_train = Y.shape[0]
    dists = np.zeros((num_test, num_train))
    for i in range(num_test):
        for j in range(num_train):
            dists[i][j] = np.sqrt(np.sum((X[i] - Y[j])**2))
    return dists

以下代码具有相同的目的,但只有一个循环。

def compute_distances_one_loop(X, Y):
    num_test = X.shape[0]
    num_train = Y.shape[0]
    dists = np.zeros((num_test, num_train))
    for i in range(num_test):
        dists[i, :] = np.sqrt(np.sum((Y - X[i])**2, axis=1))
    return dists

以下是两者的时间比较。

two_loop_time = time_function(compute_distances_two_loops, X, Y)
print ('Two loop version took %f seconds' % two_loop_time)

>> Two loop version took 20.3 seconds

one_loop_time = time_function(compute_distances_one_loop, X, Y)
print ('One loop version took %f seconds' % one_loop_time)

>> One loop version took 80.9 seconds

X 和 Y 都是带有形状的 numpy.ndarray -

X: (500, 3000) Y: (5000, 3000)

出于直觉,结果不正确,单循环至少应该以相同的速度运行。我在这里想念什么?

PS:结果不是一次运行。代码我跑了几次,在不同的机器上,结果都差不多。

【问题讨论】:

  • 我可能在这里遗漏了一些东西,但是self 在你的one_loop 函数中来自哪里?就此而言,self.X_train
  • 这是一个错字,我更正了。
  • 只是为了确定,shapeXY 之间的不匹配是故意的还是错字?
  • 这是故意的。 X 是具有 Y 训练大小 10% 的测试集。
  • 当数组的第二个暗度为 300 时,one_loop 快 2 倍; 3000时,反之,速度减半。第一个是 50 和 500。我猜它遇到了内存管理问题。

标签: python performance numpy time nearest-neighbor


【解决方案1】:

原因是循环体内的数组大小。 在两个循环变体中,适用于两个 3000 个元素的数组。这很容易适应至少比主内存快得多的 cpu 的 2 级缓存,但它也足够大,计算距离比 python 循环迭代开销慢得多。

循环体的第二种情况适用于 5000 * 3000 个元素。这是如此之多,以至于数据需要在每个计算步骤中进入主存储器(首先将 Y-X[i] 减法到一个临时数组中,将临时数平方成另一个临时数,然后将其读回以求和)。 对于所涉及的简单操作,主内存比 cpu 慢得多,因此尽管删除了循环,它仍需要更长的时间。 您可以通过使用写入预分配临时数组的就地操作来加快速度,但对于这些数组大小,它仍然比两个循环变体慢。

注意scipy.spatial.distance.cdist(X, Y) 可能是最快的,因为它根本不需要任何临时对象

【讨论】:

  • 一秒钟都没有出现缓存可以发挥它的作用。谢谢 ! @jtaylor
  • 我认为答案有点误导。双循环版本不适用于两个数组 - 它在每次迭代时都适用于两个值,因此在 Numpy C 代码和 Python 解释器代码之间复制的数据非常少。但是,在单循环版本中,将构造并返回与Y 大小相同的数组。这需要花费大量时间 - 我同意 - 但如果我没记错的话,你读取元素数量的方式有点不对劲。
猜你喜欢
  • 2017-01-04
  • 1970-01-01
  • 2017-12-17
  • 1970-01-01
  • 2015-07-01
  • 2021-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多