【发布时间】:2017-03-02 08:23:40
【问题描述】:
我有一个 python 程序,它从文件中读取行并将它们放入 dict,简单来说,它看起来像:
data = {'file_name':''}
with open('file_name') as in_fd:
for line in in_fd:
data['file_name'] += line
我发现它需要几个小时才能完成。
然后,我对程序做了一点改动:
data = {'file_name':[]}
with open('file_name') as in_fd:
for line in in_fd:
data['file_name'].append(line)
data['file_name'] = ''.join(data['file_name'])
它在几秒钟内完成。
我以为是+= 使程序变慢,但似乎不是。请看下面的测试结果。
我知道我们可以使用列表追加和连接来提高连接字符串时的性能。但是我从没想过append and join和add and assign之间会有这么大的性能差距。
所以我决定再做一些测试,最后发现是dict更新操作让程序变慢了。这是一个脚本:
import time
LOOPS = 10000
WORD = 'ABC'*100
s1=time.time()
buf1 = []
for i in xrange(LOOPS):
buf1.append(WORD)
ss = ''.join(buf1)
s2=time.time()
buf2 = ''
for i in xrange(LOOPS):
buf2 += WORD
s3=time.time()
buf3 = {'1':''}
for i in xrange(LOOPS):
buf3['1'] += WORD
s4=time.time()
buf4 = {'1':[]}
for i in xrange(LOOPS):
buf4['1'].append(WORD)
buf4['1'] = ''.join(buf4['1'])
s5=time.time()
print s2-s1, s3-s2, s4-s3, s5-s4
在我的笔记本电脑(mac pro 2013 mid, OS X 10.9.5, cpython 2.7.10)中,它的输出是:
0.00299620628357 0.00415587425232 3.49465799332 0.00231599807739
受 juanpa.arrivillaga 的 cmets 启发,我对第二个循环做了一点改动:
trivial_reference = []
buf2 = ''
for i in xrange(LOOPS):
buf2 += WORD
trivial_reference.append(buf2) # add a trivial reference to avoid optimization
更改后,现在第二个循环需要 19 秒才能完成。所以就像 juanpa.arrivillaga 说的那样,这似乎只是一个优化问题。
【问题讨论】:
-
你应该做一个连接字符串然后分配给一个字典而不是
+=一个字典上的字符串。 -
最好使用
timeit模块进行此类基准测试。 -
这与
update无关,但如果对给定字符串只有一个引用,则可能是某种优化。将第三个循环改为x = buf3.pop('1'); x += WORD; buf3['1'] = x,结果差别不大:0.00136399269104 0.00481796264648 0.00717210769653 0.00203800201416。 -
它不仅仅是字典。使用列表
buf5=['']和buf5[0] += WORD,你会得到类似的缓慢。就是更新慢了。 -
@muru 是的,juanpa.arrivillaga 的评论可能是对的,这是一个优化问题,您的测试证明了这一点。
标签: python performance dictionary cpython python-internals