【问题标题】:Indexing numpy array as slow as python append索引numpy数组和python append一样慢
【发布时间】:2020-03-24 02:27:50
【问题描述】:

一直有人告诉我,python 的原生 append 是一个慢速函数,应该避免在 for 循环中使用。然而,经过几次小测试后,我发现在使用 for 循环对其进行迭代时,它的性能比 numpy 数组差:


第一次测试-数组/列表构造

python 原生列表追加

def pythonAppend(n):
      x = []
      for i in range(n):
          x.append(i)
      return x

%timeit pythonAppend(1000000)

Numpy 分配数组然后访问

 def  numpyConstruct(n):
     x = np.zeros(n)
     for i in range(n):
         x[i] = i
     return x

%timeit numpyConstruct(1000000)

结果:

Python 时间: 179 毫秒

Numpy 时间: 189 毫秒


第二次测试 - 访问元素

n = 1000000
x = pythonAppend(n)
arr = numpyConstruct(n)
order = arr[:]; np.random.shuffle(order); order = list(order.astype(int))

def listAccess(x):
     for i in range(len(x)):
             x[i] = i/3.
     return x

def listAccessOrder(x, order):
     for i in order:
             x[i] = i/3.
     return x

%timeit listAccess(x)
%timeit listAccess(arr)
%timeit listAccessOrder(x, order)
%timeit listAccessOrder(arr, order)
%timeit arr / 3.

结果

python 顺序: 175 毫秒

numpy -sequential: 198 毫秒

python -shuffled 访问: 2.08 秒

numpy -shuffled 访问: 2.15 秒

numpy 向量化访问: 1.92ms



这些结果对我来说非常令人惊讶,因为我认为至少由于 python 是链表访问元素会比 numpy 慢,因为必须遵循指针链。还是我误解了 python 列表的实现?另外,为什么 python 列表的性能略好于 numpy 等效项 - 我猜大部分效率低下来自使用 python for 循环,但 python 的 append 仍然在竞争 numpy 访问和分配。

【问题讨论】:

  • 我猜当你在循环中松开 numpy 时,你会失去所有的性能优势。但是如果你可以把它写成一个向量函数,它会比用干净的python循环更快。
  • 我认为 numpy 数组是连续的?因此,访问它们的元素(尤其是按随机顺序)会更快,因为 CPU 可以立即获取内存块,而不必遵循指向元素的一系列指针?
  • python 列表不是链表。

标签: python arrays performance numpy


【解决方案1】:

Python 列表不是链表。列表对象的数据缓冲区包含指向对象的链接/指针(在内存中的其他位置)。所以获取ith 元素很容易。缓冲区也有增长空间,所以append 是插入新元素链接的简单问题。缓冲区必须定期重新分配,但 Python 可以无缝管理。

一个 numpy 数组也有一个 1d 数据缓冲区,但它包含数值(更普遍的是 dtype 要求的字节)。获取很容易,但它必须创建一个新的 Python 对象来“包含”该值(“拆箱”)。赋值还需要从 python 对象转换为将要存储的字节。

一般来说,我们发现通过附加到列表(最后调用一个 np.array)来创建新数组与分配给预分配数组相比具有竞争力。

遍历 numpy 数组通常比遍历列表慢。

我们强烈反对使用np.append(或一些变体)迭代地增长数组。每次都会创建一个新数组,并带有完整副本。

在编译代码中完成迭代时,numpy 数组很快。通常这是使用整个数组方法。 c 代码可以遍历数据缓冲区,直接对值进行操作,而不是在每一步都调用 python 对象方法。

【讨论】:

    猜你喜欢
    • 2018-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-09
    • 2015-02-23
    • 2019-06-02
    • 2018-10-25
    • 2018-05-23
    相关资源
    最近更新 更多