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))
如果您的工作不是纯粹的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约束的任务,您将观察到默认cpu和双cpu之间几乎没有区别。事情发生了,两次测试都有相同的最佳时间。
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秒。
如果您的任务是I/O绑定的(例如等待数据库、网络服务(,那么使线程数量超过处理器数量实际上会增加吞吐量。
这是因为当线程在等待I/O时,处理器实际上可以在其他线程上执行工作。
如果你有一个CPU繁重的任务,那么更多的处理器实际上会减慢它的速度。