通过 multiprocessing.pool 的 Python3 并行代码比顺序代码慢

标签 python python-3.x

这是我为并行创建字典而编写的一些代码

并行化.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")

这是大小为 1 mil 且阈值为 1 mil 的输出

check seq 0.6559352874755859  sec
Combining... 4.751460790634155  sec elapsed
check 152002 as key  152002
check parallel 5.064720869064331  sec

这是 size = 10mil 的输出,阈值为 1mil

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 亿、阈值为 1000 万的输出,这里最糟糕的部分甚至在组合之前,map_async仍然需要 55 secs19 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带池的代码总是比较慢,所以并不是阈值太大,因为顺序版本运行很快。即使当 sizethreshold 相同,带池的代码慢了很多秒。

即使对于像 size = 1000 million 这样的长时间运行的作业,并行化版本也比顺序执行慢得多,这意味着并行化没有任何好处。我有 8 个内核和 16GB RAM,我正在运行 MacOSX,我什至验证了我的内核在事件监视器中并行运行以执行任务,但速度较慢。如图所示,组合阶段不会花费太多时间。到时候 inter_list.get()命令结束,并行部分已经完成。所以它不会干扰字典的合并。

任何人都可以并行化此代码以使其比 Python 3 中的顺序版本更快,或者至少帮助我理解为什么会发生这种情况?

最佳答案

您的多处理版本比顺序版本慢,因为进程间通信需要将来自 Pool.map 的结果从工作人员传递回工作人员派生的进程。

由于 GIL,Python 的多处理库是 cpu 密集型并行任务的建议方法。但是,这意味着Pool 中每个worker 的虚拟内存地址空间是不同的,因此Pool.map 的结果必须序列化并在进程之间传递。因为您的 Pool.map 的结果是一个非常大的数组,这意味着您的程序花费大量时间序列化/反序列化答案并在进程之间传递它们。在顺序版本中,只有一个进程,因此结果永远不需要序列化并在进程之间传递然后反序列化,这可能是它在这种情况下运行得更快的原因。

如果你想避免这个瓶颈,你会想尝试使用 Python 的 Shared Memory array这可以避免进程间通信瓶颈,因为数组将位于所有工作进程的相同虚拟地址空间中。如果你真的需要一个键值对映射,那么看看 Python 的 multiprocessing.Manager.dict .

一般来说,Pool.map 适合并行化一些不会产生大量数据的计算。

关于通过 multiprocessing.pool 的 Python3 并行代码比顺序代码慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52654160/

相关文章:

python - 你如何使用嵌套的 for 循环在 python 中打印出以下模式?

python - 使用Python获取DNS解析时间和响应时间

python - 如何使用 pil 将没有白色背景(透明?)的 Logo 圆角化?

Python - curve_fit 给出不正确的系数

python-3.x - 将Tkinter转换为exe

python-3.x - Python代码没有抛出错误,但所需的输出不一样

python - 类型错误 : argument of type 'int' is not iterable

python - urlencode python中的多维字典

python - Python列表支持 float 吗?什么时候需要整数而浮点却不能?

python - 如果我们创建的线程数量超出 CPU 的承受能力会怎样?操作系统是否处理它? : Python - Threading