开发者社区 问答 正文

为什么Python的数组变慢?

我期望array.array比列表更快,因为数组似乎已拆箱。

但是,我得到以下结果:

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 造成这种差异的原因是什么?

展开
收起
保持可爱mmm 2020-02-06 23:24:32 483 分享 版权
1 条回答
写回答
取消 提交回答
  • 该存储是“未装箱的”,但是每次访问元素时,Python都必须对其进行“装箱”(将其嵌入常规Python对象中),以便对其进行任何处理。例如,您sum(A)遍历数组,并将每个整数一次装在一个常规Python int对象中。那要花时间。在您的中sum(L),所有装箱操作均在创建列表时完成。

    因此,最后,阵列通常较慢,但所需的内存却少得多。

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

    这是访问列表项的代码:

    PyObject * PyList_GetItem(PyObject op, Py_ssize_t i) { / error checking omitted */ return ((PyListObject *)op) -> ob_item[i]; } 几乎没有什么: somelist[i]只需返回i列表中的第一个对象(CPython中的所有Python对象都是指向结构的指针,该结构的初始段符合a的布局struct PyObject)。

    这__getitem__是array带有类型代码的实现l:

    static PyObject * l_getitem(arrayobject *ap, Py_ssize_t i) { return PyLong_FromLong(((long *)ap->ob_item)[i]); } 原始内存被视为平台本地C long整数的矢量;该i“个C long被读取起来; 然后PyLong_FromLong()调用该方法将本机包装(“框”)C long在Python long对象中(在Python 3中,该对象消除了Python 2 int和之间的区别long,实际上显示为type int)。

    此装箱必须为Python int对象分配新的内存,然后将native C long的位喷射到其中。在原始示例的上下文中,此对象的生存期非常短(足够长,足以sum()将内容添加到运行的总计中),然后需要更多时间才能取消分配新int对象。

    在CPython实现中,这就是速度差异的来源,永远都是这样,永远都是这样。

    问题来源于stack overflow

    2020-02-06 23:25:02
    赞同 展开评论
问答分类:
问答标签:
问答地址: