简体   繁体   English

list(numpy.array)变慢的原因是什么?

[英]What is the reason for slowness of list(numpy.array)?

It is well known, that if a is a numpy array, then a.tolist() is faster than list(a) , for example: 众所周知,如果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

That means, the naive list(a) version is about factor 3 slower than the special-function tolist() . 这意味着,朴素的list(a)版本比特殊功能tolist()慢约3 tolist()

However, comparing it to the the performance of the build-in array -module: 但是,将其与内置array -module的性能进行比较:

>>> import array
>>> big_arr=array.array('i', big_np)
>>> %timeit list(big_arr)
1 loop, best of 3: 312 ms per loop

we can see, that one should probably say, that list(a) is slow rather than tolist() is fast, because array.array is as fast as the special function. 我们可以看到,也许应该说list(a)慢而不是tolist()快,因为array.array和特殊函数一样快。

Another observation: array.array -module and tolist benefit from the small-integer-pool (ie when values are in range [-5, 256] ), but this is not the case for list(a) : 另一个观察结果: 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

As we can see the faster versions are about 2 times faster, but the slow version is as slow as before. 我们可以看到,较快的版本比以前快2倍左右,但较慢的版本和以前一样慢。

My question: what slows list(numpy.array) down compared to list(array.array) ? 我的问题:与list(numpy.array)相比, list(array.array)速度降低了多少?

Edit: 编辑:

One more observation, for Python2.7, it takes longer if the integers are bigger (ie cannot be hold by int32 ): 再观察一下,对于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

but still faster, than the slow list-version. 但仍比缓慢的列表版本更快。

Here is a partial answer explaining your observation re the small integer pool: 这是部分答案,解释您对小型整数池的观察:

>>> a = np.arange(10)
>>> type(list(a)[0])
<class 'numpy.int64'>
>>> type(a.tolist()[0])
<class 'int'>

As we can see tolist seems to try and create elements of native python type whereas the array iterator (which is what is used by the list constructor) doesn't bother. 正如我们所看到的, tolist似乎尝试创建本机python类型的元素,而数组迭代器(列表构造函数使用的数组)不会打扰。

Indeed, the C implementation of tolist (source here ) uses PyArray_GETITEM which is the equivalent to Python arr[index].item() , not - as one might assume - arr[index] 确实, tolist的C实现( 在此处提供源代码)使用PyArray_GETITEM ,它等效于Python arr[index].item() ,而不是-可能有人假设arr[index].item() arr[index]

Basically, the answer of Paul Panzer explains what happens: in the slow list(...) version the resulting elements of the list are not python-integers, but are numpy-scalars, eg numpy.int64 . 基本上,Paul Panzer的答案解释了会发生的情况:在慢list(...)版本中,列表的结果元素不是python-integers,而是numpy- numpy.int64 ,例如numpy.int64 This answer just elaborates a little bit and connects the dots. 这个答案只说明一点点,并连接点。

I didn't make a systematic profiling, but when stopped in the debugger, every time both versions were in the routines which created the integer-object, so it is very probably this is where the lion's share of the execution time is spent, and the overhead doesn't play a big role. 我没有进行系统的分析,但是当在调试器中停止运行时,每次两个版本都在创建整数对象的例程中,因此很可能这是执行时间的主要部分,并且开销并不重要。

The list(..) -version, iterator calls array_item , which has an special treatment for one-dimensional arrays and calls PyArray_Scalar , which is a quite generic function and doesn't use the machinery of the Pythons-integer-creation. list(..) version迭代器调用array_item ,它对一维数组有特殊处理,并调用PyArray_Scalar ,这是一个非常通用的函数,并且不使用Pythons-integer-creation的机制。 It happens to be slower than the Python-version, there is also no integer-pool for small values. 它恰好比Python版本慢,对于小值也没有整数池。

The .tolist() version calls recursive_tolist , which eventually uses Python's PyLong_FromLong(long) , which shows all the observed behaviors and happens to be faster than numpy-functionality (probably, because it is not the normal way of using numpy, not many optimizations were done). .tolist()版本调用recursive_tolist ,该版本最终使用Python的PyLong_FromLong(long) ,它显示了所有观察到的行为,并且碰巧比numpy功能更快(可能是因为这不是使用numpy的正常方式,没有很多优化措施)完成)。

There is a small difference for Python2 compared to Python3: Python2 has two different classes of integers: the one, more efficient, for numbers up to 32bit and the one for arbitrary big numbers - thus for the bigger numbers the most general (and thus more costly) path must be taken - this also can be observed. 与Python3相比,Python2的差别很小:Python2具有两种不同的整数类别:一类效率更高,适用于32位以下的数字,另一类适用于任意大数-因此,对于较大的数字,最通用的(因此更多昂贵)的路径必须走-这也是可以观察到的。

Constructing a list with list(something) iterates over something and collects the result of the iteration into a new list. 使用list(something)构造一个列表会迭代某些内容,并将迭代结果收集到一个新列表中。

If list(small_np) is slower than list(small_arr) one could assume that iterating over small_np is slower than iterating over small_arr . 如果list(small_np)list(small_arr)慢,则可以假定对small_np迭代要比对small_arr迭代慢。 Let's verify that: 让我们验证一下:

%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

Yep, iterating over a numpy array seems to be slower. 是的,迭代一个numpy数组似乎比较慢。 This is where I must start speculating. 这是我必须开始推测的地方。 Numpy arrays are flexible. numpy数组很灵活。 They can have an arbitrary number of dimensions with various strides. 它们可以具有任意数量的尺寸,并具有不同的跨度。 Array arrays are always flat. 数组数组总是平坦的。 This flexibility probably likely comes at a cost, which manifests in more complex iteration. 这种灵活性可能需要付出一定的代价,这体现在更复杂的迭代中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM