Introduction:
因为效率需求,需要将一个单进程的Monte Carlo程序并行化处理。众所周知,Monte Carlo算法天生易于并行,然而主要的问题在于,所有的子进程运行时需要读取一个大小为几个GB的ndarray object,该对象由10^4量级个长度不同的子数组(同样为ndarray object)封装而成。所以,本文需要解决的问题在于,如何在考虑到内存优化的同时最简单的实现多线程并行?
Possible solutions:
- 使用多线程,从而共享内存
- 使用multiprocessing module 自带的内存共享方法Value 和 Array(例子),但是由于本问题中共享ndarray 对象的子数组长度不对齐,难以利用ctypes 的Array 封装。
- 使用multiprocessing module自带的第二种内存共享方法Manager(文档)。该方法适用于任意的对象,通过进程锁确保数据安全性,但是据说费时。用于read-only object 似乎有点浪费。
- 使用Linux系统。在Linux系统中multiprocessing 调用系统的fork() 程序来实现多进程,由于Linux 的Copy-on-Write 特性,子例程对于read-only 对象不会复制新的对象。
Tests:
利用同样的代码段,在windows 和Linux 平台分别测试:
from multiprocessing import Pool
import numpy as np
shared_mem = np.arange(10)
def target_fun(x, a=shared_mem):
return "input x: "+str(x)+"; len(a) = "+str(len(a))+"; id(a) = "+str(id(a))
if __name__ == '__main__':
# shared_mem = np.arange(10)
inputs = list(range(10))
pool = Pool(processes=4)
res = pool.map(target_fun, inputs)
pool.close()
pool.join()
for i in res:
print(i)
Windows:
results:
input x: 0; len(a) = 10; id(a) = 2152628373184
input x: 1; len(a) = 10; id(a) = 1822852007536
input x: 2; len(a) = 10; id(a) = 1366939551344
input x: 3; len(a) = 10; id(a) = 2152628373184
input x: 4; len(a) = 10; id(a) = 1366939551344
input x: 5; len(a) = 10; id(a) = 2483693766176
input x: 6; len(a) = 10; id(a) = 1748360250992
input x: 7; len(a) = 10; id(a) = 1822852007536
input x: 8; len(a) = 10; id(a) = 2152628373184
input x: 9; len(a) = 10; id(a) = 2483693766176
Linux:
results:
input x: 0; len(a) = 10; id(a) = 47359601904656
input x: 1; len(a) = 10; id(a) = 47359601904656
input x: 2; len(a) = 10; id(a) = 47359601904656
input x: 3; len(a) = 10; id(a) = 47359601904656
input x: 4; len(a) = 10; id(a) = 47359601904656
input x: 5; len(a) = 10; id(a) = 47359601904656
input x: 6; len(a) = 10; id(a) = 47359601904656
input x: 7; len(a) = 10; id(a) = 47359601904656
input x: 8; len(a) = 10; id(a) = 47359601904656
input x: 9; len(a) = 10; id(a) = 47359601904656
注意这里的共享对象必须对子进程是可见的,即 shared_mem 的赋值必须在
if __name__ == '__main__':
前面。
Final solution:
from multiprocessing import Pool
import numpy as np
shared_mem = reader()
def target_fun(x):
do_chunk(shared_mem)
return res
if __name__ == '__main__':
pool = Pool(processes=N)
inputs = segemt()
res = pool.map(target_fun, inputs)
pool.close()
pool.join()
do_analysis(res)