这是我为并行创建字典而编写的一些代码
parallelize.py
if __name__ == "__main__":
import time
from multiprocessing import Pool
def assign_dict(alist):
return {x:x for x in alist}
my_dict = {}
size = 10000000
threshold=10000000
my_list=list(range(size))
start=time.time()
my_dict=assign_dict(my_list)
end=time.time()
print("check seq",end-start, " sec")
my_dict = {}
chunks = [my_list[i*threshold:(i+1)*threshold] for i in range(int(size/threshold))]
process_count = 7
pool = Pool(processes=process_count)
start = time.time()
inter_list = pool.map_async(assign_dict, chunks)
inter_list.wait()
inter_list=inter_list.get()
for some_dict in inter_list:
print("Combining...", time.time()-start, " sec elapsed")
my_dict = {**my_dict, **some_dict}
print("check 152002 as key ",my_dict[152002])
end=time.time()
print("check parallel",end-start, " sec")
这是大小为100万,阈值为100万的输出
check seq 0.6559352874755859 sec
Combining... 4.751460790634155 sec elapsed
check 152002 as key 152002
check parallel 5.064720869064331 sec
此处输出的大小为1000万,阈值为100万
check seq 0.668889045715332 sec
Combining... 1.6871337890625
Combining... 1.7269806861877441
Combining... 1.860083818435669
Combining... 2.0794677734375
Combining... 2.266465663909912
Combining... 2.47836971282959
Combining... 2.8915648460388184
Combining... 3.2443037033081055
Combining... 3.6063129901885986
Combining... 3.9933629035949707
check 115202 as key 1152002
check parallel 4.406447887420654 sec
这是大小为1亿,阈值为10百万的输出,这里最糟糕的部分甚至是在合并之前,与map_async
相比,55 secs
依旧需要19 secs
。
check seq 19.182615041732788 sec
Combining... 55.18172788619995
Combining... 56.38586497306824
Combining... 58.534785747528076
Combining... 61.805513858795166
Combining... 64.75091290473938
Combining... 71.34392070770264
Combining... 76.02847385406494
Combining... 81.2545096874237
Combining... 87.75674867630005
Combining... 109.01232576370239
check 115202 as key 1152002
check parallel 126.1939218044281 sec
尽管我尝试了size
和threshold
的各种组合,但是带有pool的代码总是比较慢,因此,由于顺序版本运行非常快,因此阈值并不是太大。即使size
与threshold
相同,带有pool的代码也要慢很多秒。
即使对于大小= 10亿的长期运行作业,并行化版本也比顺序执行慢得多,这意味着并行化没有任何好处。我有8个核心和16GB RAM,我正在运行MacOSX,甚至还验证了我的核心在活动监视器中并行运行以执行任务,但速度较慢。如图所示,合并阶段不会花费很多时间。在inter_list.get()
命令结束时,并行部分已经完成。因此它不会干扰字典的组合。
任何人都可以将这段代码并行化以使其比Python 3中的顺序版本更快,或者至少可以帮助我理解为什么会这样吗?
答案 0 :(得分:5)
您的多处理版本比顺序版本慢,因为需要进行进程间通信,以将Pool.map
的结果从工作人员传回给工作人员所分叉的过程。
由于GIL,建议使用Python的多处理库来执行CPU密集型并行任务。但是,这意味着Pool
中每个工作程序的虚拟内存地址空间是不同的,因此Pool.map
的结果必须被序列化并在进程之间传递。因为Pool.map
的结果是如此之大,所以这意味着您的程序要花费大量时间对答案进行序列化/反序列化并将它们在进程之间传递。在顺序版本中,只有一个进程,因此结果永远不需要序列化并在进程之间传递然后再反序列化,这可能就是在这种情况下运行速度更快的原因。
如果要避免出现此瓶颈,则将尝试使用Python的Shared Memory array来避免进程间的通信瓶颈,因为该数组将位于所有工作进程的相同虚拟地址空间中。如果您确实需要键值对映射,请查看Python的multiprocessing.Manager.dict
。
通常,Pool.map
可以并行化一些不会产生大量数据的计算。