在多處理器計算機上提高構建速度的一個極好方法是充分利用其並行處理能力,如果你在Visual Studio 2010中有一個C++項目,有兩種不同類型的並行構建配置方法供你選擇。
有哪些參數可以調整?
項目級並行構建是由MSBuild控制的,它是在Visual Studio的解決方案級進行設置的(實際上Visual Studio是為每個用戶都保存了設置,與你想象的可能有點不一樣,你可能認為不同解決方案有不同的設置,但UI卻不允許你這麼做),默認情況下,Visual Studio選取你機器上的處理器數量作為最大並行構建項目的數量,如圖1所示,你可以將這個數字調大調小找出一個並行構建速度最快的合適值,有些人可能喜歡將其調小,以便在構建期間還可以做點其它工作。
圖1 並行構建項目的最大數量
雖然MSBuild從Visual Studio接管了部分功能,但這裡的設置仍然保持和Visual Studio 2008一樣。
如果你正在構建C++或C++/CLI項目,還有一個地方你可以設置並行構建參數,CL編譯器支持/MP參數,它告訴編譯器使用自身的一個單獨實例同時構建它的子集,默認的並行數仍然使用了CPU的數量,但你可以指定一個值,如/MP5,注意現在情況發生了一點變化,因此我要告訴你如何找到這個值,以及在MSBuild格式項目文件中看起來是什麼樣子。
打開項目的“屬性”窗口,轉到“C/C++”*“常規”窗口,我建議你選擇“所有配置”和“所有平台”,在後面你才有更多的可選項。
圖2 項目屬性設置
象往常一樣,通過轉儲,你可以看到項目文件中有什麼內容,在“解決方案資源管理器”中的節點上點擊右鍵,選擇“編輯”。
圖3
下圖顯示了項目文件的一部分代碼。
圖4 項目文件代碼示例
在這裡,所有類型為“ClCompile”的項目都自動擁有元數據MultiProcessorCompilation,默認值為true,除非明確指定了一個不同的值。
順便說一下,MSBuild項目通常都是一個文件,它們的子元素是元數據,下面是一個例子,注意它們被放在一個“ItemGroup”中。
圖5 MSBuild項目文件示例
因為這是一個元數據,如果是高手,完全可以直接修改每個文件,你需要為使用了#import的文件禁用/MP,因為它不支持/MP,其它不支持/MP的特性是/Gm,/Gm表示漸進式編譯,更多參數請參考http://msdn.microsoft.com/en-us/library/bb385193.ASPx。
回到多處理器CL,如果你想明確告訴CL有多少並行編譯執行,Visual Studio可通過/MP實現,它出現在全局設置中。
圖6 C++編譯最大並行任務數設置
Visual Studio通過一個全局屬性CL_MPCount進行設置,這意味著在Visual Studio外構建時將不受任何影響。
如果你選擇一個更細粒度的值,你就不能使用圖形界面進行設置了,因為你根本看不到它的設置項,這時就必須打開項目文件直接進行修改。在CLICompile項目上這是一個完全不同的元數據塊,叫做“ProcessorNumber”,你可以設定一個從1到你認為合理的一個值,然後在/MP後也追加一個同樣的值,如果沒有,它就會被忽略。
圖7 ProcessorNumber和MultiProcessorCompilation設置
圖中出現的波浪線是一個小小的bug,直接忽略它。
如何在命令行上構建?
/MP設置來自項目文件,因此在命令行上進行設置作用是一樣的,它是整個MSBuild的一部分,在命令行上構建和在Visual Studio中構建是一樣的效果嗎?在Visual Studio中設置的全局並行設置不會影響到命令行,你必須親自給msbuild.exe傳遞/m參數,這個參數是可選的,如果你不設置,它就使用CPU的數量,但和Visual Studio開箱即用的特性不一樣,在命令行中如果不指定/m參數,它只會使用1顆CPU,這個問題可能在將來的版本中會得到修正。
圖8 命令行構建參數
如果想給/MP選任意的值,你可以設置一個環境變量,或象Visual Studio那樣傳遞一個屬性CL_MPCount。
在每個項目上都設置/MP是很煩人的,怎麼才能提高設置效率?
你可能想在多個項目上使用/MP,但你又不想在每個項目上都設置一遍,Visual Studio解決這類問題的辦法是使用屬性表。首先從“視圖”菜單打開“屬性管理器”,根據你使用的設置它的確切位置可能不一樣,下面是一個C++項目設置的位置。
圖9 視圖菜單中的屬性管理器
在一個項目上點擊右鍵,選擇“添加新的屬性表”:
圖10 給項目添加新的屬性表
我給它取了一個名字叫做“MultiprocCpp.props”,你將會看到該項目的所有配置都將添加這個屬性表,在它上面點擊右鍵,你將看到與項目相同的屬性窗口,但這個時候你編輯的是屬性表,再次將“Multi-processor Compilation”設為“YES”。關閉屬性窗口,在屬性管理器中選擇屬性表,然後點擊“保存”。
現在可以在編輯器中打開新建的MultiprocCpp.props文件,我的看起來如下:
圖11 在編輯器中打開MultiprocCpp.props
仔細查看這個項目文件,你可以看到屬性表通過一個Import標簽應用到每個配置中了,這一點和C++中的#include非常類似。
圖12 通過Import引用屬性表
現在我們就可以重用之前在項目文件中的定義了,於是我可以在屬性管理器中選中多個項目,然後點擊右鍵,選擇“添加現有屬性表”。
圖13 為多個項目同時指定屬性表
OK!現在所有項目編譯時都帶有/MP參數了。
在某些情況下,你可能想要更簡單一點,例如,你可能想要刪除大量的屬性表,幸運的是,MSBuild 4.0有一個強大的,完整的對象模型,你可以使用它,再編寫幾行代碼就可以搞定這種工作了。
如果你不想通過圖形界面進行設置,完全可以自己手動進行編輯,例如,在VS自己的構建中,我們在每個項目的頂級都設置了一套屬性。
圖14 手動設置項目頂級屬性
在這裡我們定義了所有類型的全局設置,並導入了其它設置,我將在以後的文章中介紹組織大型構建樹的方法。
並行數量太多了也不好
一般來說,利用完所有處理器或處理器核心就已經足夠了,否則可能會導致機器變慢甚至崩潰,下圖就顯示了這樣一個例子。
圖15 太多的並行構建進程很容易讓機器崩潰
我是在一台8 CPU的機器上做的這個實驗,我把解決方案中的所有項目全部開啟/MP了,然後使用msbuild.exe /m進行構建(我使用命令行進行構建不會出現這個問題,但在Visual Studio中進行構建就會出現),如果相關依賴不能阻止它,MSBuild將立即啟動8個項目,每個CL將會一次運行自己的8個實例,因此總共會有64個CL運行考驗我們的處理器核心和磁盤,這樣做不但不能提升速度,反倒會使性能急劇下降。
你可能希望有一天系統能夠實現自我調整,但如果現在遇到這樣的問題,你不得不手工調整。下面是一些建議:
◆將全局值設小一點
例如將/m:4減少到/m:3,或使用屬性表將/MP修改為/MP2,如果你的構建中還有其它問題,如有許多的並行項目,但並行的CL不夠,反之亦然,這個時候你都應該將全局並行構建參數調小。
◆為每個項目和配置調整/MP
有些時候使用/MP可能不是最佳的辦法,你也可以通過配置進行調整,Retail配置可能會使速度變得更慢,因為編譯器要做的優化更多了,為Retail開啟/MP而不是為Debug開啟/MP可能更有意義。
◆獲得超級定制
在你的團隊中,你可能有一系列硬件,也許你的開發人員使用的是雙CPU機器,但夜間構建是在一台8 CPU的機器上進行的,兩者構建時需要的來源是一樣的,你希望兩者的速度都不能太慢,在這種情況下,你可以使用環境變量,或是在MSBuild標簽上設置條件,幾乎所有MSBuild標簽都可以設置條件。
下面是一個例子,當“MultiprocCLCount”有一個大於零的值時,就可以使用這個值啟用/MP。
圖16 通過環境變量調整處理器數量
MSBuild啟動時將所有環境變量的值作為初始屬性值,因此在我更快速的機器上,我將MultiprocCLCount的值設為8,而在我的開發用機上,我將其設為2。
類似的方法還可以應用到MSBuild.exe的/m參數中,如/m:%MultiprocMSBuildCount%,
在外來條件中還有其它屬性可能很有用,如$(Number_Of_Processors)表示邏輯處理核心數量,它來自環境變量。$(MSBuildNodeCount)是傳遞給msbuild.exe /m參數的值,在Visual Studio中,這個值是通過“工具”*“選項”進行設置的。
最後,我希望你能有效利用/m和/MP。希望你對我介紹的MSBuild功能能進一步深入學習,最好自己動手配置一次。