[英]Python: multiprocessing, 8/24 cores loaded
我有一台機器有24個物理內核 (至少我被告知)運行Debian: Linux 3.2.0-4-amd64 #1 SMP Debian 3.2.68-1+deb7u1 x86_64 GNU/Linux
。 這似乎是正確的:
usr@machine:~/$ cat /proc/cpuinfo | grep processor
processor : 0
processor : 1
<...>
processor : 22
processor : 23
嘗試使用Python的multiprocessing.pool.Pool
加載所有內核時遇到了一些問題。 我用了Pool(processes=None)
; 文檔說如果提供None
,Python使用cpu_count()
。
唉, 只有8個核心100%加載 ,其他核心仍處於空閑狀態(我使用htop
來監控CPU負載)。 我以為我無法正確地烹飪Pools
並嘗試“手動”調用24個進程:
print 'Starting processes...'
procs = list()
for param_set in all_params: # 24 items
p = Process(target=_wrap_test, args=[param_set])
p.start()
procs.append(p)
print 'Now waiting for them.'
for p in procs:
p.join()
我從我開始的流程中收到了24條“問候”消息:
Starting processes...
Executing combination: Session len: 15, delta: 10, ratio: 0.1, eps_relabel: 0.5, min_pts_lof: 5, alpha: 0.01, reduce: 500
< ... 22 more messages ... >
Executing combination: Session len: 15, delta: 10, ratio: 0.1, eps_relabel: 0.5, min_pts_lof: 7, alpha: 0.01, reduce: 2000
Now waiting for them.
但仍然只加載了8個核心 :
我在這里看到, numpy
,OpenBLAS和多核執行可能存在問題。 這就是我啟動代碼的方式:
OPENBLAS_MAIN_FREE=1 python -m tests.my_module
在完成所有進口之后我做了:
os.system("taskset -p 0xff %d" % os.getpid())
所以,問題是:我應該怎樣做才能在所有內核上實現100%的負載? 這只是我糟糕的Python使用情況還是與多核計算機上的操作系統限制有關?
更新 :另一個有趣的事情是htop
輸出中的一些不一致。 如果你看一下上面的圖像,你會發現CPU負載條下面的表顯示了超過8個內核的30-50%負載,這與負載條所說的完全不同。 然后, top
似乎同意這些條:8個核心100%負載,其他閑置。
再次更新:
當我在所有導入后添加os.system("taskset -p 0xff %d" % os.getpid())
行時,我在SO上使用了這個相當受歡迎的帖子 。 我必須承認,當我這樣做時,我並沒有想太多,特別是在閱讀之后:
在模塊導入后粘貼此行,我的示例現在在所有核心上運行
我是一個單純的男人。 我看到“像魅力一樣”,我復制並粘貼。 無論如何,在玩我的代碼時,我最終刪除了這一行。 之后,我的代碼開始在所有24個核心上執行“手動” Process
啟動方案。 對於Pool
場景,無論是否使用了關聯技巧,仍然存在同樣的問題。
我認為這不是一個真正的答案,因為我不知道Pool
的問題是什么,但至少我設法讓所有核心都滿載。 謝謝!
即使你解決了這個問題,我也會嘗試解釋它以澄清這些想法。
對於我所讀到的內容,numpy為提高性能做了很多“魔術”。 其中一個神奇的技巧是設置進程的CPU親和力。
CPU親和性是OS調度程序的優化。 它基本上強制給定的進程始終在同一CPU核心上運行。
這樣可以提高性能,減少CPU緩存失效的次數,並提高參考局部性的優勢。 在高計算任務中,這些因素確實很重要。
我不喜歡numpy的事實是它隱含地做了所有這些。 經常讓開發人員感到困惑。
您的進程未在所有核心上運行的事實是由於numpy在導入模塊時為父進程設置了親和關系。 然后,當您生成新進程時,將繼承關聯,導致所有進程爭用少數核心,而不是有效地使用所有可用的進程。
os.system("taskset -p 0xff %d" % os.getpid())
命令指示操作系統在解決問題的所有核心上設置關聯。
如果你想看到它在Pool上工作,你可以做以下技巧。
import os
from multiprocessing import Pool
def set_affinity_on_worker():
"""When a new worker process is created, the affinity is set to all CPUs"""
print("I'm the process %d, setting affinity to all CPUs." % os.getpid())
os.system("taskset -p 0xff %d" % os.getpid())
if __name__ == '__main__':
p = Pool(initializer=set_affinity_on_worker)
...
在os.system("taskset -p 0xff %d" % os.getpid())
, 0xff
本質上是十六進制位掩碼,對應於1111 1111.位掩碼中的每個位對應一個CPU內核。 位值1表示可以在相應的CPU內核上執行該過程。 因此,要在24個內核上運行,您應該使用0xffffff而不是0xff的掩碼。
正確的命令:
os.system("taskset -p 0xffffff %d" % os.getpid())
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.