受this nice answer的启发,
这是一个基准:
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%)。为什么会这样?我希望它会变慢:
有人可以帮我理解吗?
(在OS-X 10.5.8上使用python 2.7)
答案 0 :(得分:14)
您的第一个测试用例必须调用列表insert
上的方法a
,而test2
中的所有操作都直接在字节码中处理。请注意下面CALL_FUNCTION
反汇编中的test1
。在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
中的ins1
函数实现。您将看到它逐个复制列表尾部的项目引用:
for (i = n; --i >= where; )
items[i+1] = items[i];
另一方面,切片分配由函数list_ass_slice
实现,该函数调用memmove
:
memmove(&item[ihigh+d], &item[ihigh],
(k - ihigh)*sizeof(PyObject *));
所以我认为你的问题的答案是C库函数memmove
比简单循环更好地优化。请参阅here for the glibc implementation of memmove
:我相信,当从list_ass_slice
调用时,它最终会调用_wordcopy_bwd_aligned
,您可以看到它是手工优化的。