程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 線程性能 - Visual Studio 2010中的資源爭用並發分析

線程性能 - Visual Studio 2010中的資源爭用並發分析

編輯:關於.NET

隨著多核處理器變得越來越常見,軟件開發人員就需要構建多線程應用程序 ,從而利用更多的處理能力來實現更高的性能。借助並行線程的強大功能,您可 以將整個工作劃分為多項單獨的任務,並以並行模式執行這些任務。

但是,線程經常需要相互通信才能完成任務;而且根據算法或數據訪問的要 求,有時還需要同步線程的行為。例如,應該以互斥的方式授予線程對同一數據 的同時寫訪問權限,以避免數據損壞。

同步操作通常是通過使用共享的同步對象來完成的。對於獲得該對象的線程 ,將授予其對敏感代碼或數據的共享或獨占訪問權限。當不再需要訪問權限時, 該線程將放棄所有權,而其他線程就可以嘗試獲取訪問權限。根據所使用的同步 類型,同時請求所有權可能會使多個線程能夠同時訪問共享資源,也可能會阻止 某些線程,直到共享對象從上一次獲取中釋放為止。具體的示例包括:C/C++ 中 使用 EnterCriticalSection 和 LeaveCriticalSection 訪問例程的關鍵部分, C/C++ 中的 WaitForSingleObject 函數,以及 C# 中的鎖定語句和 Monitor 類 。

選擇同步機制時必須謹慎,因為不恰當的線程同步不但不能提高性能,反而 會降低性能,這與多線程的目標背道而馳。因此,能否檢測由於鎖爭用沒有進展 而導致線程被阻止的情況,就顯得愈加重要。

Visual Studio 2010 中的性能工具包含一種新的分析方法“資源爭用分析” ,此方法有助於檢測線程間的並發爭用。在 John Robbins 的 Wintellect 博客 文章中,您可以看到對此功能的精彩簡介,其網址為: wintellect.com/CS/blogs/jrobbins/archive/2009/10/19/vs-2010-beta-2- concurrency-resource-profiling-in-depth-first-look.aspx。

在本文中,我將演練一個爭用分析調查,並講解可以使用 Visual Studio 2010 IDE 和命令行工具收集的數據。此外,還將向您展示如何在 Visual Studio 2010 中分析數據,您會看到,在執行爭用調查時,如何從一個分析視圖 切換到另一個分析視圖。然後,我會修改代碼,並將修改過的應用程序的分析結 果與原來的分析結果進行比較,驗證所做的修改確實減少了爭用的數量。

從問題開始

作為示例,我將使用 Hazim Shafi 在其博客文章“性能模式 1:識別鎖爭用 ”(blogs.msdn.com/hshafi/archive/2009/06/19/performance-pattern-1- identifying-lock-contention.aspx) 中使用的同一個矩陣乘法應用程序。盡管 代碼示例是用 C++ 編寫的,但是我討論的概念同樣適用於托管代碼。

示例矩陣乘法應用程序使用多個線程對兩個矩陣執行乘法操作。每個線程都 將承擔一部分工作,並運行以下代碼段:

for (i = myid*PerProcessorChunk;
    i < (myid+1)*PerProcessorChunk;
    i++) {
  EnterCriticalSection(&mmlock);
  for (j=0; j<SIZE; j++) {
   for (k=0; k<SIZE; k++) {
    C[i][j] += A[i][k]*B[k][j];
   }
  }
  LeaveCriticalSection(&mmlock);
}

每個線程都有自己的 ID (myid),並且負責使用矩陣 A 和 B 作為輸入,來 計算結果矩陣 C 中的行數(一行或多行)。深入的代碼檢測表明,沒有發生真 正引起歧義的寫共享,每個線程都寫入 C 的不同行。然而,開發人員還是決定 使用關鍵部分來保證對矩陣的賦值。我要感謝開發人員的這個決定,因為這給了 我一個好機會,來展示新的 Visual Studio 2010 性能工具能夠輕松發現冗余的 同步。

分析數據集合

假設您有一個 Visual Studio 項目,其中的代碼如上文所示(但這不是必需 的,因為您可以將分析器連接到任何一個處在運行中的應用程序),您單擊“分 析”菜單上的“啟動分析向導”開始進行爭用分析。

