python-多重处理:为什么在复制列表时与子进程共享一个numpy数组?

我使用此script(请参阅最后的代码)来评估在父进程派生时是共享还是复制了全局对象.简要地说,脚本创建了一个全局数据对象,并且子进程对数据进行迭代.该脚本还监视内存使用情况,以评估对象是否在子进程中被复制.结果...

我使用此script(请参阅最后的代码)来评估在父进程派生时是共享还是复制了全局对象.

简要地说,脚本创建了一个全局数据对象,并且子进程对数据进行迭代.该脚本还监视内存使用情况,以评估对象是否在子进程中被复制.

结果如下:

>数据= np.ones((N,N)).在子进程中的操作:
data.sum().结果:数据共享(无副本)
> data = list(range(pow(10,8))).子进程中的操作:sum(data).结果:数据被复制.
> data = list(range(pow(10,8))).子进程中的操作:对于数据中的x:通过.结果:数据被复制.

由于写时复制,因此预期结果1).我对结果2)和3)感到有些困惑.为什么要复制数据?

脚本

source

import multiprocessing as mp
import numpy as np
import logging
import os

logger = mp.log_to_stderr(logging.WARNING)

def free_memory():
    total = 0
    with open('/proc/meminfo', 'r') as f:
        for line in f:
            line = line.strip()
            if any(line.startswith(field) for field in ('MemFree', 'Buffers', 'Cached')):
                field, amount, unit = line.split()
                amount = int(amount)
                if unit != 'kB':
                    raise ValueError(
                        'Unknown unit {u!r} in /proc/meminfo'.format(u = unit))
                total += amount
    return total

def worker(i):
    x = data.sum()    # Exercise access to data
    logger.warn('Free memory: {m}'.format(m = free_memory()))

def main():
    procs = [mp.Process(target = worker, args = (i, )) for i in range(4)]
    for proc in procs:
        proc.start()
    for proc in procs:
        proc.join()

logger.warn('Initial free: {m}'.format(m = free_memory()))
N = 15000
data = np.ones((N,N))
logger.warn('After allocating data: {m}'.format(m = free_memory()))

if __name__ == '__main__':
    main()

详细结果

运行1个输出

[WARNING / MainProcess]初始免费:25.1 GB
[WARNING / MainProcess]分配数据后:23.3 GB
[WARNING / Process-2]可用内存:23.3 GB
[WARNING / Process-4]可用内存:23.3 GB
[WARNING / Process-1]可用内存:23.3 GB
[WARNING / Process-3]可用内存:23.3 GB

运行2输出

[WARNING / MainProcess]初始免费:25.1 GB
[WARNING / MainProcess]分配数据后:21.9 GB
[WARNING / Process-2]可用内存:12.6 GB
[WARNING / Process-4]可用内存:12.7 GB
[WARNING / Process-1]可用内存:16.3 GB
[WARNING / Process-3]可用内存:17.1 GB

运行3输出

[WARNING / MainProcess]初始免费:25.1 GB
[WARNING / MainProcess]分配数据后:21.9 GB
[WARNING / Process-2]可用内存:12.6 GB
[WARNING / Process-4]可用内存:13.1 GB
[WARNING / Process-1]可用内存:14.6 GB
[WARNING / Process-3]可用内存:19.3 GB

解决方法:

它们都是写时复制.您所缺少的是,当您这样做时,例如

for x in data:
    pass

由于x依次绑定到每个对象,因此数据中包含的每个对象的引用计数都会暂时增加1.对于int对象,CPython中的refcount是基本对象布局的一部分,因此将复制该对象(您确实对其进行了更改,因为refcount发生了变化).

要使之更类似于numpy.ones的情况,请尝试例如

data = [1] * 10**8

然后,只有一个唯一的对象被列表引用了很多(10 ** 8)次,因此几乎没有要复制的内容(同一对象的refcount会增加和减少很多次).

本文标题为:python-多重处理:为什么在复制列表时与子进程共享一个numpy数组?

基础教程推荐