Quantcast
Channel: 黑暗執行緒
Viewing all articles
Browse latest Browse all 2421

筆記-使用VS2013解開.NET程式CPU衝高謎團

$
0
0

前幾天,幫同事追查 .NET 程式 CPU 衝高問題,才發現 Visual Studio 2013 效能分析工具真是威力強大,特筆記備忘順便分享。原本想拿實務案例說明,但考量太多無關細節會失焦,所以我弄了一個簡單程式當靶機練習射擊:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
 
namespace CPUSharMon
{
publicclass CPUHeater
    {
publicenum JobTypes {
            Light, Medium, High, Crazy
        }
staticvoid RunSHA()
        {
            Guid g = Guid.NewGuid();
            SHA512 sha512 = new SHA512CryptoServiceProvider();
            sha512.ComputeHash(g.ToByteArray());
        }
static Tuple<string, string> GenDESKey()
        {
string rndStr = Guid.NewGuid().ToString();
returnnew Tuple<string, string>(
                rndStr.Substring(0, 8), rndStr.Substring(8, 8));
        }
staticbyte[] Encrypt(DESCryptoServiceProvider des, byte[] data)
        {
            var desencrypt = des.CreateEncryptor();
return desencrypt.TransformFinalBlock(data, 0, data.Length);
        }
staticvoid RunDES()
        {
            var keyIv = GenDESKey();
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            des.Key = Encoding.ASCII.GetBytes(keyIv.Item1);
            des.IV = Encoding.ASCII.GetBytes(keyIv.Item2);
            var data = newbyte[4096];
            var enc = Encrypt(des, data);
        }
staticvoid RunCrazyLoop()
        {
for (var i = 0; i < 10000; i++)
            {
                Guid.NewGuid();
            }
        }
publicstaticvoid RunJob(JobTypes type, int times)
        {
int j = 0;
for (int i = 0; i < times; i++)
            {
switch (type)
                {
case JobTypes.Light:
                        Random rnd = new Random();
                        j = i + rnd.Next(i);
break;
case JobTypes.Medium:
                        RunSHA();
break;
case JobTypes.High:
                        RunDES();                                                
break;
case JobTypes.Crazy:
                        RunCrazyLoop();
break;
                }
            }
        }
publicstaticvoid RunComplexJob()
        {
            RunJob(JobTypes.High, 80000);
            RunJob(JobTypes.Light, 120000);
            RunJob(JobTypes.Crazy, 6000);
            RunJob(JobTypes.Medium, 30000);
        }
    }
}

我寫了一個 Console Application 程式,核心邏輯放在 CPUHeater 類別,RunJob() 方法提供四種粗重程度不一(但毫無營養)的工作:亂數加計算、SHA512 雜湊計算、DES 加密,以及狂跑迴圈生 GUID。(所以程式名稱叫 CPUSharMon ,「瞎忙」無誤)最後,另外宣告 RunComplexJob() 循序執行四種工作。

在八核  i7 執行,耗時約 20 秒,CPU 最高衝到 13%,等於把單核吃到 100%。接著我們就來用 VS2013 找出誰是把 CPU 衝高的兇手。

從選單列 ANALYZE/Performance and Diagnostics 開啟精靈:

選取分析對象:

有四個分析選項:CPU 取樣、Instrumentation(指令執行次數及耗時)、記憶體配置及執行緒等待狀況,這次的案例鎖定 CPU,故選第一個:

選取專案:(亦支援EXE程式及ASP.NET/JavaScript程式)

按下完成,VS2013 便啟動 CPUSharMon 程式並開始蒐集數據:

程式執行完會自動產出報告,但也可在觀察到特定 CPU 暴衝後手動停止。資料處理需要一點時間,接著檢測報告就出爐了:

先說明 Sample Profiling 偵測效能瓶頸原理:觀測程式每隔固定週期中斷 CPU,取回 Callstack 資料,藉此掌握程式當行執行的段並進行統計。由於取樣間隔固定,若每次取樣時常停在特定函式上,便可推斷大部分的 CPU 時間都耗在該函式,一般就是耗用 CPU 較多的運算來源。

我們的展示程式很單純,從彙總報告就能一眼看出效能瓶頸,Hot Path 列出了RunDES() 及 RunCrazyLoop() 出現在總取樣次數的比例最高,分別為 52.73% 及 41.09%,可想成整個執行期間有一半花在 RunDES,近一半花在 RunCrazyLoop。而下方則列出在取樣記錄出現次數最多的個別函式,由 RunCrzayLoop 中的 Guid.NewGuid 及 RunDES 中的加密程序分獲冠亞軍。

報告是互動式,點選函式名稱可以進一步剖析追查兇手,例如點選 HotPath 的第二項 RunComplexJob():

上方可看出 RunComplexJob 所有時間都耗在 RunJob 函式上(Call了四次,參數不同),而下方 Function Code View 會以數字及顏色標示各程序所佔比例,比例愈高愈紅,接著我們點選上方三個Called funtions 最右邊的 RunJob:

報表將再針對 RunJob 展開,上方的藍色區塊圖及下方程式碼都能看出耗用 CPU 較多的函式,再選點上方藍色區塊的 RunDES:

再點選藍色區塊的 Encrypt:

最後抓出 CreateEncryptor()、TransformFinalBlock() 分佔總取樣次數的 13.6% 及 36.4%,到這一步已觸及 .NET Framework 的範圍,不能也不需要再深入。(除非你要改寫 .NET Framework XD)

除了上述的 Function Detail 檢視,報表還提供其他檢視,其中 Call Tree 也蠻好用的。

在 Call Tree 表格中,程式函式的從屬關聯一目瞭然,可透過樹狀結構展開追兇,操作效率更高。而圖中紅字數字標出兩個好用功能:1)火焰圖示用來標示 Hot Path 2)齒輪符號可過濾雜訊,忽略 CPU 佔用不多的項目。另外還有自訂條件過濾等功能,函式太多時很好用!

找到疑犯,按右鍵可以快速切到其他檢視及相關函式,繼續深入調查:

另外,分析工具還提供多次觀測結果比較,像減肥廣告一樣做出「使用前」vs「使用後」的對照表,修改程式調校效能時很有用。

又到呼口號時間:Visual Studio 好威啊!

延伸閱讀:Analyzing Application Performance by Using Profiling Tools


Viewing all articles
Browse latest Browse all 2421

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>