文档地址:https://docs.python.org/zh-cn/3.9/library/multiprocessing.html
在学习之前,首先了解了一下python的:
global interpreter lock – 全局解释器锁(CIL)
CPython 解释器所采用的一种机制,它确保同一时刻只有一个线程在执行 Python bytecode。此机制通过设置对象模型(包括 dict 等重要内置类型)针对并发访问的隐式安全简化了 CPython 实现。给整个解释器加锁使得解释器多线程运行更方便,其代价则是牺牲了在多处理器上的并行性。
也就是说python本身是没法实现的多线程,但是可以创造多个子进程来绕过CIL锁实现并行计算。
文档中提出了Pool对象,提供了一种快捷的方法,赋予函数并行化处理一系列输入值的能力,可以将输入数据分配给不同进程处理(数据并行)
from multiprocessing import Pool
def f(x):
return x*x
if __name__ == '__main__':
with Pool(5) as p:
print(p.map(f, [1, 2, 3]))
我基于这个样例,想探索一下并行计算带来的性能提升
from dataclasses import dataclass
from multiprocessing import Pool
import re
import time
lst=[1, 2, 3]
res=[]
def f(x):
return x*x
def cal_time1():
starttime=time.time()
with Pool(5) as p:
print(p.map(f, [1, 2, 3]))
endtime=time.time()
return endtime-starttime
def cal_time2():
starttime=time.time()
for i in lst:
res.append(f(i))
print(res)
endtime=time.time()
return endtime-starttime
if __name__ == '__main__':
t1=cal_time1()
t2=cal_time2()
print("t1",t1)
print("t2:",t2)
if t1<t2:
print("t1 win!")
结果:
看起来multiprocess中带来的效果远比单进程要弱,这大概是因为数据本来就少,且不同进程算完合并的时间成本远大于计算的开销,所以在这个例子上多线程的好处完全没体现出来。
通过组里工作的驱动,自己尝试把一个方法写成multiprocess版本,目前思路大致如下:
判断该方法中是否存在可以拆分成多组同时计算的可能,这其中不涉及前后结果依赖或者读写的问题,如果可以那么这个方法应该可以写multiprocess版本。
找到要被拆分的那个计算对象,然后利用自己写的拆分规则将结点拆分成多组。
将要并行计算的部分塞到一个函数里,然后塞到partial中,设置好除了被分组的数据外所有变量。
partial的用法可以参考这篇博客:https://blog.csdn.net/qq_33688922/article/details/91890142
简单来说就是将一个已经定义好的函数,以及该函数中不变的参数放到partial中,然后partial会返回一个新的函数,我们以后都使用这个函数就可以了。
最后利用Pool及imap函数将partial定义好的函数与被分组的数据对象传进去。
下面代码中的n_workers代表进程池中进程数量,如果为None,默认按照cpu数量来。
关于imap其实就是一旦有结果了就可以拿,而不必等到所有进程完成再一次性拿所有结果,且仍保留输入的顺序。
注意点:
1.要切割的变量需要是一个列表类型,否则会报错,所以我这里让nodes_nbrs外包了一个list
2.将要并行的那部分代码取出来作为一个单独的函数,如果要使用imap的话不能再用yield了(会报错),否则双重懒加载就没有意义了…
if n_workers is not None:
import random
from functools import partial
from multiprocessing import Pool
_local_weighted_triangles_and_degree_iter_function = partial(
_local_weighted_triangles_and_degree_iter_parallel,
G=G,
weight=weight,
max_weight=max_weight,
)
nodes_nbrs = list(nodes_nbrs)
random.shuffle(nodes_nbrs)
if len(nodes_nbrs) > n_workers * 30000:
nodes_nbrs = split_len(nodes, step=30000)
else:
nodes_nbrs = split(nodes_nbrs, n_workers)
with Pool(n_workers) as p:
ret = p.imap(_local_weighted_triangles_and_degree_iter_function, nodes_nbrs)
for r in ret:
for x in r:
yield x
else: