为什么numpy list访问速度比vanilla python慢​​?

时间:2016-01-26 17:57:16

标签: python performance numpy

我的印象是numpy对于列表操作会更快,但以下示例似乎表明不是这样:

import numpy as np
import time

def ver1():
    a = [i for i in range(40)]
    b = [0 for i in range(40)]
    for i in range(1000000):
        for j in range(40):
            b[j]=a[j]

def ver2():
    a = np.array([i for i in range(40)])
    b = np.array([0 for i in range(40)])
    for i in range(1000000):
        for j in range(40):
            b[j]=a[j]

t0 = time.time()
ver1()
t1 = time.time()
ver2()
t2 = time.time()

print(t1-t0)
print(t2-t1)

输出是:

4.872278928756714
9.120521068572998

(我在Windows 7中运行64位Python 3.4.3,在i7 920上运行)

我知道这不是复制列表的最快方法,但我试图找出我是否错误地使用了numpy。或者说numpy对于这种操作来说速度较慢,而且在更复杂的操作中效率更高?

编辑:

我也试过以下,只是通过b [:] = a直接复制,而numpy仍然是两倍慢:

import numpy as np
import time

def ver6():
    a = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    b = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    for i in range(1000000):
        b[:] = a

def ver7():
    a = np.array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
    b = np.array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
    for i in range(1000000):
        b[:] = a

t0 = time.time()
ver6()
t1 = time.time()
ver7()
t2 = time.time()

print(t1-t0)
print(t2-t1)

输出是:

0.36202096939086914
0.6750380992889404

2 个答案:

答案 0 :(得分:6)

你使用的NumPy错了。 NumPy的效率依赖于在C级循环中尽可能多地完成工作而不是解释代码。当你这样做

for j in range(40):
    b[j]=a[j]

这是一个解释循环,具有所有内在解释器开销和更多,因为NumPy的索引逻辑比列表索引更复杂,并且NumPy需要在每个元素检索上创建一个新的元素包装器对象。当您编写这样的代码时,您无法获得NumPy的任何好处。

您需要以在C:

中完成工作的方式编写代码
b[:] = a

这也可以提高列表操作的效率,但对于NumPy来说更为重要。

答案 1 :(得分:1)

您所看到的大部分内容都是使用C本机类型创建Python对象。

Python列表就是一个PyObject指针数组。当ab都是Python列表时,执行b[i] = a[i]意味着:

  • 减少b[i]
  • 指向的对象的引用计数
  • 增加a[i]
  • 指向的对象的引用计数
  • a[i]中存储的地址复制到b[i]

但如果ab是NumPy数组,那么事情会更精确,然后需要b[i] = a[i]

  • a[i]存储的本机C整数类型创建Python整数对象,请参阅this
  • 将Python整数对象转换为本机C整数类型,并将其值存储在b[i]中,请参阅here
  • 减少临时Python整数对象的引用计数。

因此,差异主要在于创建和处理该中间Python对象,列表不需要这样做。