使用的工作进程多于核心

时间:2018-12-04 14:41:57

标签: python python-3.x parallel-processing multiprocessing python-multiprocessing

This的示例来自PYMOTW,给出了一个使用multiprocessing.Pool()的示例,其中传递的processes参数(工作进程数)是计算机上内核数的两倍。

pool_size = multiprocessing.cpu_count() * 2

(否则该类将默认为cpu_count()。)

这是否有效?创造比核心更多的工人有什么作用?是否有这样做的理由,还是会在错误的方向上施加额外的开销?我很好奇为什么在我认为是信誉良好的网站的示例中始终将其包含在内。

在初始测试中,它实际上似乎使速度变慢:

$ python -m timeit -n 25 -r 3 'import double_cpus; double_cpus.main()'
25 loops, best of 3: 266 msec per loop
$ python -m timeit -n 25 -r 3 'import default_cpus; default_cpus.main()'
25 loops, best of 3: 226 msec per loop

double_cpus.py

import multiprocessing

def do_calculation(n):
    for i in range(n):
        i ** 2

def main():
    with multiprocessing.Pool(
        processes=multiprocessing.cpu_count() * 2,
        maxtasksperchild=2,
    ) as pool:
        pool.map(do_calculation, range(1000))

default_cpus.py

def main():
    # `processes` will default to cpu_count()
    with multiprocessing.Pool(
        maxtasksperchild=2,
    ) as pool:
        pool.map(do_calculation, range(1000))

2 个答案:

答案 0 :(得分:2)

如果您的任务受I / O约束(例如,等待数据库,网络服务),则使线程数超过处理器数实际上会提高吞吐量。

这是因为在线程等待I / O时,处理器实际上可以在其他线程上工作。

如果您有大量的CPU工作,那么实际上更多的处理器会降低它的速度。

答案 1 :(得分:2)

如果您的工作不是完全 cpu绑定的,那么这样做可能很有意义,而且还涉及一些I / O。

您的示例中的计算对于合理的基准测试来说也太短了,首先要创建更多进程的开销占了主导。

我修改了您的计算方法,使其在10M的范围内进行迭代,同时计算了if条件,并让其小睡一会儿以防其评估为True,发生了n_sleep次。 这样,可以将sleep_sec_total的总睡眠时间注入计算中。

# default_cpus.py
import time
import multiprocessing


def do_calculation(iterations, n_sleep, sleep_sec):
    for i in range(iterations):
        if i % (iterations / n_sleep) == 0:
            time.sleep(sleep_sec)


def main(sleep_sec_total):

    iterations = int(10e6)
    n_sleep = 100
    sleep_sec = sleep_sec_total / n_sleep
    tasks = [(iterations, n_sleep, sleep_sec)] * 20

    with multiprocessing.Pool(
        maxtasksperchild=2,
    ) as pool:
        pool.starmap(do_calculation, tasks)

# double_cpus.py
...

def main(sleep_sec_total):

    iterations = int(10e6)
    n_sleep = 100
    sleep_sec = sleep_sec_total / n_sleep
    tasks = [(iterations, n_sleep, sleep_sec)] * 20

    with multiprocessing.Pool(
        processes=multiprocessing.cpu_count() * 2,
        maxtasksperchild=2,
    ) as pool:
        pool.starmap(do_calculation, tasks)

对于两个模块,我都使用sleep_sec_total=0(完全受CPU约束)和sleep_sec_total=2进行了基准测试。

结果为sleep_sec_total=0

$ python -m timeit -n 5 -r 3 'import default_cpus; default_cpus.main(0)'
5 loops, best of 3: 15.2 sec per loop

$ python -m timeit -n 5 -r 3 'import double_cpus; double_cpus.main(0)'
5 loops, best of 3: 15.2 sec per loop

给定一个合理的计算大小,对于纯CPU绑定任务,您会发现default-cpus和double-cpus之间几乎没有区别。碰巧,这两个测试的最佳时间相同。

结果为sleep_sec_total=2

$ python -m timeit -n 5 -r 3 'import default_cpus; default_cpus.main(2)'
5 loops, best of 3: 20.5 sec per loop
$ python -m timeit -n 5 -r 3 'import double_cpus; double_cpus.main(2)'
5 loops, best of 3: 17.7 sec per loop

现在,通过添加2秒钟的睡眠作为I / 0的虚拟对象,图片看起来有所不同。与默认值相比,使用两倍的进程可以使速度提高约3秒。