【问题标题】:Why is copying a list using a slice[:] faster than using the obvious way?为什么使用 slice[:] 复制列表比使用明显方式更快?
【发布时间】:2014-04-01 20:23:24
【问题描述】:

为什么使用切片对列表进行浅拷贝比使用 list 内置函数快得多?

In [1]: x = range(10)

In [2]: timeit x_ = x[:]
10000000 loops, best of 3: 83.2 ns per loop

In [3]: timeit x_ = list(x)
10000000 loops, best of 3: 147 ns per loop

通常当我看到这样奇怪的东西时,它们在 python3 中已修复 - 但这种差异仍然存在:

In [1]: x = list(range(10))

In [2]: timeit x_ = x[:]
10000000 loops, best of 3: 100 ns per loop

In [3]: timeit x_ = list(x)
10000000 loops, best of 3: 178 ns per loop

【问题讨论】:

  • 一种解释是list是一个你需要调用的函数。
  • 还有列表需要对每个项目做一些事情,其中​​一个切片只是将一块内存复制到一个新地址......
  • 确实,至少对于[:] 的情况,有可能只在列表中执行memcopy 进行优化,而我认为list(x) 方法无法执行相同的优化,至少在它完成了许多其他样板文件之前,必须接受许多类型的构造函数必须这样做。这纯粹是猜测,没有深入了解源头。
  • @aruisdante 列表构造函数首先必须检查PyList_CheckExact,但这只是指针算术和 C 级分支(= 最坏的情况是几十个周期),所以我不认为这将是一个重要因素。

标签: python performance list cpython shallow-copy


【解决方案1】:

区别在于额外的函数调用(只是SLICE+0 vs CALL_FUNCTION 1 带有额外的堆栈操作):

>>> import dis
>>> def f(lst):
...  return lst[:]
... 
>>> def f1(lst):
...  return list(lst)
... 
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (lst)
              3 SLICE+0             
              4 RETURN_VALUE        
>>> dis.dis(f1)
  2           0 LOAD_GLOBAL              0 (list)
              3 LOAD_FAST                0 (lst)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE 

来自dis docs

SLICE+0()
实现 TOS = TOS[:]。

(TOS - 栈顶)

CALL_FUNCTION(argc)
调用一个函数。 argc 的低字节表示 位置参数个数,高字节个数 关键字参数。在栈上,操作码找到关键字 先说参数。对于每个关键字参数,该值位于 钥匙。在关键字参数下方,位置参数是 在堆栈上,最右边的参数在顶部。以下 参数,要调用的函数对象在堆栈上。全部弹出 函数参数,以及函数本身离开堆栈,并推送 返回值。

【讨论】:

    猜你喜欢
    • 2014-10-12
    • 2013-05-13
    • 1970-01-01
    • 2017-10-21
    • 2023-03-13
    • 1970-01-01
    • 2015-12-05
    • 1970-01-01
    • 2021-06-10
    相关资源
    最近更新 更多