我有两个相同的代码
Code1
>>> a=[0]*60000000
Code2
>>> a=[]
>>> for i in range(0,60000000):
a.append(0)
在我的电脑中Code1
所需时间为1秒,但Code2
为480秒!!
为什么?哇是不同的?
答案 0 :(得分:3)
(接下来我假设你使用的是Python 3;在Python 2中,故事情节类似,但我们必须讨论由range
引起的内存分配成本。)
你的第二个程序花费时间在Python字节码中运行。让我们使用dis
模块对其进行反汇编:
>>> import dis
>>> dis.dis('a=[]\nfor i in range(0,60000000):\n a.append(0)')
1 0 BUILD_LIST 0
3 STORE_NAME 0 (a)
2 6 SETUP_LOOP 36 (to 45)
9 LOAD_NAME 1 (range)
12 LOAD_CONST 0 (0)
15 LOAD_CONST 1 (60000000)
18 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
21 GET_ITER
>> 22 FOR_ITER 19 (to 44)
25 STORE_NAME 2 (i)
3 28 LOAD_NAME 0 (a)
31 LOAD_ATTR 3 (append)
34 LOAD_CONST 0 (0)
37 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
40 POP_TOP
41 JUMP_ABSOLUTE 22
>> 44 POP_BLOCK
>> 45 LOAD_CONST 2 (None)
48 RETURN_VALUE
循环从字节22到41运行,每次循环循环,Python必须解码并执行七字节代码指令,并调用函数(函数为a.append
)。这是4.2亿字节的代码指令和6000万个函数调用。
而您的第一个程序花费时间在本机代码中运行 :
>>> dis.dis('a = [0] * 60000000')
1 0 LOAD_CONST 0 (0)
3 BUILD_LIST 1
6 LOAD_CONST 1 (60000000)
9 BINARY_MULTIPLY
10 STORE_NAME 0 (a)
13 LOAD_CONST 2 (None)
16 RETURN_VALUE
你可以看到,不仅没有循环,也没有函数调用。所有工作都在BINARY_MULTIPLY
指令内“发生”,发送到list_multiply
in listobject.c
,由于列表[0]
只包含一个元素,因此结果将在此紧密循环中构建第529-536行:
if (Py_SIZE(a) == 1) {
elem = a->ob_item[0];
for (i = 0; i < n; i++) {
items[i] = elem;
Py_INCREF(elem);
}
return (PyObject *) np;
}
答案 1 :(得分:0)
range
自己创建一个列表,而不是迭代器。因此,您将Code2
中的工作加倍。这意味着,您首先创建一个6000000项的列表,然后迭代它。 Code1
不需要那个中间步骤。
此[range]是一个多功能函数,用于创建包含算术的列表 级数
Soure:Range Python 2.x
将其与Python 3.x进行比较:
范围实际上是一个不可变的序列,而不是一个函数 类型,如范围和序列类型中记录的 - 列表,元组,范围。
尝试使用xrange
代替range
,然后再次测试。
答案 2 :(得分:0)
忽略您使用range
而不是xrange
的事实,有两件事情正在发生:
1)虽然函数到达相同的输出,但函数不会做同样的事情。您的code1
表示“[0]
连接在一起的6000万份”。这不是Code2
正在做的事情,显然 - 它必须将列表的append
方法称为6000万次。
2)python中的显式循环很慢。在某个地方Code1
有一个循环,但它以“C速度”发生,而不是蟒蛇速度。
def f():
a = [0]*6000000
def g():
a = []
for i in xrange(6000000):
a.append(0)
%timeit f()
10 loops, best of 3: 31 ms per loop
%timeit g()
1 loops, best of 3: 358 ms per loop
在这样的紧密循环中,大部分开销都在循环本身中,而不是其中包含的操作。因此,优化紧密循环的一种流行方法是将它们理解为:
def h():
[0 for _ in xrange(6000000)]
%timeit h()
10 loops, best of 3: 151 ms per loop
请注意,这两个函数再次没有做同样的事情,这就是f
和h
之间存在差异的原因。