Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么切片分配比'list.insert'快?_Python_Performance_Optimization_Python Internals - Fatal编程技术网

Python 为什么切片分配比'list.insert'快?

Python 为什么切片分配比'list.insert'快?,python,performance,optimization,python-internals,Python,Performance,Optimization,Python Internals,受到 这里有一个基准: import timeit def test1(): a = [1,2,3] a.insert(0,1) def test2(): a = [1,2,3] a[0:0]=[1] print (timeit.timeit('test1()','from __main__ import test1')) print (timeit.timeit('test2()','from __main__ import test2')) 对我来说,

受到

这里有一个基准:

import timeit

def test1():
    a = [1,2,3]
    a.insert(0,1)

def test2():
    a = [1,2,3]
    a[0:0]=[1]

print (timeit.timeit('test1()','from __main__ import test1'))
print (timeit.timeit('test2()','from __main__ import test2'))
对我来说,
test2
稍微快一些(~10%)。为什么会这样?我预计会更慢,因为:

  • 切片分配必须能够接受任意长度的iterables,因此必须更加通用
  • 在切片分配中,我们需要在右侧创建一个新的列表,以使其正常工作
  • 有人能帮我理解吗


    (在OS-X 10.5.8上使用python 2.7)

    您的第一个测试用例必须调用列表
    a
    上的方法
    insert
    ,而
    test2
    中的所有操作都直接以字节码处理。注意下面的
    test1
    反汇编中的
    CALL\u函数。在Python中,调用函数的代价是适中的:当然,这一代价足以解释运行时的几个百分点差异

    >>> import dis
    >>> dis.dis(test1)
      2           0 LOAD_CONST               1 (1)
                  3 LOAD_CONST               2 (2)
                  6 LOAD_CONST               3 (3)
                  9 BUILD_LIST               3
                 12 STORE_FAST               0 (a)
    
      3          15 LOAD_FAST                0 (a)
                 18 LOAD_ATTR                0 (insert)
                 21 LOAD_CONST               4 (0)
                 24 LOAD_CONST               1 (1)
                 27 CALL_FUNCTION            2
                 30 POP_TOP             
                 31 LOAD_CONST               0 (None)
                 34 RETURN_VALUE        
    >>> dis.dis(test2)
      2           0 LOAD_CONST               1 (1)
                  3 LOAD_CONST               2 (2)
                  6 LOAD_CONST               3 (3)
                  9 BUILD_LIST               3
                 12 STORE_FAST               0 (a)
    
      3          15 LOAD_CONST               1 (1)
                 18 BUILD_LIST               1
                 21 LOAD_FAST                0 (a)
                 24 LOAD_CONST               4 (0)
                 27 LOAD_CONST               4 (0)
                 30 STORE_SLICE+3       
                 31 LOAD_CONST               0 (None)
                 34 RETURN_VALUE        
    
    错误的解释 我先发布了这篇文章,但经过考虑,我认为这是不正确的。我在这里描述的差异只会在有大量数据需要移动时产生显著的差异,而在这里的测试中不是这样。即使有很多数据,差异也只有几个百分点:

    import timeit
    
    def test1():
        a = range(10000000)
        a.insert(1,1)
    
    def test2():
        a = range(10000000)
        a[1:1]=[1]
    
    >>> timeit.timeit(test1, number=10)
    6.008707046508789
    >>> timeit.timeit(test2, number=10)
    5.861173868179321
    
    方法
    list.insert
    listobject.c
    中的函数实现。您将看到,它逐个复制列表尾部的项引用:

    for (i = n; --i >= where; )
        items[i+1] = items[i];
    
    另一方面,片分配由函数实现,该函数调用
    memmove

    memmove(&item[ihigh+d], &item[ihigh],
            (k - ihigh)*sizeof(PyObject *));
    

    所以我认为你的问题的答案是C库函数
    memmove
    比简单循环优化得更好。请看:我相信,当从
    list\u ass\u slice
    调用时,它最终会调用您所看到的经过大量手动优化的对象。

    好问题:)我自己对此很好奇。@TimPietzcker——您的基准测试真的让我感到很困惑。我必须自己测试:)。我猜
    insert
    是通过调用片分配代码来实现的。@KeithRandall:不,这两个代码路径是分开的(
    list\u assu\u slice
    vs.
    ins1
    in)。@JoranBeasley——在我的基准测试中,列表没有增长。但在Tim Pietzcker的(见链接答案)中,它在基准测试中增长。(我想长度大约为100000)我的直觉是,
    test1
    速度较慢,因为每个
    a.insert
    都有属性查找和绑定方法创建。为了测试这一点,我将
    test1
    定义为
    deftest1(ins=list.insert)
    ,并将
    a.insert(0,1)
    替换为
    ins(a,0,1)
    ,几乎没有可测量的差异我不相信你的新加入。。。
    a[0:0]
    是否隐式地需要调用
    \uuuu setitem\uuuu
    (通过
    STORE\u SLICE
    ),因为python无法知道
    a
    是列表而不是其他类型?(如果我错了,请纠正我……我不是阅读
    dis.dis
    输出的专家)。调用
    \uuuu setitem\uuuu
    与调用
    插入
    有什么不同?如果有什么需要的话,
    setitem
    需要构造一个额外的
    切片
    对象,然后对其进行解释…@user4815162342——它不也需要隐式地对
    a[0:0]
    进行属性查找吗?(它是如何访问
    \uuuuuSetItem\uuuuuuuuuuuu
    的?)它没有-访问
    \uuuuuuuuuSetItem\uuuuuuuuu
    (以及许多其他特殊方法)是实现类型对象的C结构。它是通过简单的指针解引用来访问的。