使用C++ Build Insights找到編譯過程中的瓶頸

找到編譯時間的瓶頸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]窗口中來進行操作,如下圖所示:

使用C++ Build Insights找到編譯過程中的瓶頸

Build Explorer視圖提供了4個預設選項,您可以在瀏覽工程編譯信息時選擇需要的視圖:

1. Timelines

2. Invocations

3. Invocation Properties

4. Activity Statistics

可以通過點擊視圖頂部的下拉菜單來選擇你需要的視圖,如下圖所示:

使用C++ Build Insights找到編譯過程中的瓶頸

接下來,我們將對這4個預設視圖分別進行講解。

預設1: Timelines

Timelines預設顯示了在工程編譯過程中如何安排並行調用的時間。每條時間軸都代表一個虛擬線程以及有是哪項任務執行在該線程上。在多個線程上執行的調用將佔用多條時間軸。

需要注意的是,僅從Visual Studio 2019版本16.4開始,才可以使用精確的並行代碼生成功能。在早期版本中,給定的編譯器或連接器調用的所有代碼生成都是放在一條時間軸上。

查看Timelines預設時,將鼠標懸停在彩色條上可以查看其對應的調用。下圖顯示了將鼠標懸停在第5條時間軸上的條形上方時發生的情況。

使用C++ Build Insights找到編譯過程中的瓶頸

預設2: Invocations

Invocations預設在其自己的時間軸上顯示每個調用,而與並行性無關。它更詳細地介紹了調用中發生的細節。使用此預設,將鼠標懸停在彩色條上會顯示調用在任何時間點正在處理的活動。在下面的示例中,我們可以看到鏈接器調用58中的綠色條對應於整個程序的分析活動,即鏈接時間代碼生成的一個階段。我們還可以看到連接器調用58的輸出是c2.dll。

使用C++ Build Insights找到編譯過程中的瓶頸

預設3: Invocation Properties

InvocationProperties預設在視圖底部的表中顯示每個調用的各種屬性。你可以從中找到感興趣的調用,然後查看有關它的各種信息,例如:

>編譯器或者鏈接器的版本。

>當前工作目錄

>重要的環境變量,例如PATH或_CL_。

>編譯過程中使用到的完整命令行,包括來自響應(.RSP)文件或環境變量的參數。

請注意,如果命令行或環境變量太長,則有時會在多個條目中顯示它們。

使用C++ Build Insights找到編譯過程中的瓶頸

預設4: Activity Statistics

ActivityStatistics預設顯示BuildExplorer視圖跟蹤的所有工程編譯活動的彙總統計信息。例如,使用它可瞭解所有鏈接器和編譯器調用的總持續時間,或者判斷工程的編譯時間是否受到了解析或代碼生成的影響。在此預設下,視圖的圖形部分顯示每個活動的活動時間,而表格部分顯示合計的持續時間總計,可以通過活動的詳細信息來查看該活動的所有實例。圖形,表格和下拉列表的界面如下圖所示。

使用C++ Build Insights找到編譯過程中的瓶頸

使用C++ Build Insights找到編譯過程中的瓶頸

使用C++ Build Insights找到編譯過程中的瓶頸

一個實際案例

在這個實際案例研究中,我們使用了來自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預設,並立即注意到了長時間運行的調用似乎是工程編譯開始時的瓶頸。如下圖所示:

使用C++ Build Insights找到編譯過程中的瓶頸

我們查看該調用的屬性,並注意命令行中沒有使用到/MP編譯開關,這個開關用來啟用編譯器的並行執行。我們還從[WorkingDirectory]屬性中注意到,正在編譯的項目稱為[libgit]。

使用C++ Build Insights找到編譯過程中的瓶頸

我們可以在VisualStudio裡為工程libgit上啟用/MP編譯開關,如下圖所示:

使用C++ Build Insights找到編譯過程中的瓶頸

接下來,我們可以使用本節開頭的步驟來重新捕獲一次工程編譯信息,以此來確認我們是否已經解決了這個編譯瓶頸問題。我們發現:構建時間從大約120秒減少到80秒,編譯性能提高了33%。

使用C++ Build Insights找到編譯過程中的瓶頸

通過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++ Build Insights找到編譯過程中的瓶頸

示例代碼解析

我們首先要求C++BuildInsightsSDK將我們需要的內容轉發給OnStartInvocation,OnStopInvocation和OnCompilerCommandLine函數,從而過濾所有開始活動,停止活動和簡單事件。函數的名稱對C++BuildInsightsSDK如何過濾事件沒有影響,只有它們的參數很重要。如以下代碼所示:

使用C++ Build Insights找到編譯過程中的瓶頸

我們的OnCompilerCommandLine函數跟蹤所有不使用/MP編譯開關的編譯器調用。如果這些調用是瓶頸,則稍後將使用此信息來發出有關這些調用的警告。

使用C++ Build Insights找到編譯過程中的瓶頸

我們的OnStartInvocation和OnStopInvocation函數通過在啟動時將它們添加到哈希圖中,並在停止時將其刪除來跟蹤併發運行的調用。一旦同時激活兩個調用,我們認為所有其他調用不再是瓶頸。如果在達到停止事件後將編譯器調用標記為瓶頸,則意味著在運行時再也不會啟動另一個調用。如果這些調用未使用/MP編譯開關,則會警告用戶。

使用C++ Build Insights找到編譯過程中的瓶頸

使用C++ Build Insights找到編譯過程中的瓶頸

總結

小哥我目前的工程的整體編譯時間不足30秒,所以,應該還不是什麼大的問題。

故,以上內容,獻給編譯一次可以喝一杯咖啡的大佬。


使用C++ Build Insights找到編譯過程中的瓶頸


分享到:


相關文章: