deque.popleft()和list.pop(0)。有性能差异吗?

时间:2015-09-12 20:54:01

标签: python performance list deque cpython

deque.popleft()list.pop(0)似乎返回相同的结果。它们之间是否有任何性能差异?为什么?

3 个答案:

答案 0 :(得分:21)

deque.popleft()比list.pop(0)更快,因为deque已被优化为大约在O(1)中执行popleft(),而list.pop(0)需要O(n)(参见deque objects)。

_collectionsmodule.c中的deque和listobject.c for list的注释和代码提供了解释性能差异的实现见解。即deque对象“由双向链表组成”,它有效地优化了两端的追加和弹出,而列表对象甚至不是单链表,而是C数组(指向元素的指针(见{{3}) }和Python 2.7 listobject.h#l22),这使得它们适用于元素的快速随机访问,但在删除第一个元素后需要O(n)时间重新定位所有元素。

对于Python 2.7和3.5,这些源代码文件的URL是:

  1. Python 3.5 listobject.h#l23

  2. https://hg.python.org/cpython/file/2.7/Modules/_collectionsmodule.c

  3. https://hg.python.org/cpython/file/2.7/Objects/listobject.c

  4. https://hg.python.org/cpython/file/3.5/Modules/_collectionsmodule.c

  5. 使用%timeit时,deque.popleft()和list.pop(0)之间的性能差异大约为4,因为双端队列和列表具有相同的52个元素并且当增长到1000倍时他们的长度是10 ** 8。测试结果如下。

    import string
    from collections import deque
    
    %timeit d = deque(string.letters); d.popleft()
    1000000 loops, best of 3: 1.46 µs per loop
    
    %timeit d = deque(string.letters)
    1000000 loops, best of 3: 1.4 µs per loop
    
    %timeit l = list(string.letters); l.pop(0)
    1000000 loops, best of 3: 1.47 µs per loop
    
    %timeit l = list(string.letters);
    1000000 loops, best of 3: 1.22 µs per loop
    
    d = deque(range(100000000))
    
    %timeit d.popleft()
    10000000 loops, best of 3: 90.5 ns per loop
    
    l = range(100000000)
    
    %timeit l.pop(0)
    10 loops, best of 3: 93.4 ms per loop
    

答案 1 :(得分:9)

  

是否有性能差异?

是。 deque.popleft()O(1) - 一个恒定的时间操作。虽然list.pop(0)O(n) - 线性时间操作:列表越大则需要的时间越长。

  

为什么?

CPython列表实现是基于数组的。 pop(0)从列表中删除第一项,并且需要转移左len(lst) - 1项以填补空白。

deque()实现使用双向链表。无论deque多大,deque.popleft()都需要一个恒定的(有限的以上)操作次数。

答案 2 :(得分:5)

是的,如果你有一个很长的列表或双端队列,这是相当可观的。列表中的所有元素都连续放置在内存中,因此如果删除任何元素,则所有后续元素必须向左移动一个位置 - 因此,在列表开头删除或插入元素所需的时间与列表的长度。另一方面,deque是专门构造的,允许在 端快速插入或移除(通常通过在双端队列的开始处允许“空”存储器位置,或者环绕以使得deque占用的内存段的末尾可以包含实际上被认为是在deque开头的元素。)

比较这两个片段的效果:

spans
相关问题