Python 多处理正在降低性能

Python multiprocessing is deacreasing performance

提问人:Mi-Shell 提问时间:11/10/2023 最后编辑:Mi-Shell 更新时间:11/10/2023 访问量:80

问:

为了理解并行化,我在模块多处理上编写了一个玩具脚本。我绘制了下图,结论是我的实现通过添加多个进程使性能变差。 例如,我的代码创建了一个包含 10 000 个元素的列表,然后并行计算其元素的总和。每个进程都必须在其自己的子列表中执行元素的总和。

使用 1 个进程,需要 0.78 秒来计算总和。2 个进程,1,29 秒,3 个进程,1,93 秒。看我的图表:

进程数与计算时间

法典:

import multiprocessing
import time
import math
import numpy as np

def sum_list(mylist,result,index_min,index_max):
    for i in range(index_min,index_max):
        result.value += mylist[i]

if __name__ == "__main__": 
    f = lambda x: np.sin(x)*np.cos(x)
    X = np.linspace(0,1,10000)
    mylist=f(X)  #creating the list to sum
    size = len(mylist)
    
    result = multiprocessing.Value('d')
    result.value = 0
    processes = []
    n = 10

    #creating processes
    start = time.perf_counter()
    for p in range(n):
        index_min = int(p*size/n)
        index_max = int((p+1)*size/n)
        print(index_min,index_max)
        processes.append( multiprocessing.Process(target = sum_list,args = (mylist,result,index_min,index_max)) ) 
    #starting processes
    for process in processes:
        process.start()
        
    #end processes
    for process in processes:
        process.join()
    end = time.perf_counter()
    print('time ellapsed :', end-start, 'seconds. ')
    

显然,由于列表的大小是固定的,我希望进程只有一个小的子列表要求和,并且由于它们同时执行,因此总计算时间应该会减少。

我尝试了很多不同数量的进程和两种不同的硬件架构,但问题就在那里。我错过了什么吗?是因为每次一个进程必须写入结果时,它都必须与其他进程通信,从而降低性能吗?

谢谢你的回答。

多处理 python-multiprocessing

评论

0赞 Daviid 11/10/2023
也许相关,stackoverflow.com/questions/71052227/......
0赞 Abdul Aziz Barkat 11/10/2023
你为什么在那里使用一个作为结果?鉴于你试图得到总和,你不能把所有过程的结果相加吗?使用该共享内存涉及同步,并且进程可能花费更多时间执行此操作,而不是实际求和。multiprocessing.Value
0赞 Ahmed AEK 11/10/2023
在 Windows 上启动进程是一项非常缓慢的任务,仅启动 Python 解释器就需要一些时间,这就是为什么建议改用 Python 解释器的原因,因此您只需支付一次启动时间费用,并在许多函数调用中重用池。Process Pool
0赞 Ahmed AEK 11/10/2023
Y 轴不是计算时间,整个计算在 5 毫秒内完成。the time to start N python interpreters
0赞 Mi-Shell 11/13/2023
是的,我最后使用了,我也在每个过程中使用了,如下面的解决方案所示。现在工作得很好,性能仍然比将整个列表相加差,但它非常接近,并且计算时间不会像我之前的图表那样线性增加。Process Poolsumsum

答:

3赞 jsbueno 11/10/2023 #1

“Python 多处理正在降低性能”

是的。

还有其他问题吗?

(对不起,开玩笑)

与多线程或线程内并发相比,启动多进程本身在系统资源方面成本很高。然后,从外部进程传输的所有数据,即使返回值由实例封装,也涉及一些其他资源。(通常序列化和反序列化传递的每个数据项 + 同步代码)。multiprocessing.Value()

因此,值得使用“实际工作负载”,或者设计良好的演示工作负载,这些工作负载实际上将利用系统资源。

更具体地说,你的“工作负载”实际上是在后台这样做的:向管理器进程发送信号 多处理生成以协调共享-多处理数据,请求其当前 ,并且 sum 操作本身实际上是在这个管理器进程中执行的(无论你做了多少个工作进程: 为了保证算子的原子性质,总和是在单个进程中执行的, 并在锁下阻止其他进程实际并行运行。你这样做是为了你所有的价值观。result.value += mylist[i]result.value+=

更改您的工作代码以一次将其所有份额相加,并在每个进程中增加一次值,您应该会得到很大的改进。不过,操作仍然足够简单,多处理开销可能仍大于您的收益。result.valuesum

不过,值得一提的是:显而易见的收获是,多进程能够以一种直接的方式同时在多个 CPU 内核中运行 Python 代码 - 使用 GIL(全局解释器锁),这在 Python 3.12 之前的版本中是不可能的(并且在 Python 3.12 中使用 Python 解释器实例很难实现)。

我的建议是,既然你正在玩这个,那就继续玩,直到你对使用线程、多进程和生成其中的 2 或 3 个线程(或 20-30 个)以及具有数百万个项目的工作负载之间的区别有深刻的了解。

另一个需要调整和调整的参数是将多处理启动方法更改为 - https://docs.python.org/3/library/multiprocessing.html#multiprocessing.set_start_method - Mac OS 和 Windows 的默认值是“Spawn” - 您应该通过使用“fork”(在 Windows 上不可用)来获得更好的性能multiprocessing.set_start_method

此外,在 Python 3.12 中,您可以尝试使用子解释器 - 在 Mac 或 Linux 中,您可以安装“额外解释器”包(警告:此阶段的 alpha 软件),或使用(在任何操作系统上)并按照 https://peps.python.org/pep-0554/ 上的文档进行操作import _xxsubinterpreters as interpreters

(免责声明:我是包的作者)extrainterpreters