在尝试回答What is the preferred way to compose a set from multiple lists in Python时,我做了一些性能分析,得出了一个令人惊讶的结论。
使用
python -m timeit -s '
import itertools
import random
n=1000000
random.seed(0)
A = [random.randrange(1<<30) for _ in xrange(n)]
B = [random.randrange(1<<30) for _ in xrange(n)]
C = [random.randrange(1<<30) for _ in xrange(n)]'
对于设置,我定时了以下片段:
> $TIMEIT 'set(A+B+C)'
10 loops, best of 3: 872 msec per loop
> $TIMEIT 's = set(A); s.update(B); s.update(C)'
10 loops, best of 3: 930 msec per loop
> $TIMEIT 's = set(itertools.chain(A,B,C))'
10 loops, best of 3: 941 msec per loop
令我惊讶的是,set(A+B+C)
是最快的,尽管它创建了一个包含3000000个元素的中间列表。 .update
和itertools.chain
都较慢,即使它们都没有复制任何列表。
这里发生了什么?
编辑:在第二台机器(OS X 10.10.5,Python 2.7.10,2.5GHz Core i7)上,我运行了以下脚本(向前和向后运行测试以避免排序效果):
SETUP='import itertools
import random
n=1000000
random.seed(0)
A = [random.randrange(1<<30) for _ in xrange(n)]
B = [random.randrange(1<<30) for _ in xrange(n)]
C = [random.randrange(1<<30) for _ in xrange(n)]'
python -m timeit -s "$SETUP" 'set(A+B+C)'
python -m timeit -s "$SETUP" 's = set(A); s.update(B); s.update(C)'
python -m timeit -s "$SETUP" 's = set(itertools.chain(A,B,C))'
python -m timeit -s "$SETUP" 's = set(itertools.chain(A,B,C))'
python -m timeit -s "$SETUP" 's = set(A); s.update(B); s.update(C)'
python -m timeit -s "$SETUP" 'set(A+B+C)'
并获得以下结果:
10 loops, best of 3: 579 msec per loop
10 loops, best of 3: 726 msec per loop
10 loops, best of 3: 775 msec per loop
10 loops, best of 3: 761 msec per loop
10 loops, best of 3: 737 msec per loop
10 loops, best of 3: 555 msec per loop
现在set(A+B+C)
显然更快,而且结果非常稳定 - 很难将其归结为单纯的测量误差。重复运行此脚本会产生类似的结果。