在對任務進行并行化時,為了獲得最佳性能,最重要的目標之一是要最小化這些臨界區(qū)。大部分情況下,都不可能在兩個并行部分之間避免運行串行代碼,因為總需要啟動并行作業(yè)并收集結(jié)果。然而,對臨界區(qū)的代碼進行優(yōu)化,并且移除不必要的臨界區(qū)甚至比并行代碼的正確調(diào)優(yōu)還要重要。
當您面對一個具有太多臨界區(qū)的執(zhí)行計劃的時候,請不要忘記Amdahl法則。如果不能減少這些臨界區(qū),那么可以嘗試找到一些可以與臨界區(qū)并行運行的任務。例如,可以在臨界區(qū)運行的時候并行運行一個任務預取數(shù)據(jù)供下一段并行算法使用,這樣可以增強這種方案的整體性能。重要的是要考慮現(xiàn)代多核硬件能夠提供的能力,而不是僅僅考慮只有一個單獨的執(zhí)行單元的情況。
1.6.5 理解多核并行程序的設計原則
James Reinders在Dr. Dobb’s Journal發(fā)表了一篇名為“Rules for Parallel Programming for Multicore”的文章(www.drdobbs.com/hpc-high-performance-computing/201804248)。他列舉出了8條原則幫助多核程序設計的開發(fā)人員。他的原則也適合于創(chuàng)建并行的C#和.NET Framework 4應用程序。這8條原則是:
(1) 按照并行的方式思考——這一條原則指的是以并行的思想指導設計,前面的小節(jié)解釋了這個內(nèi)容。
(2) 使用抽象編程——您可以充分利用.NET Framework 4中的Task Parallel Library (TPL)所提供的新功能,使您的高層次代碼反映問題本身,而不是復雜的底層線程管理技術。第2章介紹了TPL。
(3) 按照任務(事情)編程,而不是按照線程(CPU內(nèi)核)編程——通過TPL進行程序設計,您可以編寫代碼實現(xiàn)基于任務的設計,而不用關注底層的線程。
(4) 設計的時候要考慮關閉并發(fā)的情形——通過TPL編寫的代碼在單核微處理器的計算機(只有一個物理內(nèi)核的計算機)上也能夠運行。
(5) 避免使用鎖——非常重要的一點在于:要充分利用新的類、方法和數(shù)據(jù)結(jié)構,這些結(jié)構在設計上避免了復雜同步機制的必要性。TPL在很多復雜的情況下使得避免使用重量級的鎖更加簡單,TPL還提供了新的輕量級的同步機制。
(6) 利用為幫助并發(fā)而設計的工具和庫——Visual Studio 2010提供了新的工具用于調(diào)試、測試和調(diào)優(yōu)并行代碼。在本書中,您將學習到很多這類工具和庫。
(7) 使用可擴展的內(nèi)存分配器——TPL在公共語言運行時(Common Language Runtime,CLR)中提供了可擴展的內(nèi)存分配器,而且在使用任務和線程的時候會自動使用這些內(nèi)存分配器。然而,為了最大限度地利用高速緩存,您必須分析各種不同的分區(qū)情形,盡量避免在每一個任務中耗費大量的內(nèi)存。
(8) 設計的時候要考慮隨增長的工作負載而擴展——掌握了并行擴展之后,就可以很容易地通過TPL提供的新類考慮Gustafson法則了。如果您的設計已經(jīng)準備好了面對未來的擴展性,那么您編寫的代碼就可以隨著內(nèi)核的增長而擴展了。Windows 7和Windows Server 2008 R2支持最多256個硬件線程或邏輯處理器,因此,擴展空間還是很大的。