对于相同大小的列表,为什么深副本比浅副本慢得多?

时间:2019-01-31 19:17:11

标签: python python-3.x copy deep-copy

我一直在研究性能至关重要的应用程序,该应用程序经常需要制作2D整数列表的副本并修改副本(我正在实现minimax算法)。

我已经注意到,副本与具有相同元素数量的列表上的深层副本之间的性能存在很大差异,我想了解一下我的想法是否正确。

要重现我的问题,请运行以下代码:


{001=[GroupMember [name1], GroupMember [name2]], 002=[GroupMember [name3]], 003=[GroupMember [name4]]}

现在,对下面的语句进行计时,您应该会看到类似于我的计时。

import numpy as np

np.random.seed(0)
lst1 = np.random.randint(100, size=1000 * 1000).tolist()
lst2 = np.random.randint(100, size=(1000, 1000)).tolist()

%timeit copy.copy(lst1) %timeit lst1.copy() %timeit copy.deepcopy(lst2) 5 ms ± 49.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 5.47 ms ± 551 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 1.61 s ± 112 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) lst1都有一百万个元素,但是可靠地复制前一个元素要比具有相同数量元素的嵌套列表快200倍。我认为这与以下事实有关:制作嵌套列表的深层副本可能需要一些慢速的递归实现,所以我尝试了

lst2

时间仍然显示出巨大的放缓。我已经检查过Is anyone using valgrind and Qt?,但没有提供太多解释。但是,从时间来看,我怀疑%timeit copy.deepcopy(lst1) 1.43 s ± 90.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 也在复制每个 int ,从而创建了新的整数。但这似乎是一件浪费的事情。

我在这里想的对吗? deepcopy和浅表副本不做的Deepcopy在这里做什么?

我见过docs,但似乎该问题是在寻求替代方法而不是解释(对我来说尚不清楚)。

3 个答案:

答案 0 :(得分:3)

deepcopy未复制整数。无论如何,它不可能做到这一点。

deepcopy很慢,因为它需要处理深层副本的全部复杂性,即使事实证明这是不必要的。这包括为找到的每个对象调度到适当的复印机,即使复印机最终变成basically just be lambda x: x。这包括维护备忘单并跟踪每个复制的对象,以处理对相同对象的重复引用,即使没有对象也是如此。其中包括对listsdicts等数据结构的特殊复制处理,因此在尝试使用递归引用复制数据结构时,它不会进行无限递归。

所有这些都必须完成,无论它是否有回报。所有这些都很昂贵。

此外,deepcopy是纯Python。那没有帮助。将deepcopypickle.loads(pickle.dumps(whatever))(执行非常相似的工作)进行比较,由于C的实现,pickle轻易获胜。 (在Python 2上,将pickle替换为cPickle。)pickle对于利用输入的已知结构的实现仍然很困难,但是:

In [15]: x = [[0]*1000 for i in range(1000)]

In [16]: %timeit copy.deepcopy(x)
1.05 s ± 5.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [17]: %timeit pickle.loads(pickle.dumps(x))
78 ms ± 4.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [18]: %timeit [l[:] for l in x]
4.56 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

答案 1 :(得分:0)

在编程中,深层副本等效于某些内容的物理副本。它是原始对象的实际副本。在大多数编程工具中,您可以对其进行操作,对其进行修改而不会影响原始对象。 但是,另一方面,浅表副本是对原始对象的引用。如果更改它,它也会影响原始对象。 简而言之,由于深层副本是原始对象的实际副本,因此仅指向原始对象的浅层副本要重一些。

浅表副本:您可以对新家具进行拍照,以了解其真实外观。您可以轻松地随身携带图片。

深复制:您可以去家具店,看看真正的家具。您可能无法随身携带,并且可能需要一些帮助才能将其带回家。

答案 2 :(得分:0)

https://docs.python.org/2/library/copy.html

浅复制和深复制之间的区别仅与复合对象(包含其他对象的对象,如列表或类实例)有关:

  1. 浅表副本会构造一个新的复合对象,然后(在可能的范围内)将对原始对象中引用的对象的引用插入其中。
  2. 深层副本会构造一个新的复合对象,然后递归地将原始对象中发现的对象的副本插入其中

如此有效,浅表副本将创建一个新列表,并使用对原始列表中每个元素的引用填充该列表。因为原始列表中的每个元素本身都是一个列表,所以仅存储对此的引用比创建新副本要快得多。 Deepcopy在复制每个元素的方式方面做了一些巧妙的工作,以避免出现错误。但从本质上讲,您不需要了解这一点就可以知道为什么一个浅表副本比深表副本更快。...