以下两种实现如何在Python中具有不同的性能?
from cStringIO import StringIO
from itertools import imap
from sys import stdin
input = imap(int, StringIO(stdin.read()))
print '\n'.join(imap(str, sorted(input)))
和
import sys
for line in sys.stdin:
l.append(int(line.strip('\n')))
l.sort()
for x in l:
print x
对于10 ^ 6行的输入,第一个实现比第二个更快。为什么这样?
答案 0 :(得分:3)
>>> dis.dis(first)
2 0 LOAD_GLOBAL 0 (imap)
3 LOAD_GLOBAL 1 (int)
6 LOAD_GLOBAL 2 (StringIO)
9 LOAD_GLOBAL 3 (stdin)
12 LOAD_ATTR 4 (read)
15 CALL_FUNCTION 0
18 CALL_FUNCTION 1
21 CALL_FUNCTION 2
24 STORE_FAST 0 (input)
27 LOAD_CONST 0 (None)
30 RETURN_VALUE
>>> dis.dis(second)
2 0 SETUP_LOOP 48 (to 51)
3 LOAD_GLOBAL 0 (sys)
6 LOAD_ATTR 1 (stdin)
9 CALL_FUNCTION 0
12 GET_ITER
>> 13 FOR_ITER 34 (to 50)
16 STORE_FAST 0 (line)
3 19 LOAD_GLOBAL 2 (l)
22 LOAD_ATTR 3 (append)
25 LOAD_GLOBAL 4 (int)
28 LOAD_FAST 0 (line)
31 LOAD_ATTR 5 (strip)
34 LOAD_CONST 1 ('\n')
37 CALL_FUNCTION 1
40 CALL_FUNCTION 1
43 CALL_FUNCTION 1
46 POP_TOP
47 JUMP_ABSOLUTE 13
>> 50 POP_BLOCK
4 >> 51 LOAD_GLOBAL 2 (l)
54 LOAD_ATTR 6 (sort)
57 CALL_FUNCTION 0
60 POP_TOP
61 LOAD_CONST 0 (None)
64 RETURN_VALUE
first
是你的第一个功能。
second
是你的第二个功能。
dis
讲述了第一个更快的原因之一。
答案 1 :(得分:2)
两个主要原因:
sorted
在同时排序的同时创建一个内部列表。for
的列表(在Python VM上),而第一个版本隐式循环使用imap
(在C中的底层结构上)。 无论如何,为什么StringIO在那里?最直接也可能最快的方法是:
from sys import stdin, stdout
stdout.writelines(sorted(stdin, key=int))
答案 2 :(得分:1)
从第二步到第一步的逐步转换,看看性能如何随每一步而变化。
l.append
替换为for循环。我的猜测是,这会显着提高速度。map
和l.sort
替换为imap
和sorted
。我的猜测是,它不会影响性能,可能会有轻微的减速,但它远非显着。在两者之间,我通常会选择前者,但在Python 3中,后者可能更适合。print
和print '\n'.join(...)
替换for循环。我的猜测是,这将是另一次加速,但它会花费你一些记忆。然后,如果你尝试THC4k的答案,它可能会比上述所有更快,同时更简单,更容易阅读,并且使用的内存少于4和5.它的行为略有不同(它不会剥离数字中的前导零。)
当然,请亲自尝试,而不是相信任何猜测。同时在代码上运行cProfile
,看看哪些部分正在丢失大部分时间。