python如何利用多核處理器,python多核並行計算

本文目錄一覽:

怎麼讓python用多個cpu

python由於GIL的關係,python的多線程並沒有發揮多核的作用,這些線程都是在在單核上跑的所以要想發揮多核的作用,就需要使用多進程,儘可能的在每一個CPU核心上分配到一個python進程。所以要想跑滿多核CPU就得多進程多線程互相結合

Python如何利用多核處理器

GIL 與 Python 線程的糾葛

GIL 是什麼東西?它對我們的 python 程序會產生什麼樣的影響?我們先來看一個問題。運行下面這段 python 程序,CPU 佔用率是多少?

# 請勿在工作中模仿,危險:)def dead_loop():    while True:        passdead_loop()

答案是什麼呢,佔用 100% CPU?那是單核!還得是沒有超線程的古董 CPU。在我的雙核 CPU 上,這個死循環只會吃掉我一個核的工作負荷,也就是只佔用 50% CPU。那如何能讓它在雙核機器上佔用 100% 的 CPU 呢?答案很容易想到,用兩個線程就行了,線程不正是並發分享 CPU 運算資源的嗎。可惜答案雖然對了,但做起來可沒那麼簡單。下面的程序在主線程之外又起了一個死循環的線程

import threadingdef dead_loop():    while True:        pass# 新起一個死循環線程t = threading.Thread(target=dead_loop)t.start()# 主線程也進入死循環dead_loop()t.join()

按道理它應該能做到佔用兩個核的 CPU 資源,可是實際運行情況卻是沒有什麼改變,還是只佔了 50% CPU 不到。這又是為什麼呢?難道 python 線程不是操作系統的原生線程?打開 system monitor 一探究竟,這個佔了 50% 的 python 進程確實是有兩個線程在跑。那這兩個死循環的線程為何不能佔滿雙核 CPU 資源呢?其實幕後的黑手就是 GIL。

GIL 的迷思:痛並快樂著

GIL 的全稱為 Global Interpreter Lock ,意即全局解釋器鎖。在 Python 語言的主流實現 CPython 中,GIL 是一個貨真價實的全局線程鎖,在解釋器解釋執行任何 Python 代碼時,都需要先獲得這把鎖才行,在遇到 I/O 操作時會釋放這把鎖。如果是純計算的程序,沒有 I/O 操作,解釋器會每隔 100 次操作就釋放這把鎖,讓別的線程有機會執行(這個次數可以通過sys.setcheckinterval 來調整)。所以雖然 CPython 的線程庫直接封裝操作系統的原生線程,但 CPython 進程做為一個整體,同一時間只會有一個獲得了 GIL 的線程在跑,其它的線程都處於等待狀態等著 GIL 的釋放。這也就解釋了我們上面的實驗結果:雖然有兩個死循環的線程,而且有兩個物理 CPU 內核,但因為 GIL 的限制,兩個線程只是做著分時切換,總的 CPU 佔用率還略低於 50%。

看起來 python 很不給力啊。GIL 直接導致 CPython 不能利用物理多核的性能加速運算。那為什麼會有這樣的設計呢?我猜想應該還是歷史遺留問題。多核 CPU 在 1990 年代還屬於類科幻,Guido van Rossum 在創造 python 的時候,也想不到他的語言有一天會被用到很可能 1000+ 個核的 CPU 上面,一個全局鎖搞定多線程安全在那個時代應該是最簡單經濟的設計了。簡單而又能滿足需求,那就是合適的設計(對設計來說,應該只有合適與否,而沒有好與不好)。怪只怪硬體的發展實在太快了,摩爾定律給軟體業的紅利這麼快就要到頭了。短短 20 年不到,代碼工人就不能指望僅僅靠升級 CPU 就能讓老軟體跑的更快了。在多核時代,編程的免費午餐沒有了。如果程序不能用並發擠干每個核的運算性能,那就意謂著會被淘汰。對軟體如此,對語言也是一樣。那 Python 的對策呢?

Python 的應對很簡單,以不變應萬變。在最新的 python 3 中依然有 GIL。之所以不去掉,原因嘛,不外以下幾點:

欲練神功,揮刀自宮:

CPython 的 GIL 本意是用來保護所有全局的解釋器和環境狀態變數的。如果去掉 GIL,就需要多個更細粒度的鎖對解釋器的眾多全局狀態進行保護。或者採用 Lock-Free 演算法。無論哪一種,要做到多線程安全都會比單使用 GIL 一個鎖要難的多。而且改動的對象還是有 20 年歷史的 CPython 代碼樹,更不論有這麼多第三方的擴展也在依賴 GIL。對 Python 社區來說,這不異於揮刀自宮,重新來過。

就算自宮,也未必成功:

有位牛人曾經做了一個驗證用的 CPython,將 GIL 去掉,加入了更多的細粒度鎖。但是經過實際的測試,對單線程程序來說,這個版本有很大的性能下降,只有在利用的物理 CPU 超過一定數目後,才會比 GIL 版本的性能好。這也難怪。單線程本來就不需要什麼鎖。單就鎖管理本身來說,鎖 GIL 這個粗粒度的鎖肯定比管理眾多細粒度的鎖要快的多。而現在絕大部分的 python 程序都是單線程的。再者,從需求來說,使用 python 絕不是因為看中它的運算性能。就算能利用多核,它的性能也不可能和 C/C++ 比肩。費了大力氣把 GIL 拿掉,反而讓大部分的程序都變慢了,這不是南轅北轍嗎。

難道 Python 這麼優秀的語言真的僅僅因為改動困難和意義不大就放棄多核時代了嗎?其實,不做改動最最重要的原因還在於:不用自宮,也一樣能成功!

其它神功

那除了切掉 GIL 外,果然還有方法讓 Python 在多核時代活的滋潤?讓我們回到本文最初的那個問題:如何能讓這個死循環的 Python 腳本在雙核機器上佔用 100% 的 CPU?其實最簡單的答案應該是:運行兩個 python 死循環的程序!也就是說,用兩個分別佔滿一個 CPU 內核的 python 進程來做到。確實,多進程也是利用多個 CPU 的好方法。只是進程間內存地址空間獨立,互相協同通信要比多線程麻煩很多。有感於此,Python 在 2.6 里新引入了 multiprocessing這個多進程標準庫,讓多進程的 python 程序編寫簡化到類似多線程的程度,大大減輕了 GIL 帶來的不能利用多核的尷尬。

這還只是一個方法,如果不想用多進程這樣重量級的解決方案,還有個更徹底的方案,放棄 Python,改用 C/C++。當然,你也不用做的這麼絕,只需要把關鍵部分用 C/C++ 寫成 Python 擴展,其它部分還是用 Python 來寫,讓 Python 的歸 Python,C 的歸 C。一般計算密集性的程序都會用 C 代碼編寫並通過擴展的方式集成到 Python 腳本里(如 NumPy 模塊)。在擴展里就完全可以用 C 創建原生線程,而且不用鎖 GIL,充分利用 CPU 的計算資源了。不過,寫 Python 擴展總是讓人覺得很複雜。好在 Python 還有另一種與 C 模塊進行互通的機制 : ctypes

利用 ctypes 繞過 GIL

ctypes 與 Python 擴展不同,它可以讓 Python 直接調用任意的 C 動態庫的導出函數。你所要做的只是用 ctypes 寫些 python 代碼即可。最酷的是,ctypes 會在調用 C 函數前釋放 GIL。所以,我們可以通過 ctypes 和 C 動態庫來讓 python 充分利用物理內核的計算能力。讓我們來實際驗證一下,這次我們用 C 寫一個死循環函數

extern”C”{  void DeadLoop()  {    while (true);  }}

用上面的 C 代碼編譯生成動態庫 libdead_loop.so (Windows 上是 dead_loop.dll)

,接著就要利用 ctypes 來在 python 里 load 這個動態庫,分別在主線程和新建線程里調用其中的 DeadLoop

from ctypes import *from threading import Threadlib = cdll.LoadLibrary(“libdead_loop.so”)t = Thread(target=lib.DeadLoop)t.start()lib.DeadLoop()

這回再看看 system monitor,Python 解釋器進程有兩個線程在跑,而且雙核 CPU 全被佔滿了,ctypes 確實很給力!需要提醒的是,GIL 是被 ctypes 在調用 C 函數前釋放的。但是 Python 解釋器還是會在執行任意一段 Python 代碼時鎖 GIL 的。如果你使用 Python 的代碼做為 C 函數的 callback,那麼只要 Python 的 callback 方法被執行時,GIL 還是會跳出來的。比如下面的例子:

extern”C”{  typedef void Callback();  void Call(Callback* callback)  {    callback();  }}

from ctypes import *from threading import Threaddef dead_loop():    while True:        passlib = cdll.LoadLibrary(“libcall.so”)Callback = CFUNCTYPE(None)callback = Callback(dead_loop)t = Thread(target=lib.Call, args=(callback,))t.start()lib.Call(callback)

注意這裡與上個例子的不同之處,這次的死循環是發生在 Python 代碼里 (DeadLoop 函數) 而 C 代碼只是負責去調用這個 callback 而已。運行這個例子,你會發現 CPU 佔用率還是只有 50% 不到。GIL 又起作用了。

其實,從上面的例子,我們還能看出 ctypes 的一個應用,那就是用 Python 寫自動化測試用例,通過 ctypes 直接調用 C 模塊的介面來對這個模塊進行黑盒測試,哪怕是有關該模塊 C 介面的多線程安全方面的測試,ctypes 也一樣能做到。

結語

