我想尝试使用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
文档确实说,这种方式比共享数组慢,但这只是感觉不对。我也不知道我该如何描述这个来获得更多的信息。我遗漏了什么吗?
p.S.使用共享阵列,我得到的时间低于0.25秒
这是Linux和Python3.3上的。
这是意料之中的,因为访问共享对象意味着必须pickle请求通过某种信号/syscall发送它取消pickle请求执行它并以相同的方式返回结果。
基本上你应该尽量避免共享内存。这将导致更多的可调试代码(因为您的并发性要少得多),并且速度更快。
只有在真正需要的时候才应该使用共享内存(例如,共享千兆字节的数据,以便复制它需要太多的RAM,或者进程应该能够通过这个共享内存进行交互)。
另一方面,使用管理器可能比共享数组慢得多,因为管理器必须能够处理任何PyObject*,因此必须pickle/unpickle等,而数组可以避免很多这样的开销。
从多处理的文档中:
因此,使用管理器意味着生成一个新进程,该进程仅用于处理共享内存,这可能是它需要更多时间的原因。
如果您尝试分析代理的速度,它会比非共享列表慢很多:
虽然
Array
没有那么慢:因为最基本的操作是缓慢的,并且不认为有太大的希望来加速它们,这意味着如果您必须共享一个大的数据列表并希望快速访问它,那么您应该使用一个
Array
。一次访问多个元素(例如,获取切片而不是单个元素)可能会稍微加快速度,但这取决于您要执行的操作可能是,也可能是不可能的。
当子进程是
os.fork
ed时,Linux使用copy-on-write来演示:它屈服了
这表明最初大约有2.5GB的空闲内存。 在分配15000x15000个
float64
s数组后,有763248 KB空闲空间。这大概是有道理的,因为15000**2*8字节=1.8GB,内存的减少,2.5GB-0.763248GB也大约是1.8GB。现在,每个进程生成后,空闲内存再次报告为~750MB。可用内存没有明显减少,因此我得出结论,系统必须使用写时拷贝。
结论:如果不需要修改数据,那么在
__main__
模块的全局级别定义它是在子进程之间共享数据的一种方便且(至少在Linux上)内存友好的方式。相关问题 更多 >
编程相关推荐