分享方式:


案例研究:隔離效能問題 (C#、Visual Basic、F#)

使用分析工具調查效能問題並隔離問題區域。 本案例研究使用具有效能問題的範例應用程式,來示範如何使用分析工具提高效率。 如果要比較分析工具,請參閱我應該選擇哪一個工具?

本案例研究涵蓋以下主題:

  • 如何使用 Visual Studio 分析工具來分析應用程式效能。
  • 如何解釋這些工具提供的資料,找出效能瓶頸。
  • 如何套用實用策略來最佳化程式碼,著重於 .NET 計數器、呼叫計數和計時資料。

遵循這些技術,然後將其套用到您自己的應用程式中,使其更有效率且符合成本效益。

隔離效能問題案例研究

本案例研究中的範例應用程式是一個 ASP.NET 應用程式,其會對模擬資料庫執行查詢。 此範例以診斷範例為基礎。

該範例應用程式的主要效能問題在於效率不佳的程式碼模式。 該應用程式存在一個效能瓶頸,會嚴重影響其效率。 該問題包括以下症狀:

  • 低 CPU 使用率:應用程式顯示低 CPU 使用率,指出 CPU 不是瓶頸。

  • 高執行緒集區執行緒計數:執行緒計數相對高並平穩上升,這表示執行緒集區耗盡。

  • 應用程式回應緩慢:應用程式因缺少可用執行緒處理新的工作項目而回應緩慢。

本案例研究的目的是透過使用 Visual Studio 的分析工具來分析應用程式的效能,以解決這些問題。 透過了解該如何以及該在哪裡提升應用程式效能,開發人員可以實施最佳化以加快程式碼執行速度並提高效率。 最終目標是增強應用程式的整體效能,使其運作起來更有效率且更具成本效益。

挑戰

要解決範例 .NET 應用程式中的效能問題會面臨一些挑戰。 這些挑戰源自於診斷效能瓶頸的複雜性。 解決所述問題的主要挑戰如下:

  • 診斷效能瓶頸:其中一個主要挑戰是要能準確找出效能問題的根本原因。 低 CPU 使用率加上緩慢的效能可能是有多個影響因素。 開發人員必須有效地使用分析工具來診斷這些問題,這需要其對這些工具的運作方式以及如何解釋其輸出有一定的了解。

  • 知識和資源限制:團隊可能面臨與知識、專業知識和資源相關的限制。 分析和最佳化應用程式需要特定的技能和經驗,且並非所有團隊都可以立即存取這些資源。

需要使用策略方法來解決這些挑戰,將有效使用分析工具、技術知識以及仔細規劃和測試結合起來。 該案例研究的目的在於引導開發人員完成此程序,提供策略和深入解析來克服這些挑戰並提高應用程式效能。

策略

以下是本案例研究中方法的高級檢視:

  • 我們透過收集效能資料同時監看 .NET 計數器計量,開始進行調查。 如同 CPU 使用率工具,Visual Studio 的 .NET 計數器工具也是進行效能調查的良好起點。
  • 接下來,為了更多深入解析來協助隔離問題或改善效能,請考慮使用其他分析工具來收集追蹤資料。 例如,使用檢測工具查看呼叫計數和計時資料。

資料收集需要以下工作:

  • 將應用程式設定為發行組建。
  • 從效能分析工具選取 .NET 計數器工具 (Alt+F2)。 (後續步驟需要使用檢測工具。)
  • 從效能分析工具中,啟動應用程式並收集追蹤。

查看效能計數器

在執行應用程式時,我們會觀察 .NET 計數器工具中的計數器。 針對初始調查,要留意的幾個關鍵計量包括:

  • CPU Usage. 觀察此計數器,了解效能問題是否發生在 CPU 使用率高或低的情況。 這可作為特定類型效能問題的線索。 例如:
    • 如果 CPU 使用率高,請使用 CPU 使用率工具找出我們可能可以優化程式碼的區域。 如需這方面的教學課程,請參閱案例研究:最佳化程式碼的初學者指南
    • CPU 使用率低時使用檢測工具,根據實際經過時間識別呼叫計數和平均函式時間。 這可以協助您找出如爭用或執行緒集區耗盡等問題。
  • Allocation Rate. 若為提供要求的 Web 應用程式,使用率應該相當穩定。
  • GC Heap Size. 請觀察此計數器,了解記憶體使用量是否持續增加且可能流失。 如果看起來很高,請使用其中一個記憶體使用量工具。
  • Threadpool Thread Count. 若為提供要求的 Web 應用程式,請觀察此計數器,了解執行緒計數是否保持穩定或以穩定速率增加。

以下範例顯示 CPU Usage 偏低,而 ThreadPool Thread Count 則相對較高。

.NET 計數器工具顯示計數器的螢幕擷取畫面。

CPU 使用率低,加上執行緒計數穩定增加,可能是執行緒集區耗盡的指標。 執行緒集區被迫必須持續旋轉新執行緒。 如果集區沒有可用的執行緒來處理新的工作項目,會發生執行緒集區耗盡,而且通常會導致應用程式回應緩慢。

有鑑於 CPU 使用率低及執行緒計數相對較高,根據執行緒集區耗盡可能案例的理論推測,切換至使用檢測工具。

調查通話計數和計時資料

現在來看一下檢測工具的追蹤記錄,看看是否能設法深入了解執行緒的情況。

使用檢測工具收集追蹤資料並將其載入到 Visual Studio 後,我們先檢查顯示摘要資料的初始 .diagsession 報告頁面。 在收集的追蹤資料中,我們使用報告中的開啟詳細資料連結,然後選取 Flame Graph

檢測工具中 Flame Graph 的螢幕擷取畫面。

Flame Graph 視覺效果顯示 QueryCustomerDB 函式 (以黃色顯示) 在應用程式的執行時間上佔相當重要的比例。

以滑鼠右鍵按一下 QueryCustomerDB 函式,然後選擇 [呼叫樹狀結構檢視]

檢測工具中呼叫樹狀結構的螢幕擷取畫面。

應用程式中 CPU 使用量最高的程式碼路徑稱為「最忙碌路徑」。 最忙碌路徑火焰圖示 (顯示「最忙碌路徑」圖示的螢幕擷取畫面。) 可協助快速識別可以改進的效能問題。

呼叫樹狀圖檢視中,您可以看到最忙碌路徑包含 QueryCustomerDB 函式,其指向潛在的效能問題。

相對於其他函式所花費的時間,QueryCustomerDB 函式的 SelfAvg Self 值非常高。 有別於 TotalAvg TotalSelf 值會排除其他函式所花費的時間,所以很適合尋找效能瓶頸。

提示

如果 Self 值相對較低,而不是偏高,不妨查看 QueryCustomerDB 函式呼叫的實際查詢。

按兩下 QueryCustomerDB 函式,顯示函式的原始程式碼。

public ActionResult<string> QueryCustomerDB()
{
    Customer c = QueryCustomerFromDbAsync("Dana").Result;
    return "success:taskwait";
}

我們做了一些研究。 或者,我們可以節省時間讓 Copilot 為我們進行研究

如果我們使用 Copilot,請從操作功能表中選取 [ 詢問 Copilot ],然後輸入下列問題:

Can you identify a performance issue in the QueryCustomerDB method?

提示

您可以使用 slash 命令,例如 /optimize 協助 Copilot 建構良好的問題。

Copilot 告訴我們,此程式代碼在不使用 await 的情況下呼叫異步 API。 這是非同步中的同步程式碼模式,是 ThreadPool 耗盡常見的原因,可能封鎖執行緒。

若要解決,請使用 await。 在此範例中,Copilot 會提供下列程式代碼建議以及說明。

public async Task<ActionResult<string>> QueryCustomerDB()
{
    Customer c = await QueryCustomerFromDbAsync("Dana");
    return "success:taskwait";
}

如果您發現與資料庫查詢相關的效能問題,可以使用資料庫工具調查特定呼叫是否較慢。 此資料可能代表有機會將查詢最佳化。 如需示範如何使用資料庫工具來調查效能問題的教學課程,請參閱案例研究:最佳化程式碼的初學者指南。 資料庫工具支援以 ADO.NET 或 Entity Framework Core 使用 .NET Core。

若要在 Visual Studio 取得個別執行緒行為的視覺效果,可以在偵錯時使用 [平行堆疊] 視窗。 此視窗顯示個別執行緒,以及等待中的執行緒、它們所等待的執行緒,以及鎖死的相關資訊。

如需執行緒集區耗盡的其他資訊,請參閱偵錯 ThreadPool 耗盡

下一步

下列文章和部落格文章提供詳細資訊,可協助您了解如何有效地使用 Visual Studio 效能工具。