在該向導的第一個頁面上(如圖 1 所示),選擇“並發”,並確保選中“收 集資源爭用數據”選項。請注意,資源爭用並發分析適用於任何版本的 Windows 操作系統。但“顯示多線程應用程序的行為”選項僅適用於 Windows Vista 或 Windows 7。

圖 1 啟用並發資源分析

在該向導的第二個頁面上,確保目標是當前項目。在該向導的最後一個頁面 上,確保選中“在向導完成後啟動分析”選項,然後單擊“完成”。應用程序就 會開始運行並由分析器進行分析。當應用程序退出後,“性能資源管理器”窗口 中將顯示分析數據文件(請參見圖 2)。

圖 2 性能資源管理器中的性能分析結果文件

分析報告將在 Visual Studio 中自動打開,並在“摘要”視圖中顯示性能調 查結果,如圖 3 所示。

圖 3 分析報告的“摘要”視圖

分析數據分析

並非所有同步都會導致鎖爭用。如果有一個鎖可用,則嘗試獲取該鎖的所有 權不會阻止線程執行,也不會發生爭用。在“資源爭用分析”模式下,分析器只 會收集導致爭用的同步事件數據,而不會報告成功的(未被阻止的)資源獲取操 作。如果您的應用程序沒有導致任何爭用,也就不會收集到任何數據。如果您得 到了數據,則意味著您的應用程序存在鎖爭用現象。

對於每次爭用,分析器會報告被阻止的線程、發生爭用的位置(資源和調用 堆)、發生爭用的時間(時間戳),以及被阻止的線程嘗試執行某些操作的時間 量(時間長度),這些操作包括獲取鎖、進入關鍵部分、等待單個對象等。

當您打開該文件時,首先會看到“摘要”視圖(圖 3),其中包含三個主要 區域,可供您進行簡單的診斷:

爭用圖顯示了每秒鐘發生的爭用次數,將在應用程序的生命周期內繪制。您 可以直觀地檢查爭用峰值,也可以選擇一個時間間隔,以便詳細觀察爭用或篩選 結果。篩選操作將再次對數據進行分析,並刪除所選時間間隔以外的數據。

“爭用最多的資源”表列出了導致所檢測到的大部分爭用的資源。

“爭用最多的線程”表列出了發生爭用次數最多的線程。此表使用爭用次數 而不是爭用時間長度作為標准。因此,可能會有一個線程長時間被單個爭用所阻 止,但該線程不會顯示在“摘要”視圖中。另一方面,如果一個線程經歷了大量 非常短的爭用,每次爭用都只將該線程阻止很短時間,則該線程會出現在“摘要 ”視圖中。

如果您看到某個資源導致了大部分爭用,則應該更詳細地檢查該資源。如果 您觀察到某個線程經歷了大量您沒有預期到的爭用,也應該檢查該線程的爭用情 況。

例如,在圖 3 中,您可以看到,關鍵部分 1 導致了該應用程序中的幾乎所 有 (99.90%) 的爭用。讓我們對該資源進行深入調查。

“摘要”視圖上的資源名稱和線程 ID 是超鏈接。單擊“關鍵部分 1”會轉 到“資源詳細信息”視圖(請參見圖 4),並將上下文設置為特定的資源:關鍵 部分 1。

圖 4 “資源詳細信息”視圖

資源詳細信息

