Python Numpy单个元素访问速度比列表慢
我刚开始使用Numpy,并注意到在Numpy数组中迭代每个元素的速度比使用列表进行相同操作要慢4倍左右。我现在知道这违背了Numpy的目的,如果可能的话,我应该将函数矢量化。我的问题是为什么它慢了4倍。这似乎是一个相当大的数目 我使用Python Numpy单个元素访问速度比列表慢,python,arrays,list,numpy,Python,Arrays,List,Numpy,我刚开始使用Numpy,并注意到在Numpy数组中迭代每个元素的速度比使用列表进行相同操作要慢4倍左右。我现在知道这违背了Numpy的目的,如果可能的话,我应该将函数矢量化。我的问题是为什么它慢了4倍。这似乎是一个相当大的数目 我使用%timeit import numpy as np b = np.eye(1000) a = b.tolist() %timeit b[100][100] #1000000 loops, best of 3: 692 ns per loop %timeit a[
%timeit
import numpy as np
b = np.eye(1000)
a = b.tolist()
%timeit b[100][100] #1000000 loops, best of 3: 692 ns per loop
%timeit a[100][100] #10000000 loops, best of 3: 70.7 ns per loop
%timeit b[100,100] #1000000 loops, best of 3: 343 ns per loop
%timeit b.item(100,100) #1000000 loops, best of 3: 297 ns per loop
我试图使用dis.dis
查看引擎盖下发生了什么,但得到了:
TypeError: don't know how to disassemble method-wrapper objects
然后我试图查看Numpy源代码,但无法找出哪个文件对应于数组元素访问。我很好奇是什么导致了额外的开销,更重要的是,我自己将来如何解决这个问题。看起来python不能很容易地编译成C代码,所以我可以看到不同之处。但是,有没有一种方法可以查看为每行生成的字节码,以了解差异?当numpy从数组中的一个位置返回项时,它必须将内部C类型(float、double等)值转换为Python类型的标量值(int、long、float)。然后返回对Python类型值的引用。这种转换需要一些时间
有趣的是,同样的低效率在另一方面也会损害绩效。我有一个Python列表,我正在使用来自numpy数组的索引值对其进行索引。在创建Python整型值时也会发生同样的转换,该整型值是索引到Python列表中所需的。我不得不用一个本地Python整数的中间数组重写我的算法。总之:从NumPy数组中获取项需要创建新的Python对象,而列表则不是这样。此外,NumPy数组的索引比列表稍微复杂一些,这可能会增加一些额外的开销
总而言之,您列出的NumPy操作执行以下操作:
b[100][100]
将b
的第100行作为数组返回,然后获取该行索引100处的值,并将该值作为对象返回(例如anp.int64
type)b[100100]
直接返回第100行和第100列的值(不首先返回中间数组)b.item(100100)
执行与上述b[100100]
相同的操作,只是将值转换为本机Python类型并返回b[100100]
快
对象创建
Python列表是指向内存中对象的指针数组。例如,列表[1,2,3]
不直接包含那些整数,而是指向内存地址的指针,因为每个整数对象都已经存在。为了从列表中获取项目,Python只返回对该对象的引用
NumPy数组不是对象的集合。数组np.array([1,2,3])
只是一个连续的内存块,其位被设置为表示那些整数值。要从该数组中获取整数,必须在与该数组分离的内存中构造一个新的Python对象。例如,索引操作可能返回np.int64
的对象:该对象以前不存在,必须创建
索引复杂性
a[100][100]
(从列表中获取)比b[100100]
(从数组中获取)更快的另外两个原因是:
- 当索引列表和数组时,字节码操作码
会被执行,但是对于Python列表,字节码操作码是经过优化的BINARY_SUBSCR
- 处理Python列表整数索引的内部C函数非常简短。另一方面,NumPy索引要复杂得多,需要执行大量代码来确定所使用的索引类型,以便返回正确的值
a[100][100]
和b[100100]
访问列表和数组中元素的步骤
字节码
列表和数组都会触发相同的四个字节码操作码:
0 LOAD_NAME 0 (a) # the list or array
3 LOAD_CONST 0 (100) # index number (tuple for b[100,100])
6 BINARY_SUBSCR # find correct "getitem" function
7 RETURN_VALUE # value returned from list or array
注意:如果开始多维列表的链索引,例如a[100][100][100]
,则开始重复这些字节码指令。使用元组索引的NumPy数组不会出现这种情况:b[100100100]
仅使用四条指令。这就是为什么随着维度数量的增加,计时中的差距开始缩小的原因
查找正确的“getitem”函数
访问列表和数组的函数是不同的,在每种情况下都需要找到正确的函数。此任务由操作码处理:
w=POP();//我们的索引
v=TOP();//我们的列表或NumPy数组
如果(PyList_CheckExact(v)和&PyInt_CheckExact(w)){//我们有列表和int吗?
/*内联:列表[int]*/
Py_ssize_t i=PyInt_AsSsize_t(w);
if(i<0)
i+=PyList\u GET\u尺寸(v);
如果(i>=0&&i
此代码针对Python列表进行了优化。我