1.5 使用輕量級并發(fā)模型
Amdahl法則和Gustafson法則都沒有考慮引入并行所帶來的開銷。這兩個法則也沒有考慮到存在一些模式能夠?qū)⒋胁糠洲D(zhuǎn)換為能夠充分利用并行化的新算法。減少應(yīng)用程序中串行部分的代碼對于提高并行執(zhí)行單元的使用率而言非常重要。
在以前版本的.NET Framework中,如果想要在C#應(yīng)用程序(一個進(jìn)程)中并行地運行代碼,那么您必須創(chuàng)建并管理多個線程(軟件線程)。因此,您必須編寫非常復(fù)雜的多線程代碼。將算法分解為多個線程、協(xié)調(diào)各個代碼單元、在代碼單元之間共享信息以及收集運算結(jié)果等任務(wù)實在是非常復(fù)雜的程序設(shè)計工作。隨著邏輯內(nèi)核的增加,這個任務(wù)會變得更加復(fù)雜,因為您需要通過更多線程來獲得更好的可擴展性。
多線程編程模型的設(shè)計并不是為了幫助開發(fā)人員面對多核革命。事實上,創(chuàng)建新的線程需要執(zhí)行大量的處理器指令,而且可能會對已經(jīng)分解為并行化線程的代碼引入太大的開銷。很多有用的數(shù)據(jù)結(jié)構(gòu)和類在設(shè)計上并沒有考慮到被多線程訪問,因此,為了能夠?qū)崿F(xiàn)這一點還需要添加很多代碼。這些額外的代碼會使得開發(fā)人員的注意力偏離主要目標(biāo):通過并行執(zhí)行來提升性能。
由于這個多線程模型過于復(fù)雜,難以應(yīng)對多核革命,因此這個模型被稱為重量級并發(fā)(heavyweight concurrency)。這個模型加入了嚴(yán)重的開銷,需要編寫很多代碼來處理由于框架層次缺乏對多線程訪問的支持而帶來的問題,并且會導(dǎo)致代碼復(fù)雜難以理解。
由于以前版本的.NET Framework所提供的多線程模型和現(xiàn)代微處理器日益增長的邏輯內(nèi)核數(shù)引發(fā)的上述問題,促進(jìn)了允許創(chuàng)建并行化代碼的新模型的創(chuàng)建。這個新模型稱為輕量級并發(fā)(lightweight concurrency),這個模型減少了在不同邏輯內(nèi)核上創(chuàng)建和執(zhí)行代碼所需要的總開銷。這并不是說能夠完全消除并行化帶來的開銷,但是這個模型本身是為現(xiàn)代多核微處理器而設(shè)計的。重量級并發(fā)模型是在多處理器的時代出現(xiàn)的,在那個時代計算機可能有很多物理微處理器,每個微處理器只有一個內(nèi)核。輕量級的并發(fā)模型考慮了新的微架構(gòu),這個架構(gòu)中有很多由一些物理內(nèi)核支撐的邏輯內(nèi)核。
輕量級并發(fā)模型并不只是關(guān)注不同邏輯內(nèi)核之間的作業(yè)調(diào)度,它還在框架級別添加了對多線程訪問的支持,從而使得代碼更容易理解。
大多數(shù)現(xiàn)代程序設(shè)計語言都在向輕量級并發(fā)模型變革,幸運的是,.NET Framework 4也加入了這一波變革。因此,所有能夠生成.NET應(yīng)用程序的托管語言都能夠充分利用這種新的模型并發(fā)揮其優(yōu)勢。
1.6 創(chuàng)建成功的基于任務(wù)的設(shè)計
有時候,必須對現(xiàn)有的解決方案進(jìn)行優(yōu)化才能夠充分利用并行化的優(yōu)勢。在這些情況下,您必須理解現(xiàn)有的串行設(shè)計,或者理解提供了有限可擴展性的并行化算法,然后再對現(xiàn)有設(shè)計進(jìn)行重構(gòu),從而使其獲得性能提升,而且不會引入問題或產(chǎn)生不同的結(jié)果。您既可以取問題的一小部分,或者也可以考慮整個問題,并創(chuàng)建出一個基于任務(wù)的設(shè)計,然后就可以引入并行化了。在設(shè)計新解決方案的時候也可以采取同樣的技術(shù)。
遵循以下的步驟就可以創(chuàng)建出成功的基于任務(wù)的設(shè)計:
(1) 將每個問題分解為很多子問題,完全不要去考慮順序執(zhí)行。
(2) 將每個子問題想象為下面三類中的一類:
● 能夠以并行的方式進(jìn)行處理的數(shù)據(jù)——對數(shù)據(jù)進(jìn)行分解以實現(xiàn)并行化。
● 需要很多任務(wù),而且能夠以某種復(fù)雜的并行化進(jìn)行處理的數(shù)據(jù)流——對數(shù)據(jù)和任務(wù)進(jìn)行分解以實現(xiàn)并行化。
● 可以并行運行的任務(wù)——對任務(wù)進(jìn)行分解以實現(xiàn)并行化。
(3) 將設(shè)計組織為能夠表達(dá)并行化的形式。
(4) 考慮將不同子問題連接起來的任務(wù)的必要性。盡可能地避免依賴性。
(5) 在進(jìn)行設(shè)計的時候,心里要想著并發(fā)和潛在的并行化。
(6) 分析并行化的問題的執(zhí)行計劃,考慮當(dāng)前的多核微處理器和未來的架構(gòu)。在設(shè)計的時候要準(zhǔn)備好更高的可擴展性。
(7) 盡可能減少臨界區(qū)。
(8) 盡可能通過基于任務(wù)的程序設(shè)計實現(xiàn)并行化。
(9) 調(diào)優(yōu)和迭代。
上述步驟并不是說所有的子問題都必須是運行在不同線程中的并行化任務(wù)。設(shè)計的時候必須考慮并行化的可能性,然后在編寫代碼的時候,可以根據(jù)性能和可擴展性的目標(biāo)選擇最佳的實現(xiàn)方式。重要的是要以并行方式進(jìn)行思考,將要解決的工作分解為任務(wù)。通過這種方式,您就能夠按照需要對代碼進(jìn)行并行化。如果您已經(jīng)擁有一個面向傳統(tǒng)串行執(zhí)行的設(shè)計,那么您將需要努力通過基于任務(wù)的程序設(shè)計技術(shù)對現(xiàn)有設(shè)計進(jìn)行并行化。