極簡說明Multi-thread/Multi-process、CPU-bound/IO-bound和GIL

Posted on November 26, 2022 in CS. View: 3,821

TL;DR

  • Multi-Thread中每個Thread會共享資源,因此其優點是:資源利用較有效率、共享資源存取較快速
  • Multi-process將這些資源複製且隔離,其優點是:Process間不共享記憶體、程式碼比較好寫
  • 特別注意:不管在 Multi-Thread 和 Multi-process 都可以吃到完整的多顆CPU,因為所有工作都會丟到OS Thread Scheduler,Scheduler會管控CPU的使用,但是Python可能有GIL需另外討論。
  • 等待CPU計算較多的工作稱為CPU-bound Task,等待DMA拉資訊進記憶體較多的工作稱為IO-bound Task。
  • Python 在原始 default 的編譯器是採用CPython,而 CPython 採用了GIL,這導致了一個Python Job(一個Process)同一時間只能跑一個Thread。最終導致,CPython 的 Multi-thread 不能同時吃到多顆CPU,因此無法解決CPU-bound Task的問題。
  • 粗略的原則:在CPython的情況下,遇到CPU-bound Task使用Multi-process,遇到 IO-bound Task使用Multi-thread。

Multi-Thread and Multi-Process

  • Single Process Single Thread
    • Single Process中記憶體存放Code、Data
    • Single Thread 進行中需使用 Stack 來追蹤紀錄
  • Single Process Multi-Thread
    • 單一Process中的多個Thread共享資源(記憶體存放的Code、Data)
    • 也因為共享資源的緣故,要小心撰寫程式碼避免dead lock和race condition等問題
  • Multi-process
    • 每個Process都有自己一份的Code、Data,process彼此間不共享這些資源
    • 所以開許多Process比較吃記憶體
    • 而且如果想要讓Process之間共享資訊也會比較不容易,需要要做 serialize、memory copy、de-serialize 等等 OS 的開銷
  • 譬喻:
    • Process 像是一間工廠,工廠裡面有許多器具(Code、Data)
    • Thread 像是裡頭的工人
    • Multi-Thread就像是一間工廠有多個工人,這樣的話那就需要避免他們互搶設備或互相等待
    • Multi-Process就像是蓋多間工廠,蓋更多工廠意味著更多的資本支出,而且工廠和工廠間要資源交換比較不容易
  • Multi-Thread v.s. Multi-process
    • Multi-Thread 優點:資源利用較有效率;共享資源存取較快速
    • Multi-Thread 缺點:因為共用記憶體,所以使用上要注意dead lock和race condition等問題;程式碼比較難寫
    • Multi-processing 優點:process之間不共享記憶體;程式碼比較好寫
    • Multi-processing 缺點:資源利用比較沒效率,較佔記憶體;共享資源需要額外開銷,有一些東西沒辦法serialize處理上就很麻煩;如果有跨Process的分享資源的話,仍需注意dead lock和race condition等問題
  • 特別注意:不管在 Multi-Thread 和 Multi-process 都可以吃到完整的多顆CPU,因為所有工作都會丟到OS Thread Scheduler,Scheduler會管控CPU的使用,但是Python情況特殊我們待會討論。

CPU-bound and IO-bound

  • CPU-bound
    • 當某個工作高度仰賴CPU計算,我們會稱之為CPU-bound Task
  • IO-bound
    • 當某個工作大量等待外部存取(例如:從硬碟讀寫、網路溝通),我們會稱之為IO-bound Task
    • 外部存取:意味著從外部將資訊拉近記憶體,此工作主要由DMA來完成,CPU在這過程並不需要參與,所以會有一段等待的時間
    • DMA(Direct Memory Access)允許某些電腦內部的硬體子系統,可以獨立地直接讀寫系統記憶體,而不需CPU介入處理 。很多硬體的系統會使用DMA,包含硬碟控制器、繪圖顯示卡、網路卡和音效卡。

GIL (Global Interpreter Lock)

  • Python 在原始 default 的編譯器是採用CPython,而 CPython 採用了GIL,這導致了一個Python Job(一個Process)同一時間只能跑一個Thread,這意味著就算有多顆CPU的存在,同時間還是沒辦法吃一顆以上的CPU,也因此會有以下情況。
  • 當我們需要處理CPU-bound Task時,如果採用Multi-thread,因為GIL的緣故導致無法同時使用多顆CPU,也因此計算效能無法提升。相反如果採用Multi-process,因為開了好幾個Process,每個Process都有自己獨立的GIL,因此才能吃到多顆CPU,進而解決CPU-bound的問題。
  • 當我們需要處理IO-bound Task時,如果採用Multi-thread,因為CPU的使用量並不高,大部分thread的時間都是在等待IO,所以多個thread其實就足夠解決IO-bound問題。當然如果使用Multi-process也是可以做到,但是這會多造成資源的浪費。