使用multiprocessing.Manager.list而不是真实列表会使计算耗时
我想尝试multiprocessing
从本示例开始的不同使用方式:
$ cat multi_bad.py
import multiprocessing as mp
from time import sleep
from random import randint
def f(l, t):
# sleep(30)
return sum(x < t for x in l)
if __name__ == '__main__':
l = [randint(1, 1000) for _ in range(25000)]
t = [randint(1, 1000) for _ in range(4)]
# sleep(15)
pool = mp.Pool(processes=4)
result = pool.starmap_async(f, [(l, x) for x in t])
print(result.get())
这里l
是一个列表,当生成4个进程时,该列表将被复制4次。为了避免这种情况,文档页面提供了使用队列,共享数组或使用创建的代理对象的信息multiprocessing.Manager
。对于最后一个,我更改了的定义l
:
$ diff multi_bad.py multi_good.py
10c10,11
< l = [randint(1, 1000) for _ in range(25000)]
---
> man = mp.Manager()
> l = man.list([randint(1, 1000) for _ in range(25000)])
结果看起来仍然正确,但是执行时间却大大增加,以至于我做错了什么:
$ time python multi_bad.py
[17867, 11103, 2021, 17918]
real 0m0.247s
user 0m0.183s
sys 0m0.010s
$ time python multi_good.py
[3609, 20277, 7799, 24262]
real 0m15.108s
user 0m28.092s
sys 0m6.320s
文档确实说这种方法比共享数组要慢,但这感觉很不对。我也不确定如何配置此文件,以获取有关正在发生的事情的更多信息。我想念什么吗?
PS与共享数组,我得到的时间低于0.25s。
PPS这是在Linux和Python 3.3上。
-
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[i,:].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()
产生了
[WARNING/MainProcess] Initial free: 2522340 [WARNING/MainProcess] After allocating data: 763248 [WARNING/Process-1] Free memory: 760852 [WARNING/Process-2] Free memory: 757652 [WARNING/Process-3] Free memory: 757264 [WARNING/Process-4] Free memory: 756760
这表明最初大约有2.5GB的可用内存。分配15000x15000的数组后
float64
,有763248 KB的可用空间。因为15000 ** 2 * 8个字节= 1.8GB,而内存的下降2.5GB-0.763248GB也大约是1.8GB,所以这大概是有道理的。现在,在生成每个进程之后,再次报告可用内存为〜750MB。可用内存没有明显减少,因此我得出结论,系统必须使用写时复制。
结论:如果不需要修改数据,则在
__main__
模块的全局级别定义数据是一种方便的(至少在Linux上)内存友好的方式,可以在子进程之间共享数据。