[英]What is the reason for slowness of list(numpy.array)?
众所周知,如果a
是一个numpy数组,则a.tolist()
比list(a)
更快,例如:
>>> import numpy as np
>>> big_np=np.random.randint(1,10**7,(10**7,))
>>> %timeit list(big_np)
1 loop, best of 3: 869 ms per loop
>>> %timeit big_np.tolist()
1 loop, best of 3: 306 ms per loop
这意味着,朴素的list(a)
版本比特殊功能tolist()
慢约3
tolist()
。
但是,将其与内置array
-module的性能进行比较:
>>> import array
>>> big_arr=array.array('i', big_np)
>>> %timeit list(big_arr)
1 loop, best of 3: 312 ms per loop
我们可以看到,也许应该说list(a)
慢而不是tolist()
快,因为array.array
和特殊函数一样快。
另一个观察结果: array.array
-module和tolist
受益于small-integer-pool (即,当值在[-5, 256]
tolist
[-5, 256]
范围内时),但是list(a)
并非如此:
##only small integers:
>>> small_np=np.random.randint(1,250, (10**7,))
>>> small_arr=array.array('i', small_np)
>>> %timeit list(small_np)
1 loop, best of 3: 873 ms per loop
>>> %timeit small_np.tolist()
10 loops, best of 3: 188 ms per loop
>>> %timeit list(small_arr)
10 loops, best of 3: 150 ms per loop
我们可以看到,较快的版本比以前快2倍左右,但较慢的版本和以前一样慢。
我的问题:与list(numpy.array)
相比, list(array.array)
速度降低了多少?
编辑:
再观察一下,对于Python2.7,如果整数更大(即不能由int32
保持),则需要更长的时间:
>>> very_big=np.random.randint(1,10**7,(10**7,))+10**17
>>> not_so_big=np.random.randint(1,10**7,(10**7,))+10**9
>>> %timeit very_big.tolist()
1 loop, best of 3: 627 ms per loop
>>> %timeit not_so_big.tolist()
1 loop, best of 3: 302 ms per loop
但仍比缓慢的列表版本更快。
这是部分答案,解释您对小型整数池的观察:
>>> a = np.arange(10)
>>> type(list(a)[0])
<class 'numpy.int64'>
>>> type(a.tolist()[0])
<class 'int'>
正如我们所看到的, tolist
似乎尝试创建本机python类型的元素,而数组迭代器(列表构造函数使用的数组)不会打扰。
确实, tolist
的C实现( 在此处提供源代码)使用PyArray_GETITEM
,它等效于Python arr[index].item()
,而不是-可能有人假设arr[index].item()
arr[index]
基本上,Paul Panzer的答案解释了会发生的情况:在慢list(...)
版本中,列表的结果元素不是python-integers,而是numpy- numpy.int64
,例如numpy.int64
。 这个答案只说明一点点,并连接点。
我没有进行系统的分析,但是当在调试器中停止运行时,每次两个版本都在创建整数对象的例程中,因此很可能这是执行时间的主要部分,并且开销并不重要。
list(..)
version迭代器调用array_item
,它对一维数组有特殊处理,并调用PyArray_Scalar
,这是一个非常通用的函数,并且不使用Pythons-integer-creation的机制。 它恰好比Python版本慢,对于小值也没有整数池。
.tolist()
版本调用recursive_tolist
,该版本最终使用Python的PyLong_FromLong(long)
,它显示了所有观察到的行为,并且碰巧比numpy功能更快(可能是因为这不是使用numpy的正常方式,没有很多优化措施)完成)。
与Python3相比,Python2的差别很小:Python2具有两种不同的整数类别:一类效率更高,适用于32位以下的数字,另一类适用于任意大数-因此,对于较大的数字,最通用的(因此更多昂贵)的路径必须走-这也是可以观察到的。
使用list(something)
构造一个列表会迭代某些内容,并将迭代结果收集到一个新列表中。
如果list(small_np)
比list(small_arr)
慢,则可以假定对small_np
迭代要比对small_arr
迭代慢。 让我们验证一下:
%timeit for i in small_np: pass # 1 loop, best of 3: 916 ms per loop
%timeit for i in small_arr: pass # 1 loop, best of 3: 261 ms per loop
是的,迭代一个numpy数组似乎比较慢。 这是我必须开始推测的地方。 numpy数组很灵活。 它们可以具有任意数量的尺寸,并具有不同的跨度。 数组数组总是平坦的。 这种灵活性可能需要付出一定的代价,这体现在更复杂的迭代中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.