找到編譯時間的瓶頸C++ Build Insights
提供了多種分析C++編譯時間的方法。在本文中,我們將會討論可用來識別編譯過程中的瓶頸的兩種方法:
1) 使用vcperf工具進行手動分析。
2) 使用C++ Build Insights SDK以程序化的方式進行分析。
我們提供了一個案例研究,展示瞭如何使用這些工具來加快Git for Windows這一開源項目的編譯速度。我們希望這些教程在分析你自己的工程編譯時能派上用場。
使用vcperf
vcperf工具可以用來捕獲工程編譯過程的信息並在Windows Performance Analyzer(WPA)工具中進行查看。
具體步驟如下:
1. 下載最新版本的Visual Studio 2019。
2. 從最新版本的Windows ADK中獲取WPA工具。
3. 按照vcperf的文檔說明,將WPA配置為與C++ Build Insights一起使用。在這個階段,你可以可以考慮自行編譯一份vcpef作為一個可選的步驟。
4. 打開具有管理員權限的[x64 Native Tools Command Prompt for VS 2019]。
5. 獲取工程編譯信息:
5.1 執行指令:[vcperf /start MySessionName]
5.2 在系統的任何位置編譯你的工程,甚至是不需要在Visual Studio中編譯(因為vcperf會在整個系統級別上收集工程編譯信息)。
5.3 執行指令:[vcperf /stop MySessionName outputFile.etl]。這將會將工程編譯信息拷貝到outputFile.etl文件中。
6. 在WPA工具中打開收集到的編譯信息。
在WPA中使用Build Explorer視圖
首次在WPA中打開編譯信息時,你需要做的第一件事就是打開[BuildExplorer]視圖。可以通過將其從[GraphExplorer]窗口拖到[Analysis]窗口中來進行操作,如下圖所示:
Build Explorer視圖提供了4個預設選項,您可以在瀏覽工程編譯信息時選擇需要的視圖:
1. Timelines
2. Invocations
3. Invocation Properties
4. Activity Statistics
可以通過點擊視圖頂部的下拉菜單來選擇你需要的視圖,如下圖所示:
接下來,我們將對這4個預設視圖分別進行講解。
預設1: Timelines
Timelines預設顯示了在工程編譯過程中如何安排並行調用的時間。每條時間軸都代表一個虛擬線程以及有是哪項任務執行在該線程上。在多個線程上執行的調用將佔用多條時間軸。
需要注意的是,僅從Visual Studio 2019版本16.4開始,才可以使用精確的並行代碼生成功能。在早期版本中,給定的編譯器或連接器調用的所有代碼生成都是放在一條時間軸上。
查看Timelines預設時,將鼠標懸停在彩色條上可以查看其對應的調用。下圖顯示了將鼠標懸停在第5條時間軸上的條形上方時發生的情況。
預設2: Invocations
Invocations預設在其自己的時間軸上顯示每個調用,而與並行性無關。它更詳細地介紹了調用中發生的細節。使用此預設,將鼠標懸停在彩色條上會顯示調用在任何時間點正在處理的活動。在下面的示例中,我們可以看到鏈接器調用58中的綠色條對應於整個程序的分析活動,即鏈接時間代碼生成的一個階段。我們還可以看到連接器調用58的輸出是c2.dll。
預設3: Invocation Properties
InvocationProperties預設在視圖底部的表中顯示每個調用的各種屬性。你可以從中找到感興趣的調用,然後查看有關它的各種信息,例如:
>編譯器或者鏈接器的版本。
>當前工作目錄
>重要的環境變量,例如PATH或_CL_。
>編譯過程中使用到的完整命令行,包括來自響應(.RSP)文件或環境變量的參數。
請注意,如果命令行或環境變量太長,則有時會在多個條目中顯示它們。
預設4: Activity Statistics
ActivityStatistics預設顯示BuildExplorer視圖跟蹤的所有工程編譯活動的彙總統計信息。例如,使用它可瞭解所有鏈接器和編譯器調用的總持續時間,或者判斷工程的編譯時間是否受到了解析或代碼生成的影響。在此預設下,視圖的圖形部分顯示每個活動的活動時間,而表格部分顯示合計的持續時間總計,可以通過活動的詳細信息來查看該活動的所有實例。圖形,表格和下拉列表的界面如下圖所示。
一個實際案例
在這個實際案例研究中,我們使用了來自GitHub的真實開源項目,並展示瞭如何發現並修復工程編譯時間的瓶頸。
具體步驟如下:
1.搜索並[GitforWindowsGitHub]倉庫到本地。
2.切換到[vs/master]分支。
3.從工程的根目錄開始,打開[git\git.sln]解決方案。
4.生成x64版本配置。這將提取所有程序包依賴關係並進行一次完整的工程編譯。
5.獲取有關工程編譯的信息:
5.1在PATH上使用vcperf打開具備管理員權限的命令提示符。
5.2運行以下命令:vcperf/startGit
5.3在VisualStudio中重新編譯[x64Release]版本的[git\git.sln]解決方案。
5.4運行以下命令:vcperf/stopGitgit.etl。這將在git.etl文件中保存工程編譯的所有信息。
6.在WPA中打開編譯信息。
我們使用BuildExplorer視圖的Timelines預設,並立即注意到了長時間運行的調用似乎是工程編譯開始時的瓶頸。如下圖所示:
我們查看該調用的屬性,並注意命令行中沒有使用到/MP編譯開關,這個開關用來啟用編譯器的並行執行。我們還從[WorkingDirectory]屬性中注意到,正在編譯的項目稱為[libgit]。
我們可以在VisualStudio裡為工程libgit上啟用/MP編譯開關,如下圖所示:
接下來,我們可以使用本節開頭的步驟來重新捕獲一次工程編譯信息,以此來確認我們是否已經解決了這個編譯瓶頸問題。我們發現:構建時間從大約120秒減少到80秒,編譯性能提高了33%。
通過C++ Build Insights SDK來定位工程編譯瓶頸
使用vcperf和WPA手動執行的大多數分析任務也可以使用C++ Build Insights SDK以程序化的方式執行。
為了說明這一點,我們準備了[BottleneckCompileFinder]SDK示例。當找到未使用/MP編譯開關的編譯器調用瓶頸時,它將發出警告。
如果沒有其他編譯器或連接器調用同時被調用,則該調用被視為瓶頸。
讓我們重複上一節中的Windows版Git案例研究,但是這次通過使用BottleneckCompileFinder來查看發現的編譯瓶頸。
具體步驟如下:
1. 搜索並克隆C++ Build Insights SDK示例倉庫。
2. 針對所需的體系結構(x86或x64)並使用所需的配置(調試或發行版),編譯Samples.sln解決方案。編譯出來的可執行文件將從倉庫的根目錄開始放置到[out/{architecture}/{configuration}/BottleneckCompileFinder]文件夾中。
3. 按照上一章節的步驟進行操作並收集工程編譯的信息。停止信息收集時,請使用[/ stopnoanalyze]命令而不是[/stop]命令。
4. 將收集的信息作為第一個參數傳遞給BottleneckCompileFinder可執行文件。
如下圖所示,BottleneckCompileFinder可以正確識別libgit項目併發出了警告。它還標識了另一個:xdiff,儘管它對工程編譯的時間影響很小而不需要採取任何改進措施。
示例代碼解析
我們首先要求C++BuildInsightsSDK將我們需要的內容轉發給OnStartInvocation,OnStopInvocation和OnCompilerCommandLine函數,從而過濾所有開始活動,停止活動和簡單事件。函數的名稱對C++BuildInsightsSDK如何過濾事件沒有影響,只有它們的參數很重要。如以下代碼所示:
我們的OnCompilerCommandLine函數跟蹤所有不使用/MP編譯開關的編譯器調用。如果這些調用是瓶頸,則稍後將使用此信息來發出有關這些調用的警告。
我們的OnStartInvocation和OnStopInvocation函數通過在啟動時將它們添加到哈希圖中,並在停止時將其刪除來跟蹤併發運行的調用。一旦同時激活兩個調用,我們認為所有其他調用不再是瓶頸。如果在達到停止事件後將編譯器調用標記為瓶頸,則意味著在運行時再也不會啟動另一個調用。如果這些調用未使用/MP編譯開關,則會警告用戶。
總結
小哥我目前的工程的整體編譯時間不足30秒,所以,應該還不是什麼大的問題。
故,以上內容,獻給編譯一次可以喝一杯咖啡的大佬。