为什么这里的多处理速度较慢?

时间:2020-05-19 21:16:11

标签: python multiprocessing

我正在尝试通过Python中的多处理来加快某些代码的速度,但是我无法理解其中的一点。假设我有以下愚蠢的功能:

import time
from multiprocessing.pool import Pool

def foo(_):
    for _ in range(100000000):
        a = 3 

当我在笔记本电脑(英特尔-8核cpu)上不使用多处理功能(请参见下面的代码)运行此代码时,所需时间约为2.31秒。

t1 = time.time()
foo(1)
print(f"Without multiprocessing {time.time() - t1}")

相反,当我使用Python多处理库(请参见下面的代码)运行此代码时,所花的时间约为6.0秒。

pool = Pool(8)
t1 = time.time()
pool.map(foo, range(8))
print(f"Sample multiprocessing {time.time() - t1}")

据我所知,我了解到使用多处理时会产生一些时间开销,这主要是由于需要产生新进程并复制内存状态所致。但是,此操作应在刚开始时最初生成处理对象时执行一次,并且不应太大。

那么我在这里想念的是什么?我的推理有问题吗?

编辑:我认为最好是对我的问题更明确。我在这里期望的是多进程代码比顺序代码要慢一些。的确,我没有将整个工作分配到8个内核中,但是我正在并行使用8个内核 (因此,在理想的情况下,处理时间应或多或少保持原样)。考虑到产生新流程的开销,我预计总时间会增加一些(不是太大)的百分比,但是在我到达这里时不会增加〜2.60倍。

2 个答案:

答案 0 :(得分:9)

嗯,多重处理不可能使速度更快:您没有将工作划分为8个流程,而是要求8个流程中的每一个都完成整个工作。不使用多处理,每个过程至少需要花费您的代码一次的时间。

因此,如果多处理根本无济于事,那么您希望它花费的时间大约是单处理器运行时间的8倍(工作量的8倍!)。但是您说它不需要花费2.31 * 8〜= 18.5秒,而是“仅”大约6秒。因此,您获得的提升比3倍加速要好。

为什么不超过?从这里无法猜测。这将取决于您的计算机有多少个物理核心,以及您同时运行多少其他东西。对于该特定功能,每个进程都将100%受CPU限制,因此“逻辑”内核的数量几乎无关紧要-处理器超线程帮助的机会很少。所以我猜您有4个物理核心。

在我的盒子上

我的机器上有8个逻辑核心,但只有4个物理核心的示例计时,否则让盒子显得很安静:

Without multiprocessing 2.468580484390259
Sample multiprocessing 4.78624415397644

如上所述,这些都没有让我感到惊讶。实际上,我 对该程序用尽了机器的真实容量的效率感到有些惊讶(但令人愉快)。

答案 1 :(得分:1)

@TimPeters已经回答您实际上只是在8个Pool子进程中运行了8次作业,所以它变慢而不是变快。

这可以回答问题,但不能真正回答您真正的内在问题。令您惊讶的是,您很清楚地看到,您期望将单个作业以某种方式自动拆分并在8个Pool流程中分批运行。那不是它的工作方式。您必须内置/告诉它如何分散工作。

需要以不同的方式细分不同种类的工作,但是为了继续进行示例,您可以执行以下操作:

import time
from multiprocessing.pool import Pool

def foo(_):
    for _ in range(100000000):
        a = 3 

def foo2(job_desc):
    start, stop = job_desc
    print(f"{start}, {stop}")

    for _ in range(start, stop):    
        a = 3 

def main():
    t1 = time.time()
    foo(1)
    print(f"Without multiprocessing {time.time() - t1}")

    pool_size = 8
    pool = Pool(pool_size)

    t1 = time.time()

    top_num = 100000000
    size = top_num // pool_size
    job_desc_list = [[size * j, size * (j+1)] for j in range(pool_size)]
    # this is in case the the upper bound is not a multiple of pool_size
    job_desc_list[-1][-1] = top_num

    pool.map(foo2, job_desc_list)
    print(f"Sample multiprocessing {time.time() - t1}")


if __name__ == "__main__":
    main()

这将导致:

Without multiprocessing 3.080709171295166
0, 12500000
12500000, 25000000
25000000, 37500000
37500000, 50000000
50000000, 62500000
62500000, 75000000
75000000, 87500000
87500000, 100000000
Sample multiprocessing 1.5312283039093018

如此所示,将作业拆分确实可以节省更多时间。加速将取决于CPU的数量。在受CPU限制的作业中,应尝试将其池大小限制为CPU数量。我的笔记本电脑具有更多的CPU,但是部分好处却被开销所抵消。如果工作时间更长,这应该看起来更有用。