雖然 CPython 的線程庫封裝了操作系統的原生線程,但卻因為 GIL 的存在導致多線程不能利用多個 CPU 內核的計算能力。好在現在 Python 有了易經筋(multiprocessing), 吸星大法(C 語言擴展機制)和獨孤九劍(ctypes),足以應付多核時代的挑戰,GIL 切還是不切已經不重要了,不是嗎。

python怎麼提高cpu利用率

內置的是多處理模塊。multiprocessing.Pool類使用map和相關方法在多個CPU之間提供矢量化.但是這裡需要權衡.如果必須在進程之間傳遞大量數據,那麼這種開銷可能會抵消多核的優勢。使用合適的numpy版本。如果numpy是使用多線程ATLAS庫構建的,則在遇到大問題時會更快。使用擴展模塊,例如numexpr、parallelpython、corepy或CopenhagenVectorByteCode。請注意,線程模塊在這方面並不是很有用.為了簡化內存管理,全局解釋器鎖(「GIL」)強制一次只能有一個線程執行python位元組碼.但是,像numpy這樣的外部模塊可以在內部使用多個線程.

中央處理器(CPU),是電子計算機的主要設備之一,電腦中的核心配件。其功能主要是解釋計算機指令以及處理計算機軟體中的數據。電腦中所有操作都由CPU負責讀取指令,對指令解碼並執行指令的核心部件。

程序是由指令構成的序列,執行程序就是按指令序列逐條執行指令。一旦把程序裝入主存儲器中,就可以由CPU自動地完成從主存取指令和執行指令的任務。

如何讓一個Python的腳本跑滿多核的CPU

python由於GIL的關係,python的多線程並沒有發揮多核的作用,這些線程都是在在單核上跑的

所以要想發揮多核的作用,就需要使用多進程,儘可能的在每一個CPU核心上分配到一個python進程。

所以要想跑滿多核CPU就得多進程多線程互相結合

python如何利用多核cpu

你需要利用JavaScript,然後才能夠直接使用這個多核的CPU用編程的語言。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/293157.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-25 18:39
下一篇 2024-12-25 18:39

相關推薦

  • Python中引入上一級目錄中函數

    Python中經常需要調用其他文件夾中的模塊或函數,其中一個常見的操作是引入上一級目錄中的函數。在此,我們將從多個角度詳細解釋如何在Python中引入上一級目錄的函數。 一、加入環…

    編程 2025-04-29
  • 如何查看Anaconda中Python路徑

    對Anaconda中Python路徑即conda環境的查看進行詳細的闡述。 一、使用命令行查看 1、在Windows系統中,可以使用命令提示符(cmd)或者Anaconda Pro…

    編程 2025-04-29
  • Python列表中負數的個數

    Python列表是一個有序的集合,可以存儲多個不同類型的元素。而負數是指小於0的整數。在Python列表中,我們想要找到負數的個數,可以通過以下幾個方面進行實現。 一、使用循環遍歷…

    編程 2025-04-29
  • Python周杰倫代碼用法介紹

    本文將從多個方面對Python周杰倫代碼進行詳細的闡述。 一、代碼介紹 from urllib.request import urlopen from bs4 import Bea…

    編程 2025-04-29
  • Python計算陽曆日期對應周幾

    本文介紹如何通過Python計算任意陽曆日期對應周幾。 一、獲取日期 獲取日期可以通過Python內置的模塊datetime實現,示例代碼如下: from datetime imp…

    編程 2025-04-29
  • Python程序需要編譯才能執行

    Python 被廣泛應用於數據分析、人工智慧、科學計算等領域,它的靈活性和簡單易學的性質使得越來越多的人喜歡使用 Python 進行編程。然而,在 Python 中程序執行的方式不…

    編程 2025-04-29
  • python強行終止程序快捷鍵

    本文將從多個方面對python強行終止程序快捷鍵進行詳細闡述,並提供相應代碼示例。 一、Ctrl+C快捷鍵 Ctrl+C快捷鍵是在終端中經常用來強行終止運行的程序。當你在終端中運行…

    編程 2025-04-29
  • 蝴蝶優化演算法Python版

    蝴蝶優化演算法是一種基於仿生學的優化演算法,模仿自然界中的蝴蝶進行搜索。它可以應用於多個領域的優化問題,包括數學優化、工程問題、機器學習等。本文將從多個方面對蝴蝶優化演算法Python版…

    編程 2025-04-29
  • Python字典去重複工具

    使用Python語言編寫字典去重複工具,可幫助用戶快速去重複。 一、字典去重複工具的需求 在使用Python編寫程序時,我們經常需要處理數據文件,其中包含了大量的重複數據。為了方便…

    編程 2025-04-29
  • Python清華鏡像下載

    Python清華鏡像是一個高質量的Python開發資源鏡像站,提供了Python及其相關的開發工具、框架和文檔的下載服務。本文將從以下幾個方面對Python清華鏡像下載進行詳細的闡…

    編程 2025-04-29

發表回復

登錄後才能評論