【发布时间】:2013-01-25 13:15:36
【问题描述】:
for a in sorted(arr):
doSomething()
对
sArr = sorted(arr)
for a in sArr:
doSomething()
这两个是否完全相同,或者一个会比另一个消耗更多的 cpu 周期?
意思是,在第一个例子中,sorted(arr) 会在每个循环中被调用吗?
谢谢
【问题讨论】:
标签: python for-loop python-2.7
for a in sorted(arr):
doSomething()
对
sArr = sorted(arr)
for a in sArr:
doSomething()
这两个是否完全相同,或者一个会比另一个消耗更多的 cpu 周期?
意思是,在第一个例子中,sorted(arr) 会在每个循环中被调用吗?
谢谢
【问题讨论】:
标签: python for-loop python-2.7
它们几乎相同。
表达式列表被计算一次;它应该产生一个可迭代的对象。
因此,在上述代码中,sorted() 调用不会发生多次。
第一种形式仍然更好,因为它更短更简洁,并且可以更好地优化(因为解释器可以确定循环结束后排序列表完全超出范围)。
【讨论】:
两者并不完全相同,但差异很小。如有疑问,请使用dis 模块查找:
>>> import dis
>>> def f():
... for a in sorted(arr):
... doSomething()
...
>>> def g():
... sArr = sorted(arr)
... for a in sArr:
... doSomething()
...
>>> dis.dis(f)
2 0 SETUP_LOOP 27 (to 30)
3 LOAD_GLOBAL 0 (sorted)
6 LOAD_GLOBAL 1 (arr)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 13 (to 29)
16 STORE_FAST 0 (a)
3 19 LOAD_GLOBAL 2 (doSomething)
22 CALL_FUNCTION 0
25 POP_TOP
26 JUMP_ABSOLUTE 13
>> 29 POP_BLOCK
>> 30 LOAD_CONST 0 (None)
33 RETURN_VALUE
>>> dis.dis(g)
2 0 LOAD_GLOBAL 0 (sorted)
3 LOAD_GLOBAL 1 (arr)
6 CALL_FUNCTION 1
9 STORE_FAST 0 (sArr)
3 12 SETUP_LOOP 21 (to 36)
15 LOAD_FAST 0 (sArr)
18 GET_ITER
>> 19 FOR_ITER 13 (to 35)
22 STORE_FAST 1 (a)
4 25 LOAD_GLOBAL 2 (doSomething)
28 CALL_FUNCTION 0
31 POP_TOP
32 JUMP_ABSOLUTE 19
>> 35 POP_BLOCK
>> 36 LOAD_CONST 0 (None)
39 RETURN_VALUE
如您所见,g() 函数添加了STORE_FAST 和LOAD_FAST 操作。您还使用了更多内存,因为排序结果会一直保留,直到清理 sArr 变量,而在 f() 中,排序结果可以在循环结束后立即清理。
CALL_FUNCTION 执行sorted() 函数;它只执行一次。
【讨论】:
sorted() 返回一个随后被迭代的对象。它只被调用一次。
【讨论】:
和其他人说的一样。请记住,当有疑问时(尽管在这种情况下微不足道且显而易见),请始终在源代码上运行反汇编。或者,您可以通过 cProfile 运行它,即使反汇编不同,cProfile 也会对性能给出一个公平的概念
>>> def foo():
total = 0
arr = range(10000000,1,-1)
for a in sorted(arr):
total += a
>>> def bar():
total = 0
for a in sorted(range(10000000,1,-1)):
total += a
>>> cProfile.run("bar()")
5 function calls in 2.614 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 1.914 1.914 2.614 2.614 <pyshell#88>:1(bar)
1 0.000 0.000 2.614 2.614 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.266 0.266 0.266 0.266 {range}
1 0.434 0.434 0.434 0.434 {sorted}
>>> cProfile.run("foo()")
5 function calls in 2.477 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 1.779 1.779 2.346 2.346 <pyshell#86>:1(foo)
1 0.130 0.130 2.477 2.477 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.152 0.152 0.152 0.152 {range}
1 0.415 0.415 0.415 0.415 {sorted}
>>>
正如您将在此处观察到的那样
【讨论】:
timeit 来消除操作系统调度的变化。
cProfile 的主要目的是展示functions calls 和ncalls