簡體   English   中英

如何找到理想數量的並行進程以使用 python 多處理運行?

[英]How to find ideal number of parallel processes to run with python multiprocessing?

試圖找出使用python multiprocessing運行的正確數量的並行進程。

以下腳本在 8 核 32 GB (Ubuntu 18.04) 機器上運行。 (以下測試時只有系統進程和基本用戶進程在運行。)

使用以下內容測試了multiprocessing.Poolapply_async

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 秒,這與我們上面觀察到的一致。
  • 一次運行 2 個進程應該將時間減半,這在上面觀察到(~14 秒)。
  • 一次運行 4 個進程應該進一步將時間減半,這在上面觀察到(~7s)。
  • 將並行度增加到超過內核數 (8) 應該會降低性能(由於 CPU 爭用),這是可以觀察到的(有點)。

沒有意義的是:

  • 為什么並行運行 8 的速度不是並行運行 4 的兩倍,即為什么不是 ~3.5s?
  • 為什么一次並行運行 5 到 8 個比一次運行 4 個更糟糕? 有 8 個核心,但為什么整體運行時間更差? (當並行運行 8 個時, htop顯示所有 CPU 的利用率接近 100%。當並行運行 4 個時,其中只有 4 個處於 100%,這是有道理的。)

為什么一次並行運行 5 到 8 個比一次運行 4 個更糟糕?”

好吧,有幾個原因,我們將從一個靜態的、最容易觀察到的原因開始:

由於硅設計(他們為此使用了一些硬件技巧)沒有擴展到超過 4.

因此,最后一個阿姆達爾定律解釋並促進了僅+1升級處理器數量的加速是 4 並且任何下一個 +1 都不會以在 { 2, 3, 4 }-case 中觀察到的相同方式提升性能:

lstopo CPU 拓撲圖有助於開始解碼WHY (此處為 4 核,但邏輯與您的 8 核芯片相同 - 在您的設備上運行lstopo以查看更多體內細節):

┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 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 │                       │
│                                                                           └───────────────┘                       │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

仔細觀察,比如調用hwloc -tool: lstopo-no-graphics -.ascii ,顯示了相互處理獨立性在哪里結束- 這里是共享L1指令緩存的級別( L3也是共享的,但是在層次結構的頂部,並且規模如此之大,僅困擾大型問題解決者,而不是我們的情況)


接下來是一個更糟糕的可觀察到的原因,為什么在 8-processes 上更糟糕

“為什么並行運行 8 的速度沒有並行運行 4 的兩倍,即為什么不是~3.5s ?”

因為熱管理

在此處輸入圖像描述

加載到 CPU 內核上的工作越多,通過硅迷宮驅動~3.5+ GHz的電子產生的熱量就越多。 熱限制是那些阻止 CPU 計算能力進一步提高性能的限制,僅僅是因為我們所知道的物理定律不允許超出某些材料定義的限制。

那么接下來會發生什么?
CPU 設計沒有繞過物理(那是不可能的),而是我們,用戶 - 通過向我們承諾一個具有~3.5+ GHz的 CPU 芯片(但實際上,CPU 只能將這個時鍾頻率用於少量時間 - 直到散發的熱量沒有使硅接近熱限制 - 然后,CPU 將決定降低自己的時鍾速率作為過熱防御步驟(這會降低性能,不是嗎?)或者一些 CPU 微架構可能會跳躍(移動處理流程)到另一個免費的、因此更酷的 CPU 內核(它承諾在那里提供更高的時鍾速率(至少在一小段時間內)但也降低了性能,因為躍點不會在零時間發生並且不會以零成本發生(緩存丟失,重新獲取等)

這張圖片顯示了核心跳躍情況的快照 - 核心0-19過熱並且處於熱節流上限之下,而核心20-39可以(至少目前)全速運行:

在此處輸入圖像描述


結果?

兩種熱約束(將 CPU 潛水到液氮池中已為“流行”雜志展示,但對於任何可持續計算來說都不是一個合理的選擇,因為從深度冷凍狀態到6+ GHz的機械應力時鍾頻率的蒸汽形成過熱器會破壞 CPU 的主體,並會導致 CPU 因裂縫和機械疲勞而死亡,但在少數工作負載中 - 所以這是一個禁區,因為任何嚴肅項目的ROI 都是負的) .

基於體內預測試的員工池的良好冷卻和合適的規模是這里唯一確定的賭注。

其他架構:

在此處輸入圖像描述

最可能的原因是您在使用同時多線程 (SMT)的 CPU 上運行程序,在 Intel 單元上更廣為人知的是超線程 在 wiki 之后引用,對於物理上存在的每個處理器內核,操作系統會尋址兩個虛擬(邏輯)內核並在可能的情況下在它們之間共享工作負載。 這就是這里發生的事情。

您的操作系統說 8 個內核,但實際上它是 4 個帶 SMT 的內核。 該任務顯然受 CPU 限制,因此任何超出物理內核數量的增加都不會帶來任何好處,只會帶來多處理的開銷成本。 這就是為什么您會看到性能幾乎呈線性增長,直到達到(物理!)最大值。 核心數量 (4),然后在需要為這個 CPU 密集型任務共享核心時減少。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM