1. 程式人生 > >使用 dotTrace 分析 .NET Core 程式碼問題

使用 dotTrace 分析 .NET Core 程式碼問題

0.背景

在專案開發之中,前期可能主要以保證任務完成為主,對於效能優化主要在於開發完成之後再來進行。可能在測試的時候發現部分介面的程式碼執行時間過長,但是又毫無頭緒,這個時候你就需要效能分析工具來協助你排查問題了。

常規效能分析藉助於 Visual Studio 強大的效能測試工具就可以進行分析,但是這些功能只包含在企業版當中。這個時候我們就可以使用 JetBrains 的 .NET 分析全家桶來進行這個操作了,其包含記憶體分析(dotMemory)與效能分析(dotTrace),其實他的 dotCover(單元測試) 也是挺好用的。

1.安裝與下載

1.1 下載

安裝步驟較為簡單,前往 Jetbrains 官網,找到 dotTrace ,點選下載即可。

其地址為 https://www.jetbrains.com/profiler/download/ ,選擇自己需要的安裝包形式,一般選擇 WebInstaller 進行安裝,當然這裡推薦選擇 Standalone (獨立版),直接下載執行就 OK 。

1.2 安裝

每個使用者可以免費評估使用 10 天,當然你要使用某些補丁或者啟用工具也是可以的,這裡不再詳述過程,只是注意一下(WebInstaller)在安裝的時候選擇自己需要的安裝就可以了,不需要的直接選為 Skip 跳過。

你也可以在安裝的時候選擇 "Visual Studio Integration",這樣就會與 VS 整合,在分析程式碼的時候可以快速跳轉到相應的程式碼行。

2.使用與分析

dotTrace 使用比較方便,本身支援 .NET Core 分析,分析時只是會有四種不同的分析模式,這裡大概講解一下各種分析模式的區別。

Profiler Options 作用與描述
Sampling 通過獲取 CLR 內部一個方法開始執行和結束執行的時間差來計算的分析時間。這是最快的方法,它用於精確測量程式執行時間,但可能會丟失一些資料。使用此配置型別可使你快速獲取應用程式的的總體效能。
Tracing 慢於 Sampling 的方法,但是可以準確地測量特定方法被呼叫的準確次數。它是通過獲取 CLR 內部一個方法開始執行和結束執行的時間差來計算的分析時間。
Line-by-line 通過收集程式碼執行的每條語句的時間來進行比較,它計算出的時間更加精確。該方法適用於你已經知道效能問題大概在哪裡出現,並要找到具體某一個出現效能問題的時候。
Timeline 採取抽樣的方式,每隔一段時間 (10 ms),會暫停所有執行緒,並抓取堆疊裡的資訊,然後才計算出程式碼執行時間差。使用這個方式可能會導致一些執行時間少於 10 ms 的方法無法被抓取到。

一般來說我們使用的是 Tracing 來進行程式碼的效能分析,因為一般都是需要檢視每個方法具體的呼叫時間。下面我就將以一個介面的例項來作為示範,看如何來排查呼叫緩慢的問題。

2.1 獲取快照資訊

首先執行 dotTrace 之後,選擇 .NET Core Application,之後右側的 Profiler Options 則選擇 Tracing。最後一步則是選擇需要進行檢測的 dll 檔案,這裡我選擇的是一個基於 Abp 框架開發的 ASP.NET Core 專案。

當然,你也可以勾選上 Advanced ,配置諸如啟動引數之類的東西,之後點選 Run 則開始進行分析了。

這裡右下角的 Get Snapshot and Wait 點選之後呢,就會獲取到快照檔案了,當然現在先不慌,我們先來測試一下我們要測試的介面。

比如說我這裡有一個 TestMethod 方法,其程式碼如下:

public class TestApplicationService : ApplicationService
{
    private readonly IRepository<SysSystem> _tempRep;

    public TestApplicationService(IRepository<SysSystem> tempRep)
    {
        _tempRep = tempRep;
    }

    public async Task<string> TestMethod()
    {
        var systems = _tempRep.GetAll().ToList();

        foreach (var system in systems)
        {
            system.Status = 10;
            await _tempRep.UpdateAsync(system);
        }

        int i = 0;
        for (int j = 0; j < 10000; j++)
        {
            i += j;
        }

        return systems[0].SystemCode;
    }
}

現在我們通過 SwaggerUI 呼叫這個介面,看需要多長時間。

可以看到平均時常都需要 300ms ,現在我們點選 GetSnapshot and Wait 按鈕,會彈出分析視窗,並且我們隨時可以通過再次點選 Start 按鈕,繼續分析。

2.2 分析程式碼

2.2.1 概覽資訊

Tracing 分析的介面比較簡單,一個 All Calls 頁籤與 Overview (概覽) 的頁籤,首先我們大致看一下概覽視窗。

可以看到他給我們標識了使用者程式碼執行週期最長的一些地方,其次也用柱狀圖很直觀地體現了耗時最長的程式碼分類。

右側則提列了一些快照的資訊與執行時的環境資訊,以便使用者作為參考。

2.2.2 Threads Tree (執行緒資訊)

本視窗主要的作用是分析應用程式裡面發生的所有的執行緒活動,主執行緒有一個 圖示,而終結器執行緒則是擁有一個 圖示,剩下的都是執行緒池內部的工作執行緒。

在這裡我們以主執行緒為例,分析一下其具體內容所表達的意思。

  • Main:代表不帶名稱空間的方法簡稱。
  • 99 . 99 %:代表該方法針對於整個執行緒執行時間所佔的百分比,這裡的意思就是 Main 方法佔用了整個主執行緒執行時間的 99.99 %。
  • 523,732 ms:代表該方法與子方法執行的總時間。
  • 1 call:方法在堆疊上所被呼叫的次數。
  • XXX.Web.Host.Startup.Program.Main(string[] ):被呼叫方法的全稱,

2.2.3 Call Tree (呼叫樹)

一般我們使用本頁面的時候會多一點,這個頁面會顯示在所有執行緒中的所有被呼叫的方法。其每一個根節點代表的是每一個執行緒所執行的一個根函式,而下面每一個節點則代表其根函式內部呼叫的子函式的相關效能分析資訊。

那麼我們如何快速定位我們剛才測試的介面呢?

按下 Ctrl+F ,會彈出搜尋框,在裡面輸入我們所編寫的介面方法名字,按下回車就會快速定位了。

之後我們會看到如下內容:

通過展開節點我們可以知道最耗費時間的方法,即為 GetAll 方法,當點選節點的時候,右側也會定位到相應的程式碼位置。

這裡可以看到整個 GetAll 方法使用了 1015ms 的時間,這是為什麼呢?你可以看到在其右側有一個 8 calls ,這個時間是 8 次呼叫總共所花費的時間。

右鍵節點,你可以通過 Properties 可以看到該方法的平均執行時間:

可以看到其自身只花費了 8.3 μs,說明真正執行緩慢的還在其更深層,這裡就不再往裡面跟了,如果需要更加詳細的效能報告,可以不使用 Tracing 模式,而使用 Line-by-line 模式來進行分析。

2.2.4 Plain List (簡單列表)

以平鋪的方式展示所有被呼叫過的方法列表,讓你分析具體程式碼。

2.2.5 Hot Spots (熱點跟蹤)

該檢視會列舉出所有耗時最長的方法。

3.參考資料