以下代码并行化for循环。
import networkx as nx;
import numpy as np;
from joblib import Parallel, delayed;
import multiprocessing;
def core_func(repeat_index, G, numpy_arrary_2D):
for u in G.nodes():
numpy_arrary_2D[repeat_index][u] = 2;
return;
if __name__ == "__main__":
G = nx.erdos_renyi_graph(100000,0.99);
nRepeat = 5000;
numpy_array = np.zeros([nRepeat,G.number_of_nodes()]);
Parallel(n_jobs=4)(delayed(core_func)(repeat_index, G, numpy_array) for repeat_index in range(nRepeat));
print(np.mean(numpy_array));
可以看出,要打印的期望值是2.但是,当我在集群(多核,共享内存)上运行我的代码时,它返回0.0。
我认为问题在于每个工作者都创建自己的numpy_array
对象副本,并且不会更新在main函数中创建的副本。如何修改代码以便更新numpy数组numpy_array
?
答案 0 :(得分:6)
joblib
使用进程的多处理池,如its manual所示:
在引擎盖下,Parallel对象创建一个多处理池 在多个进程中分叉Python解释器来执行每个进程 列表中的项目。延迟功能是一个简单的技巧 能够使用函数调用创建元组(函数,args,kwargs) 语法。
这意味着,每个进程都会继承数组的原始状态,但无论它在其中写入什么内容,都会在进程退出时丢失。只有函数结果被传递回调用(主)进程。但是你没有返回任何东西,所以返回None
。
要使共享数组具有可编辑性,您有两种方法:使用线程和使用共享内存。
与进程不同,线程共享内存。所以你可以写入数组,每个工作都会看到这个变化。根据{{1}}手册,它是这样做的:
joblib
运行时:
Parallel(n_jobs=4, backend="threading")(delayed(core_func)(repeat_index, G, numpy_array) for repeat_index in range(nRepeat));
但是,当您将复杂的东西写入数组时,请确保正确处理数据或数据块周围的锁定,否则您将遇到竞争条件(google it)。
还要仔细阅读GIL,因为Python中的计算多线程是有限的(与I / O多线程不同)。
如果您仍然需要进程(例如,因为GIL),您可以将该数组放入共享内存。
这是一个更复杂的主题,但$ python r1.py
2.0
手册中也显示了joblib + numpy shared memory example。
答案 1 :(得分:0)
正如谢尔盖在回答中写道的那样,流程并不共享状态和记忆。这就是为什么你没有看到预期的答案。
线程共享状态和内存空间,因为它们在同一进程下运行。如果您有许多I / O操作,这将非常有用。由于 GIL
,它不会为您带来更多处理能力(更多CPU)进程间通信的一种技术是使用Manager的代理对象。您可以创建一个管理器对象,该对象可以在进程之间同步资源。
Manager()返回的管理器对象控制一个服务器进程,该进程保存Python对象并允许其他进程使用代理操作它们。
我还没有测试过这段代码(我没有使用你使用的所有模块),而且可能需要对代码进行更多修改,但使用Manager对象应该看起来像这样
if __name__ == "__main__":
G = nx.erdos_renyi_graph(100000,0.99);
nRepeat = 5000;
manager = multiprocessing.Manager()
numpys = manager.list(np.zeros([nRepeat, G.number_of_nodes()])
Parallel(n_jobs=4)(delayed(core_func)(repeat_index, G, numpys, que) for repeat_index in range(nRepeat));
print(np.mean(numpys));