歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> 淺談多核CPU、多線程與並行計算

淺談多核CPU、多線程與並行計算

日期:2017/2/28 14:22:32   编辑:Linux教程

0.前言

筆者用過MPI和C#線程池,參加過比賽,有所感受,將近一年來,對多線程編程興趣一直不減,一直有所關注,決定寫篇文章,算是對知識的總結吧。有說的不對的地方,歡迎各位大哥們指正:)

1.CPU發展趨勢

核心數目依舊會越來越多,依據摩爾定律,由於單個核心性能提升有著嚴重的瓶頸問題,普通的桌面PC有望在2017年末2018年初達到24核心(或者16核32線程),我們如何來面對這突如其來的核心數目的增加?編程也要與時俱進。筆者斗膽預測,CPU各個核心之間的片內總線將會采用4路組相連:),因為全相連太過復雜,單總線又不夠給力。而且應該是非對稱多核處理器,可能其中會混雜幾個DSP處理器或流處理器。

2.多線程與並行計算的區別

(1)多線程的作用不只是用作並行計算,他還有很多很有益的作用。

還在單核時代,多線程就有很廣泛的應用,這時候多線程大多用於降低阻塞(意思是類似於

while(1)

{

if(flag==1)

break;

sleep(1);

}

這樣的代碼)帶來的CPU資源閒置,注意這裡沒有浪費CPU資源,去掉sleep(1)就是純浪費了。

阻塞在什麼時候發生呢?一般是等待IO操作(磁盤,數據庫,網絡等等)。此時如果單線程,CPU會干轉不干實事(與本程序無關的事情都算不干實事,因為執行其他程序對我來說沒意義),效率低下(針對這個程序而言),例如一個IO操作要耗時10毫秒,CPU就會被阻塞接近10毫秒,這是何等的浪費啊!要知道CPU是數著納秒過日子的。

所以這種耗時的IO操作就用一個線程Thread去代為執行,創建這個線程的函數(代碼)部分不會被IO操作阻塞,繼續干這個程序中其他的事情,而不是干等待(或者去執行其他程序)。

同樣在這個單核時代,多線程的這個消除阻塞的作用還可以叫做“並發”,這和並行是有著本質的不同的。並發是“偽並行”,看似並行,而實際上還是一個CPU在執行一切事物,只是切換的太快,我們沒法察覺罷了。例如基於UI的程序(俗話說就是圖形界面),如果你點一個按鈕觸發的事件需要執行10秒鐘,那麼這個程序就會假死,因為程序在忙著執行,沒空搭理用戶的其他操作;而如果你把這個按鈕觸發的函數賦給一個線程,然後啟動線程去執行,那麼程序就不會假死,繼續相應用戶的其他操作。但是,隨之而來的就是線程的互斥和同步、死鎖等問題,詳細見有關文獻。

現在是多核時代了,這種線程的互斥和同步問題是更加嚴峻的,單核時代大都算並發,多核時代真的就大為不同,為什麼呢?具體細節請參考有關文獻。我這裡簡單解釋一下,以前volatile型變量的使用可以解決大部分問題,例如多個線程共同訪問一個Flag標志位,如果是單核並發,基本不會出問題(P.S.在什麼情況下會出問題呢?Flag有多個,或者是一個數組,這時候只能通過邏輯手段搞定這個問題了,多來幾次空轉無所謂,別出致命問題就行),因為CPU只有一個,同時訪問這個標志位的只能有一個線程,而多核情況下就不太一樣了,所以僅僅volatile不太能解決問題,這就要用到具體語言,具體環境中的“信號量”了,Mutex,Monitor,Lock等等,這些類都操作了硬件上的“關中斷”,達到“原語”效果,對臨界區的訪問不被打斷的效果,具體就不解釋了,讀者可以看看《現代操作系統》。

(2)並行計算還可以通過其他手段來獲得,而多線程只是其中之一。

其他手段包括:多進程(這又包括共享存儲區的和分布式多機,以及混合式的),指令級並行。

ILP(指令級並行),x86架構裡叫SMT(同時多線程),在MIPS架構裡與之對應的是super scalar(超標量)和亂序執行,二者有區別,但共同點都是可以達到指令級並行,這是用戶沒法控制的,不屬於編程范圍,只能做些有限的優化,而這有限的優化可能只屬於編譯器管轄的范疇,用戶能做的甚少。

(3)典型的適於並行計算的語言

Erlang和MPI:這兩個前者是語言,後者是C++和Fortran的擴展庫,效果是一樣的,利用多進程實現並行計算,Erlang是共享存儲區的,MPI是混合型的。

C#.NET4.0:新版本4.0可以用少量代碼實現並行For循環,之前版本需要用很繁瑣的代碼才能實現同樣功能。這是利用了多線程實現並行計算。Java和C#3.5都有線程池(ThreadPool),也是不錯的很好用的多線程管理類,可以方便高效的使用多線程。

CUDA,還是個初生牛犢,有很大的發展潛力,只不過就目前其應用領域很有限。其目前只能使用C語言,而且還不是C99,比較低級,不能使用函數指針。個人感覺這由於硬件上天生的局限性(平均每個核心可用內存小,與系統內存通訊時間長),只適用於做科學計算,靜態圖像處理,視頻編碼解碼,其他領域,還不如高端CPU。等以後GPU有操作系統了,能充分調度GPU資源了,GPU就可以當大神了。游戲中的物理加速,實際上多核CPU也能很好的做到。

其他語言。。。恩。。留作將來討論。

3.線程越多越好嗎?什麼時候才有必要用多線程?

線程必然不是越多越好,線程切換也是要開銷的,當你增加一個線程的時候,增加的額外開銷要小於該線程能夠消除的阻塞時間,這才叫物有所值。

Linux自從2.6內核開始,就會把不同的線程交給不同的核心去處理。Windows也從NT.4.0開始支持這一特性。

什麼時候該使用多線程呢?這要分四種情況討論:

a.多核CPU——計算密集型任務。此時要盡量使用多線程,可以提高任務執行效率,例如加密解密,數據壓縮解壓縮(視頻、音頻、普通數據),否則只能使一個核心滿載,而其他核心閒置。

b.單核CPU——計算密集型任務。此時的任務已經把CPU資源100%消耗了,就沒必要也不可能使用多線程來提高計算效率了;相反,如果要做人機交互,最好還是要用多線程,避免用戶沒法對計算機進行操作。

c.單核CPU——IO密集型任務,使用多線程還是為了人機交互方便,

d.多核CPU——IO密集型任務,這就更不用說了,跟單核時候原因一樣。

4.程序員需要掌握的技巧/技術

(1)減少串行化的代碼用以提高效率。這是廢話。

(2)單一的共享數據分布化:把一個數據復制很多份,讓不同線程可以同時訪問。

(3)負載均衡,分為靜態的和動態的兩種。具體的參見有關文獻。

Copyright © Linux教程網 All Rights Reserved