“資源詳細信息”視圖的上半部分顯示了一個基於時間的圖表,其中每條水 平線都屬於一個線程。除非您在代碼中對托管線程進行命名(例如通過使用 C# System.Threading.Thread.Name 屬性),否則這些線條將由線程的根函數來定 義標簽。此類線條上的方塊代表相應線程發生的資源爭用。方塊長度表示爭用時 間長度。不同線條上的方塊在時間上可能會重疊,這意味著幾個線程同時被該資 源阻止。

“總計”線條比較特殊。它不屬於任何特定的線程,但包含所有線程對該資 源的所有爭用(它實際上是爭用方塊在該線條上的投影)。正如您所看到的,關 鍵部分 1 相當繁忙,它在“總計”線條上似乎已經沒有任何空位。

您可以使用鼠標左鍵來選擇時間范圍(在圖表中左鍵單擊所需的開始點,然 後向右拖動指針),從而放大到特定的圖表區域。圖表的右上部有兩個鏈接:“ 縮放重置”和“縮小”。“縮放重置”可恢復原始圖表視圖;“縮小”則會一步 步後退,按照您放大的過程逐漸取消縮放。

總體的爭用方塊圖案可能會幫助您對於應用程序的執行情況得出一些結論。 例如,您可以看到,各個線程的爭用在時間上大量重疊,表示這不是最優的並行 化方案。每個線程被該資源阻止的時間遠超過了其運行的時間,而這也是應用程 序缺乏效率的另一個標志。

函數詳細信息

“資源詳細信息”視圖的底部是一個爭用調用堆,只有當您選擇了特定的爭 用時,此處才會顯示數據。如果您選擇了某個方塊,相應的堆就會顯示在底部面 板中。您也可以懸停在圖表上的某個爭用方塊上,而不單擊該方塊,這將彈出一 個窗口,向您顯示堆和爭用時間長度信息。

正如您所看到的,爭用調用堆中列出了一個名為 MatMult 的示例應用程序函 數,這樣您就知道它是導致爭用的原因。若要確定是哪一行函數代碼導致了爭用 ,請在調用堆面板中雙擊函數名稱。這將顯示“函數詳細信息”視圖,如圖 5 所示。

圖 5 “函數詳細信息”視圖

在此視圖中,您會看到名為 MatMult 的函數的圖形化表示,以及在此函數內 部調用的函數。視圖底部清楚地顯示出,EnterCriticalSection(&mmlock) 導致了所有的線程被阻止的情況。

當您知道是哪一行代碼導致了爭用後,您可能會重新考慮您的決定:是否應 該按這種方式來實現同步?這是保護代碼的最好方式嗎?真的需要保護嗎?

在示例應用程序中,在此代碼中使用關鍵部分是不必要的,因為線程不會共 享對同樣的結果矩陣行的寫權限。利用 Visual Studio 性能工具,您可以將使 用 mmlock 的語句變成注釋,從而大大提高應用程序的速度。要是始終都這麼簡 單就好了!

線程詳細信息

如上文所述,“摘要”視圖為您的調查提供了一個良好的起點。通過查看“ 爭用最多的資源”和“爭用最多的線程”表,您可以決定如何繼續調查。如果您 發現其中某個線程看起來很可疑,因為您認為它不應該出現在爭用最多的線程列 表中,您就可能會決定深入檢查該線程。

在“摘要”視圖上單擊該線程 ID,即可跳轉到“線程詳細信息”視圖(請參 見圖 6)。盡管看起來與“資源詳細信息”視圖相似,但是此視圖具有不同的含 義,因為它會在所選線程的上下文中顯示爭用情況。每條水平線條代表該線程在 其生命周期內爭用的一種資源。在此圖表上,您不會看到爭用方塊在時間上重疊 ,因為這意味著同一個線程在同一時間被多個資源阻止。

圖 6 包含所選爭用方塊的“線程詳細信息”視圖

請注意,WaitForMultipleObjects(此處未顯示)是單獨處理的,對於一組 對象,它將用一條線條來表示。這是因為,分析器將 WaitForMultipleObjects 的所有參數對象當作一個實體。

您能夠在“資源詳細信息”視圖中執行的任何操作(縮放圖表、選擇特定的 爭用,以及查看以毫秒為單位的時間長度和調用的堆)也都適用於“線程詳細信 息”視圖。在“爭用調用堆”面板中雙擊函數名稱,即可導航到該函數的“函數 詳細信息”視圖。

在示例中,您可以看到,該線程在執行前期被阻止的時間超過了其運行的時 間,然後它會被一組多個句柄長時間阻止。最後一個方塊是由於等待其他線程完 成而引起的,但是前期的爭用表明線程的使用並非最優,從而導致線程處於被阻 止狀態的時間比處於運行狀態的時間還要長。

找出問題

您可能已經注意到,圖表軸上的標簽都是超鏈接。這樣就可以在資源和線程 的詳細視圖之間來回切換,並且每次都能為視圖設置所需的上下文。這在交互式 查找和解決問題的過程中非常實用。例如,您可以對阻止了許多線程的資源 R1 進行檢查。然後從“資源詳細信息”視圖轉到線程 T1 的詳細信息視圖,並發現 它不僅被 R1 阻止,有時還會被資源 R2 阻止。接著,您可以深入了解 R2 的詳 細信息,並觀察被 R2 阻止的所有線程。接下來,您可以單擊線程 T2 的標簽, 集中注意力來檢查阻止了 T2 的所有資源;如此這般。

對於在任意給定時間誰持有鎖這個問題,爭用分析數據不會給出明確的答案 。但是倘若線程之間對同步對象的使用是公平的,並且您對應用程序的行為有所 了解,則通過在資源詳細信息與線程詳細信息之間來回切換,研究其數據,您就 可以找出可能的鎖持有者(成功獲取同步鎖的線程)。

例如,假設在“線程詳細信息”視圖中,您看到線程 T 在時間 t 處被資源 R 阻止。您可以單擊 R 的標簽以切換到 R 的“資源詳細信息”視圖,然後查看 在應用程序生命周期內被 R 阻止的所有線程。在時間 t 處,您會看到被 R 阻 止的許多線程(包括 T)。而在時間 t 處未被 R 阻止的線程就可能是鎖持有者 。

我在上文中說過,圖表的“總計”線條是所有爭用方塊的投影。“總計”標 簽也是一個超鏈接,但它會使您從“資源詳細信息”視圖轉到“爭用”視圖(請 參見圖 7),該視圖包含每種資源對應的爭用調用樹。並且,適當資源調用樹的 熱路徑已激活。該視圖在資源調用樹中顯示了每種資源及每個節點(函數)的爭 用和阻止時間統計信息。與其他視圖不同,該視圖將爭用堆聚集到資源調用樹中 (就像在其他分析模式下),並且會提供應用程序總體運行情況的統計信息。

圖 7 對關鍵部分 1 應用了熱路徑的“爭用”視圖

在“爭用”視圖中,您可以使用上下文菜單返回任何資源的“資源詳細信息 ”視圖。指向某個資源,單擊鼠標右鍵,然後選擇“顯示爭用資源詳細信息”。 使用上下文菜單還可以執行其他有用的操作。一般來說,建議您浏覽“分析器” 視圖中的上下文菜單,因為它們非常有用!

單擊“線程詳細信息”視圖的“總計”標簽可顯示“進程”視圖,其中選定 了相應的線程(請參見圖 8)。在該視圖中,您可以查看相對於應用程序啟動時 間的線程啟動時間、線程終止時間、線程運行時間長度、線程經歷的爭用次數, 以及線程被所有爭用阻止的時間(以毫秒為單位,並且會顯示其占線程生命周期 的百分比)。

圖 8 “進程”視圖

同樣,使用上下文菜單可以返回任何線程的“線程詳細信息”視圖,方法是 :選擇所需的線程,單擊右鍵,然後選擇“顯示線程爭用詳細信息”。

另一種可行的調查流程是在文件打開時直接顯示“進程”視圖,然後通過單 擊某個可用列的標題對線程進行排序(例如,按爭用次數對線程進行排序),然 後選擇某個線程,並使用上下文菜單切換到該線程的爭用詳細信息圖表。

解決問題並比較結果

當您找到應用程序中發生鎖爭用的根本原因後,可以將 mmlock 關鍵部分變 成注釋,然後重新運行分析過程:

for (i = myid*PerProcessorChunk;
    i < (myid+1)*PerProcessorChunk;
    i++) {
  // EnterCriticalSection(&mmlock);
  for (j=0; j<SIZE; j++) {
   for (k=0; k<SIZE; k++) {
    C[i][j] += A[i][k]*B[k][j];
   }
  }
  // LeaveCriticalSection(&mmlock);
}

您預期爭用次數會減少,而代碼修改後的實際分析僅報告了一次鎖爭用,如 圖 9 所示。

圖 9 代碼修改後的分析結果的“摘要”視圖

我們還可以在 Visual Studio 中比較新的性能結果和以前的性能結果。為此 ,請在性能資源管理器中選擇這兩個文件(選擇一個文件,按住 Shift 或 Ctrl ,然後選擇另一個文件),然後單擊右鍵,並選擇“比較性能報告”。

此時將顯示一個比較報告,如圖 10 所示。在示例應用程序中,您可以看到 MatMult 函數包含的爭用次數從 1,003 降到了 0。

圖 10 比較報告

其他數據收集方法

如果您創建了性能會話用於進行“采樣”或“檢測”分析,則以後始終能將 其轉換為“並發”模式。一種快速轉換方法是使用性能資源管理器中的分析模式 菜單。只需選擇您需要的模式,就能輕松進入該模式。

您也可以通過會話“屬性”設置來進入所需的模式。在性能資源管理器中指 向您的會話,單擊右鍵以顯示上下文菜單,然後選擇“屬性”。“屬性”頁的“ 常規”選項卡可用於控制分析會話模式和其他分析參數。

當您的分析模式設置為“並發”(或“采樣”,就此處而言)後,就可以啟 動您的應用程序(如果您使用的是性能向導,則它已經在您的“目標”列表中; 否則您可以手動進行添加),或者連接到已經在運行中的應用程序。性能資源管 理器提供了用來執行這些任務的控件,如圖 11 所示。

圖 11 性能資源管理器的分析控件

Visual Studio UI 能夠自動執行收集分析數據所需的多個步驟。但也可以使 用命令行工具來收集分析數據,這種方法對於自動化操作和腳本很有用。

若要在爭用分析模式下啟動應用程序,請打開 Visual Studio 命令提示符( 它將所有的分析器二進制文件都放到您的路徑中,無論是 x86 還是 x64 工具) ,然後執行以下輸入操作:

VSPerfCmd.exe /start:CONCURRENCY,RESOURCEONLY /output:<您的輸出 文件>

VSPerfCmd.exe /launch:<您的應用程序> /args:"<您的應用程序 參數>"

運行您的方案

VSPerfCmd.exe /detach

如果您的應用程序已終止,則此步驟不是必需的,但此步驟沒有任何危害, 因此您可以將其添加到腳本中。

VSPerfCmd.exe /shutdown

現在,您可以在 Visual Studio 中打開 YourOutputFile.VSP 進行分析了。

如果您的應用程序已經在運行中,則可以按照以下步驟將分析器連接到該應 用程序:

VSPerfCmd.exe /start:CONCURRENCY,RESOURCEONLY /output:<您的輸出 文件>

VSPerfCmd.exe /attach:<PID 或進程名稱>

運行您的方案

VSPerfCmd.exe /detach

VSPerfCmd.exe /shutdown

有關可用命令行選項的更詳細說明,請訪問以下網址: msdn.microsoft.com/library/bb385768(VS.100)。

您可以利用多種 Visual Studio 視圖來深入檢查所收集的數據。有些視圖會 顯示應用程序生命周期的整體概況,而另一些視圖則專門顯示特定的爭用,您可 以使用您認為最有用的視圖。

當您對分析結果進行分析時,可以借助超鏈接、雙擊或上下文菜單在不同視 圖之間切換,也可以通過下拉菜單直接切換到任何可用的視圖。圖 12 簡要介紹 了每種視圖。

圖 12 分析視圖

視圖 說明 摘要 “摘要”信息為您的調查提供了一個良好的起點。這是您看到的第一 個視圖。當分析會話結束且結果文件准備就緒之後,它會自動打開。 調用樹 一個聚集了所有爭用堆的調用樹。您可以在此看到是哪些堆導致了爭 用。 模塊 一個模塊列表,這些模塊所包含的每個函數都導致了一個爭用。每個 模塊都包含相關函數的列表以及檢測到的爭用次數。 調用方/被調用方 包含三個面板的視圖,這些面板分別顯示函數 F、調用 F 的所有函 數,以及 F 調用的所有函數(當然,僅限導致爭用的調用)。 函數 在任何爭用堆上檢測到的所有函數及其相關數據的列表。 行 源代碼中的函數行。 資源詳細信息 關於特定資源(例如鎖)的詳細信息,將顯示應用程序生命周期內被 該資源阻止的所有線程。 線程詳細信息 關於特定線程的詳細信息,將顯示阻止該線程的所有資源(例如鎖) 。 爭用 與調用樹視圖相似,但在此處,調用樹是按爭用資源劃分的。也就是 說,此視圖將顯示一組調用樹,每個樹分別包含被某種特定資源阻止的堆。 標記 自動和手動記錄的標記列表,其中每個標記都關聯了其時間戳以及 Windows 計數器的值。 進程 經檢查的進程列表,其中每個進程都具有其線程列表,而每個線程都 顯示其經歷的爭用次數以及被阻止的總時間長度。 函數詳細信息 關於特定函數的詳細信息,包括它調用的函數以及收集的數據。 IP 發生爭用的指令指針的列表(即,諸如 EnterCriticalSection、 WaitForSingleObject 等之類函數的列表,因為這是爭用實際發生的位置)。

借助 Visual Studio 中新的資源爭用分析功能,您可以發現由於在代碼中使 用線程同步而導致的性能問題,並能夠在運行時通過更改、減少或消除不必要的 同步來提高應用程序的性能。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved