为什么 Python 的数组很慢? [英] Why are Python's arrays slow?

查看:58
本文介绍了为什么 Python 的数组很慢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望 array.array 比列表更快,因为数组似乎没有装箱.

I expected array.array to be faster than lists, as arrays seem to be unboxed.

但是,我得到以下结果:

However, I get the following result:

In [1]: import array

In [2]: L = list(range(100000000))

In [3]: A = array.array('l', range(100000000))

In [4]: %timeit sum(L)
1 loop, best of 3: 667 ms per loop

In [5]: %timeit sum(A)
1 loop, best of 3: 1.41 s per loop

In [6]: %timeit sum(L)
1 loop, best of 3: 627 ms per loop

In [7]: %timeit sum(A)
1 loop, best of 3: 1.39 s per loop

造成这种差异的原因是什么?

What could be the cause of such a difference?

推荐答案

storage 是未装箱的",但是每次访问元素时,Python 都必须对其进行装箱"(将其嵌入到常规 Python 对象)以便对其进行任何操作.例如,您的 sum(A) 遍历数组,并在常规 Python int 对象中一次装箱每个整数.这需要时间.在您的 sum(L) 中,所有装箱都在创建列表时完成.

The storage is "unboxed", but every time you access an element Python has to "box" it (embed it in a regular Python object) in order to do anything with it. For example, your sum(A) iterates over the array, and boxes each integer, one at a time, in a regular Python int object. That costs time. In your sum(L), all the boxing was done at the time the list was created.

因此,最终,数组通常会更慢,但需要的内存要少得多.

So, in the end, an array is generally slower, but requires substantially less memory.

以下是 Python 3 最新版本的相关代码,但自 Python 首次发布以来,相同的基本思想适用于所有 CPython 实现.

Here's the relevant code from a recent version of Python 3, but the same basic ideas apply to all CPython implementations since Python was first released.

这是访问列表项的代码:

Here's the code to access a list item:

PyObject *
PyList_GetItem(PyObject *op, Py_ssize_t i)
{
    /* error checking omitted */
    return ((PyListObject *)op) -> ob_item[i];
}

它的内容很少:somelist[i] 只返回列表中的第 i 个对象(并且 CPython 中的所有 Python 对象都是指向一个结构的指针,该结构的初始段符合 struct PyObject 的布局.

There's very little to it: somelist[i] just returns the i'th object in the list (and all Python objects in CPython are pointers to a struct whose initial segment conforms to the layout of a struct PyObject).

这里是类型代码为 larray__getitem__ 实现:

And here's the __getitem__ implementation for an array with type code l:

static PyObject *
l_getitem(arrayobject *ap, Py_ssize_t i)
{
    return PyLong_FromLong(((long *)ap->ob_item)[i]);
}

原始内存被视为平台原生C long 整数的向量;i'th C long 被读取;然后调用 PyLong_FromLong() 将本机 C long 包装(装箱")在 Python long 对象(在 Python 3 中),它消除了 Python 2 在 intlong 之间的区别,实际上显示为类型 int).

The raw memory is treated as a vector of platform-native C long integers; the i'th C long is read up; and then PyLong_FromLong() is called to wrap ("box") the native C long in a Python long object (which, in Python 3, which eliminates Python 2's distinction between int and long, is actually shown as type int).

这个装箱必须为 Python int 对象分配新的内存,并将本机 C long 的位喷射到其中.在原始示例的上下文中,此对象的生命周期非常短(仅足够 sum() 将内容添加到运行总数中),然后需要更多时间来释放新的 int 对象.

This boxing has to allocate new memory for a Python int object, and spray the native C long's bits into it. In the context of the original example, this object's lifetime is very brief (just long enough for sum() to add the contents into a running total), and then more time is required to deallocate the new int object.

这是速度差异的来源,在 CPython 实现中一直来自,并且将永远来自.

This is where the speed difference comes from, always has come from, and always will come from in the CPython implementation.

这篇关于为什么 Python 的数组很慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