协慌网

登录 贡献 社区

为什么 [] 比 list()快?

我最近比较了[]list()的处理速度,并惊讶地发现[]运行速度比list()快三倍以上。 {}dict()进行了相同的测试,结果实际上是相同的: []{}都花费了约 0.128sec / 百万个周期,而list()dict()花费了约 0.428sec / 百万个周期。

为什么是这样? Do []{} (可能还有()'' )也立即传回一些空股票文字的副本,同时使用它们的显式命名副本( list()dict()tuple()str() )完全创建对象,无论它们实际上是否包含元素?

我不知道这两种方法有何不同,但我很想找出答案。我在文档中或 SO 上都找不到答案,而寻找空括号却比我预期的要麻烦得多。

timeit.timeit("[]")timeit.timeit("list()")以及timeit.timeit("{}")timeit.timeit("dict()")获得计时结果。分别比较列表和字典。我正在运行 Python 2.7.9。

我最近发现 “为什么 True 慢于 if 1? if Trueif 1的性能进行了比较,似乎触及了类似的文字对全局的情况。也许也值得考虑。

答案

因为[]{}文字语法。 Python 可以创建字节码仅用于创建列表或字典对象:

>>> import dis
>>> dis.dis(compile('[]', '', 'eval'))
  1           0 BUILD_LIST               0
              3 RETURN_VALUE        
>>> dis.dis(compile('{}', '', 'eval'))
  1           0 BUILD_MAP                0
              3 RETURN_VALUE

list()dict()是单独的对象。它们的名称需要解析,必须包含堆栈以推入参数,必须存储框架以供以后检索,并且必须进行调用。这都需要更多时间。

对于空的情况下,这意味着你至少是一个LOAD_NAME (已通过全局命名空间,以及以搜索builtins模块),接着是CALL_FUNCTION ,里面有保存当前帧:

>>> dis.dis(compile('list()', '', 'eval'))
  1           0 LOAD_NAME                0 (list)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        
>>> dis.dis(compile('dict()', '', 'eval'))
  1           0 LOAD_NAME                0 (dict)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE

timeit分别对名称查找进行计时:

>>> import timeit
>>> timeit.timeit('list', number=10**7)
0.30749011039733887
>>> timeit.timeit('dict', number=10**7)
0.4215109348297119

时间差异可能是字典哈希冲突。从调用这些对象的时间中减去这些时间,然后将结果与使用文字的时间进行比较:

>>> timeit.timeit('[]', number=10**7)
0.30478692054748535
>>> timeit.timeit('{}', number=10**7)
0.31482696533203125
>>> timeit.timeit('list()', number=10**7)
0.9991960525512695
>>> timeit.timeit('dict()', number=10**7)
1.0200958251953125

因此,每 1 千万次调用必须调用对象额外花费1.00 - 0.31 - 0.30 == 0.39

您可以通过将全局名称别名为本地名称来避免全局查找成本(使用timeit设置,您绑定到名称的所有内容都是本地名称):

>>> timeit.timeit('_list', '_list = list', number=10**7)
0.1866450309753418
>>> timeit.timeit('_dict', '_dict = dict', number=10**7)
0.19016098976135254
>>> timeit.timeit('_list()', '_list = list', number=10**7)
0.841480016708374
>>> timeit.timeit('_dict()', '_dict = dict', number=10**7)
0.7233691215515137

但您永远无法克服CALL_FUNCTION费用。

list()需要全局查找和函数调用,但是[]编译为一条指令。看:

Python 2.7.3
>>> import dis
>>> dis.dis(lambda: list())
  1           0 LOAD_GLOBAL              0 (list)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        
>>> dis.dis(lambda: [])
  1           0 BUILD_LIST               0
              3 RETURN_VALUE

由于list是一个功能转化说一个字符串列表对象,而[]用于创建一个列表蝙蝠。尝试以下操作(可能对您更有意义):

x = "wham bam"
a = list(x)
>>> a
["w", "h", "a", "m", ...]

尽管

y = ["wham bam"]
>>> y
["wham bam"]

为您提供包含您所输入内容的实际列表。