1. 程式人生 > >如何提高多執行緒程式的cpu利用率

如何提高多執行緒程式的cpu利用率

  正如大家所知道的那樣,多核多cpu越來越普遍了,而且編寫多執行緒程式也是件很簡單的事情。在Windows下面,呼叫CreateThread函式一次就能夠以你想要的函式地址新建一個子執行緒執行。然後,事情確實你發現建立多執行緒根本沒有讓程式快多少,也沒有提高多少cpu利用率,甚至可能讓cpu利用率下降。唯一能夠確定的是多執行緒能夠避免介面假死。為什麼會是這樣的了。本文將舉一些例子和講述一些原因。


  首先,我來講一下多處理的一些知識。如下圖所示,
SMP體系結構
  多處理器系統也只有一個待執行的執行緒佇列,記憶體中也只有一個作業系統拷貝,而且也只有一個記憶體系統,但是會有多個cpu同時執行不同的執行緒。一個cpu執行一個執行緒,那麼上圖中的系統最多能在同一時間執行2個執行緒。其實,多處理系統需要掌握的知識不是這些,而是快取一致性


  現在來解釋下什麼是快取一致性。由於,還是隻有一個記憶體系統。所有cpu都要和這個記憶體系統通訊,但是隻有一條匯流排,那麼這無疑會造成匯流排緊張,限制整體的速度了。那麼,你多個cpu也沒多少意義了。解決這個問題的辦法還是利用cpu的快取機制,學過組成原理的同學都知道,cpu的快取命中率還是很高的,有90%以上吧。那麼,我繼續利用快取機制還是可以降低匯流排的頻繁使用的。但是,每個cpu都有自己的快取。如果有2個cpu的快取儲存的是同一記憶體資料的內容,其中一個cpu的快取更新了,另外一個cpu的快取也必須更新,這就是所謂的快取一致性。程式設計多執行緒程式的一個很重要的一點就是避免因為快取一致性引起的快取更新風暴。
  現在我舉一個快取更新風暴
的例子。
如圖所示的類定義,
快取一致性風暴
  鎖lockHttp和lockSsl中間只有8個位元組,而絕大部分系統上一個快取行是128個位元組,那麼這2個鎖很可能就處在同一個快取行上面。那麼,最壞的情況會發生什麼事情了。假設處理器P1在執行一個處理http請求的執行緒T1,處理器P2在執行一個處理ssl請求的執行緒T2,那麼當T1獲得鎖lockHttp的時候,鎖的內容就會改變,為了保持快取一致性,就會更新P2的快取。那麼,T2要獲得鎖lockssl的時候,發現快取已經失效了,就必須從記憶體中重新載入快取之類。總之,這會將快取命中率降低到90%以下,引起效能的嚴重降低。而且發生這種事情的原因是因為我們不瞭解硬體的體系結構。


  多cpu不能成倍提高速度的原因是任務的某些部分是必須序列處理的。比如,矩陣乘法可以分為三個部分,初始化矩陣,相乘,返回結果。這三部分第二部分可以用多執行緒來處理,第一部分和第三部分則是不可以的。而且第二部分必須在第一部分完成之後,第三部分必須在第一部分完成之後。那麼,無論你新增多少個處理器,最快的時間都至少是第一部分和第二部分的時間之和。這個事實好像叫做Amdahl法則。
  如果使用多執行緒,那麼就必須考慮執行緒同步,而執行緒同步又是導致速度降低的關鍵。所以下面就講述一些方法來加快多執行緒程式的吞吐速度。
  方法一,把一個任務分解為多個可以子任務。

  因為總有些子任務是可以併發的,多個子任務併發執行了很可能就能夠避免cpu需要io操作的完成了,而且能夠提高系統的吞吐量。
  方法二,快取多執行緒的共享資料。
  當你已經在使用多執行緒了,很多時候必須使用共享資料。如果,資料是隻讀的,那麼可以在第一次獲取後儲存起來,以後就可以重複使用了。但是,第一次的獲取還是無法避免的需要執行緒同步操作的。
  方法三,如果執行緒數目有限,就不要共享資料。
  做法是為每一個執行緒例項化一個單獨的資料,其實就是為每一個執行緒分配一塊資料使用。這樣沒有執行緒同步操作了,速度可以儘可能的提示。
  方法四,如果沒辦法確定執行緒數目到底有多少,那麼使用部分共享吧。
  部分共享其實就是使用多個資源池代替一個資源池,資源池的數目得更加經驗來確定。如下圖所示,
多個資源池

  最後在提一個叫做Thundering Herd的問題,該問題維基百科有定義http://en.wikipedia.org/wiki/Thundering_herd_problem。大意是,當多個執行緒在等待一個資源的時候,如果事件等待到了,作業系統是喚醒所有等待的執行緒讓它們自己去競爭資源了還是選擇一個執行緒把資源給它。當然喚醒所有的執行緒肯定開銷要大,而且所有沒有搶到資源的執行緒還得重新進入等待狀態,這無疑造成很多沒必要的操作,浪費了沒必要的執行緒上下文切換。總之,會不會存在Thundering Herd還是跟不同的作業系統有關的。萬一存在Thundering Herd了,多執行緒可能就沒那麼好辦了。


  到現在我們知道了為什麼多cpu並不能成倍提高程式的速度了。首先因為有些任務無法並行,其次即使是並行cpu之間還是有很多牽制的。本書的內容主要來自提高c++效能的程式設計技術一書。