Python核心使用速度较慢/低于100%的多处理.池



在一个内核 @ 100% 上运行的代码在多处理时实际上运行得更慢,它在多个内核上运行 @ ~50%。

这个问题经常被问到,我找到的关于它的最佳线程(0,1(给出了答案,"这是因为工作负载不够重,所以进程间通信(IPC(开销最终会让事情变慢。

我不知道这是否正确,但是我已经隔离了一个示例,其中这种情况在相同的工作负载中发生并且不会发生,我想知道这个答案是否仍然适用或为什么它实际上会发生:

from multiprocessing import Pool
def f(n):
    res = 0
    for i in range(n):
        res += i**2
    return res

def single(n):
    """ Single core """
    for i in range(n):
        f(n)

def multi(n):
    """ Multi core """
    pool = Pool(2)
    for i in range(n):
        pool.apply_async(f, (n,))
    pool.close()
    pool.join()
def single_r(n):
    """ Single core, returns """
    res = 0
    for i in range(n):
        res = f(n) % 1000 # Prevent overflow
    return res

def multi_r(n):
    """ Multi core, returns """
    pool = Pool(2)
    res = 0
    for i in range(n):
        res = pool.apply_async(f, (n,)).get() % 1000
    pool.close()
    pool.join()
    return res
# Run
n = 5000
if __name__ == "__main__":
    print(f"single({n})...", end='')
    single(n)
    print(" DONE")
    print(f"multi({n})...", end='')
    multi(n)
    print(" DONE")
    print(f"single_r({n})...", end='')
    single_r(n)
    print(" DONE")
    print(f"multi_r({n})...", end='')
    multi_r(n)
    print(" DONE")

工作负载f()

f()以单核和双核运行,无需通过single()multi()进行回访。

然后f()运行单核和双核,通过single_r()multi_r()进行回访。

我的结果是,当f()通过回声多处理运行时,速度会变慢。没有回报,它就不会发生。

所以single()需要q秒。 multi()要快得多。好。然后single_r()需要 q 秒。但是multi_r()需要的不仅仅是q秒。对我的系统监视器的目视检查证实了这一点(有点难以分辨,但驼峰multi(n)有两种颜色,表明来自两个不同内核的活动(。

此外,终端输出的佐证视频

即使工作负载统一,这仍然是 IPC 开销吗?是否仅在其他进程返回其结果时才支付此类开销,如果是,是否有办法在返回结果的同时避免它?

正如 Darkonaut 指出的那样,在 multi_r() 中使用多个进程时速度变慢是因为get()调用阻塞:

for i in range(n):
        res = pool.apply_async(f, (n,)).get() % 1000

这有效地按顺序或并发运行工作负载(更类似于多线程(,同时增加多进程开销,使其运行速度比单核等效single_r()慢!

同时,multi()运行得更快(即正确并行运行(,因为它不包含get()调用。

要并行运行返回结果,请先收集结果对象,如下所示:

def multi_r_collected(n):
    """ Multi core, collects apply_async() results before returning them """
    pool = Pool(2)
    res = 0
    res = [pool.apply_async(f, (n,)) for i in range(n)] # Collect first!
    pool.close()
    pool.join()
    res = [r.get() % 1000 for r in res] # .get() after!
    return res

对CPU活动的目视检查证实了注意到的加速;当通过Pool(12)运行12个进程时,有一个干净,均匀的多个内核台面明显以100%并行运行(而不是multi_r(n)的50%大杂烩(。

最新更新