我有一个简单的多处理代码:
from multiprocessing import Pool
import time
def worker(data):
time.sleep(20)
if __name__ == "__main__":
numprocs = 10
pool = Pool(numprocs)
a = ['a' for i in xrange(1000000)]
b = [a+[] for i in xrange(100)]
data1 = [b+[] for i in range(numprocs)]
data2 = [data1+[]] + ['1' for i in range(numprocs-1)]
data3 = [['1'] for i in range(numprocs)]
#data = data1
#data = data2
data = data3
result = pool.map(worker,data)
b
只是一个大列表。data
是传递给的长度numprocs
的列表池.map,所以我希望numprocs
进程被分叉,data
的每个元素都被传递给其中一个。
我测试了3个不同的data
对象:data1
和{data1
时,每个进程都会得到同一个对象的副本,而当使用data2
时,一个进程得到所有的{data3
基本上是空的,用于度量分叉进程的基本开销成本。在
问题:
在data1
和{
data1
:~8GBdata2
:~0.8GBdata3
:~0GB不应该1)和2)相等,因为传递给子对象的数据总量是相同的。怎么回事?在
我从Linux机器上的/proc/meminfo的Active
字段测量内存使用情况(Total-MemFree
给出了相同的答案)
您看到的是
pool.map
必须对data
中的每个元素进行pickle以将其传递给子进程的副作用。在当你腌制它时,
data1
比data2
大得多。下面是一个小脚本,它将每个列表中每个元素的总pickle大小相加:输出:
^{pr2}$如您所见,
data1
的总pickle大小大约是data2
大小的十倍,这与两者在内存使用上的数量级差异完全匹配。在之所以
data2
pickles这么小是因为Cilyan在评论中提到的;实际上,当您创建每个data
列表时,实际上是在进行浅拷贝:现在,
data1
也在制作浅拷贝:关键的区别在于使用
data2
,所有的浅拷贝都在iterable(data2[0]
)的一个顶层元素中。因此,当pool.map
pickle该元素时,它可以将所有浅表副本反复制到一个单独的子列表中,只需pickle该子列表,以及描述该子列表如何嵌套到data2
中的元数据。使用data1
时,浅层副本跨data1
的不同顶层元素,因此pool.map
分别对它们进行pickle,这意味着重复数据消除丢失。因此,当您取消拾取data1
时,浅层副本就不复存在了,每个元素都有一个唯一但相同的子列表副本。在比较这两个例子,其中}与您的列表相同:
data1
和{这模拟了
pool.map
所做的相同的酸洗。使用data1
,由于浅拷贝而进行的所有重复数据消除都将丢失,这使得内存使用率更高。在相关问题 更多 >
编程相关推荐