如何找到使用python多处理运行的理想并行进程数?
试图找出要运行的并行进程的正确数目 下面的脚本运行在一台8核32 GB(Ubuntu 18.04)的机器上。(只有系统进程和基本用户进程在运行,而下面的进程正在测试中。) 测试了如何找到使用python多处理运行的理想并行进程数?,python,python-3.x,parallel-processing,multiprocessing,python-3.6,Python,Python 3.x,Parallel Processing,Multiprocessing,Python 3.6,试图找出要运行的并行进程的正确数目 下面的脚本运行在一台8核32 GB(Ubuntu 18.04)的机器上。(只有系统进程和基本用户进程在运行,而下面的进程正在测试中。) 测试了多处理池和应用异步,包括以下各项: from multiprocessing import current_process, Pool, cpu_count from datetime import datetime import time num_processes = 1 # vary this print(f"
多处理池
和应用异步
,包括以下各项:
from multiprocessing import current_process, Pool, cpu_count
from datetime import datetime
import time
num_processes = 1 # vary this
print(f"Starting at {datetime.now()}")
start = time.perf_counter()
print(f"# CPUs = {cpu_count()}") # 8
num_procs = 5 * cpu_count() # 40
def cpu_heavy_fn():
s = time.perf_counter()
print(f"{datetime.now()}: {current_process().name}")
x = 1
for i in range(1, int(1e7)):
x = x * i
x = x / i
t_taken = round(time.perf_counter() - s, 2)
return t_taken, current_process().name
pool = Pool(processes=num_processes)
multiple_results = [pool.apply_async(cpu_heavy_fn, ()) for i in range(num_procs)]
results = [res.get() for res in multiple_results]
for r in results:
print(r[0], r[1])
print(f"Done at {datetime.now()}")
print(f"Time taken = {time.perf_counter() - start}s")
结果如下:
num_processes total_time_taken
1 28.25
2 14.28
3 10.2
4 7.35
5 7.89
6 8.03
7 8.41
8 8.72
9 8.75
16 8.7
40 9.53
以下几点对我来说很有意义:
- 每次运行一个进程大约需要0.7秒,因此运行40个进程大约需要28秒,这与我们上面观察到的一致
- 一次运行两个进程可以将时间减半,这一点可以在上面观察到(~14秒)
- 一次运行4个进程可以将时间进一步减半,这一点可以在上面观察到(~7秒)
- 将并行度增加到超过内核数(8)会降低性能(由于CPU争用),这是可以观察到的(某种程度上)
- 为什么并行运行8的速度不比并行运行4的速度快两倍,即为什么不是~3.5s
- 为什么一次并行运行5到8比一次运行4更糟糕?共有8个内核,但为什么总体运行时间更差?(当并行运行8时,
显示所有CPU的利用率接近100%。当并行运行4时,只有4个CPU的利用率达到100%,这是有道理的。)htop
你的操作系统上说是8核,但事实上SMT是4核。该任务显然受到CPU的限制,因此超出物理核数的任何增加都不会带来任何好处,只会带来多处理的开销成本。这就是为什么在达到(物理!)最大内核数(4)之前,性能几乎呈线性增长,然后在需要为这项CPU密集型任务共享内核时,性能下降。最有可能的原因是,您在使用CPU的CPU上运行该程序,即在英特尔设备上运行该程序。在wiki之后引用,对于物理上存在的每个处理器核心,操作系统寻址两个虚拟(逻辑)核心,并尽可能在它们之间共享工作负载。这就是这里发生的事情 你的操作系统上说是8核,但事实上SMT是4核。该任务显然受到CPU的限制,因此超出物理核数的任何增加都不会带来任何好处,只会带来多处理的开销成本。这就是为什么在达到(物理!)最大内核数(4)之前,性能几乎呈线性增长,然后在需要为这项CPU密集型任务共享内核时,性能下降的原因 Q:“为什么一次并行运行5到8个比一次运行4个更糟糕?” 嗯,
有几个原因,我们将从一个静态的、最容易观察到的原因开始: 由于硅设计(为此他们使用了一些硬件技巧)
的扩展范围不超过4 因此,上一次解释和提升的处理器加速比仅为
+1
递增计数为4,任何下一次+1都不会以{2,3,4}案例中观察到的相同方式递增性能:
此lstopo
CPU拓扑图有助于开始解码为什么(此处为4核,但逻辑与您的8核硅相同-在设备上运行lstopo
,以查看活体内的更多细节):
仔细观察,就像调用hwloc
-tool:lstopo no graphics-.ascii
一样,显示了在共享L1
-指令缓存的级别上,相互处理独立性结束(三级L3
一级也是共享的,但处于层次结构的顶端,其规模仅对大型问题解决者有影响,而不是我们的案例)
接下来是一个更糟糕的可观察的原因,为什么在8进程上更糟糕: Q:“为什么并行运行8的速度不比并行运行4的速度快两倍,即为什么它不是
~3.5s
?”
由于热管理。
CPU内核上的工作量越多,通过硅迷宫驱动电子在~3.5+GHz
上产生的热量就越多。热约束是那些阻止CPU计算能力进一步提高性能的因素,这仅仅是因为物理定律,正如我们所知道的em,不允许增长超过一些m物质定义的界限
那么接下来是什么?CPU设计绕过了物理(这是不可能的),而是我们,用户——向我们承诺了一款具有
~3.5+GHz
(但事实上,CPU只能在很短的时间内使用此时钟频率-直到散热使硅接近热极限-然后,CPU将决定降低自己的时钟频率作为过热防御步骤(这会降低性能,不是吗?)或者一些CPU微体系结构可能会跳转到另一个空闲的、因而更冷的CPU内核上(在那里保持较高的时钟速率(至少在一小段时间内),但也会降低性能,因为跳转不会在零时间内发生,也不会以零成本发生(缓存丢失、重新获取等)
这张图片显示了堆芯跳跃情况的快照-堆芯0-19
变得太热,并且在热节流帽下,
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Machine (31876MB) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ ┌───────────────────────────┐ │
│ │ Package P#0 │ ├┤╶─┬─────┼┤╶───────┤ PCI 10ae:1F44 │ │
│ │ │ │ │ │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │ │ ┌────────────┐ ┌───────┐ │ │
│ │ │ L3 (8192KB) │ │ │ │ │ renderD128 │ │ card0 │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │ │ └────────────┘ └───────┘ │ │
│ │ │ │ │ │ │
│ │ ┌──────────────────────────┐ ┌──────────────────────────┐ │ │ │ ┌────────────┐ │ │
│ │ │ L2 (2048KB) │ │ L2 (2048KB) │ │ │ │ │ controlD64 │ │ │
│ │ └──────────────────────────┘ └──────────────────────────┘ │ │ │ └────────────┘ │ │
│ │ │ │ └───────────────────────────┘ │
│ │ ┌──────────────────────────┐ ┌──────────────────────────┐ │ │ │
│ │ │ L1i (64KB) │ │ L1i (64KB) │ │ │ ┌───────────────┐ │
│ │ └──────────────────────────┘ └──────────────────────────┘ │ ├─────┼┤╶───────┤ PCI 10bc:8268 │ │
│ │ │ │ │ │ │
│ │ ┌────────────┐┌────────────┐ ┌────────────┐┌────────────┐ │ │ │ ┌────────┐ │ │
│ │ │ L1d (16KB) ││ L1d (16KB) │ │ L1d (16KB) ││ L1d (16KB) │ │ │ │ │ enp2s0 │ │ │
│ │ └────────────┘└────────────┘ └────────────┘└────────────┘ │ │ │ └────────┘ │ │
│ │ │ │ └───────────────┘ │
│ │ ┌────────────┐┌────────────┐ ┌────────────┐┌────────────┐ │ │ │
│ │ │ Core P#0 ││ Core P#1 │ │ Core P#2 ││ Core P#3 │ │ │ ┌──────────────────┐ │
│ │ │ ││ │ │ ││ │ │ ├─────┤ PCI 1002:4790 │ │
│ │ │ ┌────────┐ ││ ┌────────┐ │ │ ┌────────┐ ││ ┌────────┐ │ │ │ │ │ │
│ │ │ │ PU P#0 │ ││ │ PU P#1 │ │ │ │ PU P#2 │ ││ │ PU P#3 │ │ │ │ │ ┌─────┐ ┌─────┐ │ │
│ │ │ └────────┘ ││ └────────┘ │ │ └────────┘ ││ └────────┘ │ │ │ │ │ sr0 │ │ sda │ │ │
│ │ └────────────┘└────────────┘ └────────────┘└────────────┘ │ │ │ └─────┘ └─────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │ └──────────────────┘ │
│ │ │
│ │ ┌───────────────┐ │
│ └─────┤ PCI 1002:479c │ │
│ └───────────────┘ │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