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

查看:169
本文介绍了为什么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?

推荐答案

存储已取消装箱",但是每次访问元素时,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整数的向量;读取iC 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天全站免登陆