使用multiprocessing.P时,Python核心使用速度较慢/低于100%

2024-04-16 07:24:14 发布

您现在位置:Python中文网/ 问答频道 /正文

在一个核上以100%的速度运行的代码,在多个核上以50%的速度运行时,实际上运行得比较慢。你知道吗

这个问题经常被问到,我找到的最好的线程(01)给出的答案是,“这是因为工作负载不够重,所以进程间通信(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秒花费的时间要多得多。Visual inspection of my system monitor证实了这一点(有点难说,但是multi(n)驼峰有两种颜色的阴影,表明来自两个不同的核的活动)。你知道吗

另外,corroborating video of the terminal outputs

即使有统一的工作负载,这仍然是IPC开销吗?这样的开销是不是只有在其他进程返回结果时才会支付,如果是的话,有没有办法在返回结果的同时避免这种开销?你知道吗


Tags: incoreforreturn进程defrangeres
1条回答
网友
1楼 · 发布于 2024-04-16 07:24:14

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

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

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

同时,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

Visual inspection of CPU activity证实了所注意到的加速;当通过Pool(12)运行12个进程时,有一个干净的、均匀的多核平台,明显地以100%的速度并行运行(而不是multi_r(n)的50%混合)。你知道吗

相关问题 更多 >