netpref 使用_使用PerfView监测.NET程序性能(转发)
原文:
前言:
在日常項目開發中,我們時不時會遇到程序【占用了很高CPU的】情況,可能是程序里某些未經優化的代碼或者Bug,或者是程序運行壓力太大。無論是什么原因,我們總希望能看到到底是【哪個方法占用了如此高的CPU】。
微軟為我們提供了很多【性能診斷工具】來達到此目的。例如在Visual Studio 2017中的性能查探器,Windows SDK中的Windows Performance Recorder (WPR) 和Windows Performance Analyzer (WPA),XPerf,當然,還有這篇博客介紹的PerfView。
但在介紹PerfView的使用前,有一個在Windows系統及應用程序性能這個主題上扮演重要角色的技術必須被提及,這就是Event Trace for Windows (ETW)。事實上,上邊提及到的性能分析工具都是基于ETW來實現的。
什么是ETW
在微軟上的解釋中,ETW是自Windows 2000 推出的"內核級"的事件日志實現。通過記錄系統及應用程序中各類事件日志,可以監測和分析系統及程序的運行細節,例如CPU使用率,.NET程序的GC狀況等。ETW的日志里,包含著非常多的有用信息,例如進程/線程信息,上下文切換,各種I/O信息,程序執行時的時間節點,甚至是函數調用等信息。通過對日志數據進行實時采集,或者分析事件日志記錄文件(.etl文件),就能輕易地檢測和分析系統和程序的運行狀況和性能瓶頸,而更為難得的是,ETW的性能還非常的高,據說可以達到 每秒寫入20W條記錄,而僅占用5%的CPU。
Windows除了在系統內核及系統組件自帶了大量事件日志,ETW還為開發者提供編程接口(在 System.Diagnostics.Eventing 命名空間下),允許開發人員在項目中實現自己的事件跟蹤,或者像使用log4net一樣,將系統自定義的日志記錄到ETW里面。這點不在這里展開了,有興趣的可以參考Artech的文章:如何利用ETW(Event Tracing for Windows)記錄日志
但話說回來,我曾經也嘗試過在項目了使用ETW,但感覺在一般項目開發中,還是使用Log4net和nlog這些日志框架較為合適,畢竟一般來說業務系統的日志是給人看的,而ETW的日志數據是二進制形式保存的,更偏向于給日志消費者用的,肉眼在看起來并不那么方便,而且log4net/nlog更合適系統的業務場景的日志使用。ETW還是作為系統性能分析手段比較適合。關于這個可以參考這里
ETW VS 性能監視器
或者有人會問,Windows里已經提供了強大的性能監視器(Perfmon.exe),和資源監視器(順便說下,資源監視器也是基于ETW實現的。參考這里),為什么還需要ETW和其他基于它的工具?例如使用性能監視器,添加各種性能計數器,也可以將系統在運行時的方方面面的性能數據呈現出來,那ETW及PerfView還有什么存在意義?
一個非常明顯的理由就是,ETW的日志存儲著非常詳細的程序運行數據。利用PerfView等工具,你可以看到具體一個進程加載信息,線程的執行信息,函數的調用樹,和執行時間,執行堆棧,CPU執行時間等等信息,而性能監視器只能提供各個性能指標的數據,但并不能具體地展示哪個程序引起了具體的性能問題。通常我們可以配合性能監視器和ETW相關工具的使用,可以全方位的了解系統的性能狀況,并且可以直觀地看到具體是那些函數導致了性能問題,達到知其然同時知其所以然的效果。
第二個理由便是,ETW日志的速度比性能監視器要快,可以在生產環境中自由地獲取運行數據而不影響服務器運行。但性能監視器也是即開即用,所以我覺得這點對我來說意義不太大。
.ETL文件
.etl文件是ETW的日志文件擴。當使用PerfView等工具捕獲系統日志后,便會生成此類文件。使用PerfView等ETW的分析工具,可以對文件內的日志進行各種統計與分析操作。而如果只想單純地看日志內容,可以使用Microsoft Message Analyzer打開文件。
參考資料:
原文:
在上一篇博客中,我們了解了對Windows及應用程序進行性能分析的基礎:Event Trace for Windows (ETW)。現在來看看基于ETW的性能分析工具——Perfview.exe
Perfview簡介
Perfview是一個開源的CPU和內存性能分析工具,也包括一些針對.NET的分析功能,例如GC分析,JIT分析,甚至ASP.NET中的請求統計等等。Perfview是一個Windows應用程序,但也能對在Linux系統上采集的數據進行分析(參考)。Perfview免安裝,而且只是一個14M的.exe文件,非常容易部署到需要進行性能分析的機器上,例如生產環境的服務器。而且在性能數據收集的過程中不需要重啟應用程序或者服務器,而且收集的性能數據日志(.etl文件)可以被拷貝到其他Windows機器上,再進行分析工作,對業務的影響非常少。
Perfview已遷移到GitHub上,可以在上面下載Perfview.exe,clone庫或者查看相關資料。
Perfview使用
在簡單介紹Perfview后,我們來使用Perfview進行一個小小的性能分析,來熟悉一下Perfivew的基本操作。
這個實驗使用的代碼,就是Vance Morrison在視頻教程中用到的Console程序。代碼可以在Perfivew自帶的幫助文件中找到。
using System;
// using System.Collections.Generic;
class Program
{
public static int aStatic = 0;
// Spin is a simple compute bound program that lasts for 5 seconds
// It is a useful test program for CPU profilers.
static int Main(string[] args)
{
int numSec = 5;
if (args.Length == 1)
numSec = int.Parse(args[0]);
Console.WriteLine("Spinning for {0} seconds", numSec);
RecSpin(numSec);
return 0;
}
// Spin for 'timeSec' seconds. We do only 1 second in this
// method, doing the rest in the helper.
static void RecSpin(int timeSec)
{
if (timeSec <= 0)
return;
--timeSec;
SpinForASecond();
RecSpinHelper(timeSec);
}
// RecSpinHelper is a clone of RecSpin. It is repeated
// to simulate mutual recursion (more interesting example)
static void RecSpinHelper(int timeSec)
{
if (timeSec <= 0)
return;
--timeSec;
SpinForASecond();
RecSpin(timeSec);
}
// SpingForASecond repeatedly calls DateTime.Now until for
// 1 second. It also does some work of its own in this
// methods so we get some exclusive time to look at.
static void SpinForASecond()
{
DateTime start = DateTime.Now;
for (; ; )
{
if ((DateTime.Now - start).TotalSeconds > 1)
break;
// Do some work in this routine as well.
for (int i = 0; i < 10; i++)
aStatic += i;
}
}
}
以上代碼很簡單,SpinForASecond()在一秒內不斷調用DateTIme.Now,而RecSpin()和RecSpinHelper()則不斷地相互調用對方。這里使用循環的目的是,循環執行是一種典型的CPU密集型操作,而RecSpin()和RecSpinHelper()則是為了豐富程序的函數調用棧。
步驟一:收集程序運行數據,生成由ETW數據組成的.etl文件。
Perfview提供兩種收集數據的方式,Run和Collect。“Run”是直接指定需要啟動的應用程序的名稱,以便啟動該程序。“Collect”則是直接啟動Perfview并開始收集。但不要以為"Run"方式只收集指定程序的數據。事實上無論哪種方式,Perfview都會收集系統范圍內全部數據,并且收集完成后,需要選擇某一個進程以進行分析。
我們以“Run”方式來收集以上代碼生成的Tutorial.exe程序。
在彈出的對話框中,填入需要啟動Tutorial.exe的全文件名,以及填入生成etl文件的文件名(這里是PerfViewData.etl),并點擊“Run Command”:
Perfview收集和處理數據的時間比較長。在處理過程中,Perfview的右下角會閃動,并且可以查看運行日志,了解到當前Perfview在執行什么工作。
步驟二:選擇需要分析的進程
在收集完畢后,在左邊選擇“PerfViewData.etl.zip”,并在展開的選擇項中雙擊選擇“CPU Stacks”,此時,會彈出進程選擇對話框,選擇需要進行CPU分析的進程。這里選擇我們運行的Tutorial.exe進程。
步驟三:查看執行棧視圖
在雙擊選擇了“Tutorial.exe”的進程后,進入到程序詳細的執行棧的視圖中。這里記錄著Tutorial.exe的函數調用樹,以及函數的執行時間。
在該視圖中,你可以看到Tutorial.exe的函數調用情況,包括函數調用樹(Call-Tree),某個函數的調用者(Calls)和被該函數調用的函數(Callees),另外,在視圖右側,是函數的執行時間,其中,“Exc”是指 Exclusive,是指函數自己(不包含該函數里執行的子函數)的執行時間,而“Inc”是指Inclusive,指該函數及該函數中執行的子函數的總的執行時間。
另外,這個執行時間是怎么認定的呢? 答案是CPU采樣。Perfview對CPU進行采樣,默認每個CPU采樣是1毫秒(在Prefview的高級設置中可以設置到0.125毫秒~1毫秒),每次采樣中可以得到當前CPU正則執行什么代碼。例如DateTime_getNow()有3250采樣,則可以說明在整個程序運行中,DateTime_getNow()占用了3250毫秒的CPU時間,占整個運行時間的66.2%。通過比較各個函數的執行時間,我們就可以知道程序中哪個函數占用比較多的CPU時間。
以上便是Prefview的基本的使用步驟。Prefview提供了非常多并強大的功能,例如分組(Grouping),折疊(Folding),時間范圍選擇,這些在后續教程里再聊。而更強大的是,F1幫助手冊里,有著非常詳細的使用說明和術語解析,而且界面上幾乎每個功能都有說明的ToolTip和說明的超鏈接如果對某個功能用法不是很清楚,可以方便地找到說明,真是業界良心。
參考資料
在上一篇博客中,我們通過Perfview幫助文件中自帶的代碼來簡單使用了Perfview,了解了基本操作。現在來看看Perfview中的分組操作(Grouping)。分組功能都旨將記錄到的各種函數調用堆棧以指定的規則進行分組,幫助你組織和找到更關心的數據。
在實際使用中,PerfView通常會收集到非常多的函數調用棧數據,包括我們關心的程序的函數調用信息,及一大堆我們不關心的其他函數調用信息,例如windows系統的底層函數。這么多有用沒用的條目都列出在列表視圖上,令人眼花繚亂。如何將我們不需要的數據分組歸納呢?Perfview提供分組功能。
分組功能使用類似于正則的匹配功能,將函數全名(一個函數的全名包含了程序集,命名空間,類名和函數名,例如"mscorlib.n!System.DateTime.get_Now()")進行匹配,并替換成自定義的分組名稱。例如,可以對所有在Debug目錄下的程序集的函數單獨顯示,而其他函數則分組成“OTHER”,這樣,我們就可以只看見我們程序里的函數調用。其實這就是默認的[Just My App]分組規則的作用:
通配符
那么,分組功能如何使用呢?
在使用分組之前,先看看PerfView定義的幾個"通配符":
* :匹配任意數量的字符
%:匹配任意數量的數字和英文字母和點號("."),等于.NET正則中的 [\w\d.]*
^ :匹配開頭
|:“或”操作
{}:代表一個分組,等于.NET正則里的小括號
分組規則
PerfView中有兩種分組操作,分別是 PAT->GROUP 和 PAT=>GROUP。在這里“PAT”代表需要匹配的模式(Pattern),“GROUP”代表你自定義的組名。 而這兩種分組方式區別就在于中間的“->”和"=>",前者表示忽略入口函數,后者則會將入口函數顯示在分組中。有時我們希望知道一個分組里的函數最初是由哪個函數開始調用的,這時候就可以使用后者了。除此之外,兩種分組方式沒有其他不同。
我們看看具體的分組規則的使用:
1. PAT->GROUP形式
如上述,PAT->GROUP形式是簡單地將一個函數的全名稱中包含"PAT"字眼的條目都分到“GROUP”組中,例如,在不分組的情況下,我們收集到的函數調用數據列表是這樣的:
里面有我們Tutorial.exe的函數,例如,tutorial!Program.SpinForASecond()和tutorial!Program.RecSpin(),同時也有很多.NET的內部函數,例如 mscorlib.n!System.get_Now()和mscorlib.n!System.TimeSpan.get_TotalSeconds(),等等。假設我們只關心tutorial.exe自身的函數,而不希望被.NET內部函數所干擾,我們則可以設置一個分組規則“mscorlib->.NET”,這樣,所有包含“mscorlib”字眼的方法全名稱的條目都會被分組進“.NET”組,效果如下:
是不是清爽了很多?這樣的分組能使我們快速地過濾掉mscorlib有關的函數,只剩下tutorial自己的函數(和一些其他函數,當然如果你愿意,也可以將其他的函數"分組"掉)
2. {*}=>$1
該形式的規則意思是: 花括號里匹配到的條目會被分組,而組名正是是花括號里的匹配到的內容,“$1”是一個占位符,對應的是花括號“{}”里的內容。假設有兩個函數:tutorial!Program.SpinForASecond()和tutorial!Program.RecSpin(), 而應用的規則是“{tutorial!}->My APP $1”,則分組后,這兩個函數被分進一組,并且組名為“My App tutorial!”
Perfview還支持同時設置多個規則,例如設置規則為“{tutorial!}->My APP $1;{mscorlib.ni!}->Internal $1”,這里有兩個規則,一個是藍色部分,另一個是紅色部分,中間用分號(;)隔開。如果函數全名中有"tutorial!"的就分進名為“My APP tutorial!”組,而有“mscorlib.ni!”字眼的就分進“Internal?mscorlib.ni!”組。
3.PAT=>GROUP
最后來看看入口點規則分組(Entry Point Grouping)。前邊提到過,“PAT=>GROUP”與“PAT->GROUP”的不同在于,后者會忽略掉該組的入口函數,意味著你很難知道某個分組里的函數是從哪個函數執行進去的,而前者則會包含入口點函數信息。我們通過圖例來看看實際效果。
下圖中,使用“mscorlib->System Functions”規則來對mscorlib的函數進行分組,組名為“System Functions”,但除非你展開這個分組的明細,查找每個函數調用樹,否則你不知道是什么函數調用了這組函數。
而現在使用“mscorlib=>System Functions”,看看有什么不同:
System Functions組明確指示了該組的函數的入口點是“mscorlib.ni!System.DateTime.get_Now()”函數,即DateTime.Now導致了這些函數的執行。
以上便是PerfView的分組功能。但其實這只是分組功能中的一部分。通過規則的搭配可以有更強大的效果。而最全面的說明其實是在PerfView自帶的F1幫助文件。這里只作一個拋磚引玉的簡要說明。因此如果需要了解更全面的分組技巧,可以去幫助文件里搜索相關主題。
在上一篇文章中,我們使用了Perfview的分組功能。分組功能旨在對某些函數按照某個格式進行分組,以減少視圖中的各種無關函數的數量。但僅有分組還不夠,有時我們想將一些函數調用信息按某些條件過濾掉,例如將采用小于1%的函數調用信息去掉,或者將函數名中包含某個字眼的函數信息去掉,甚至只顯示某個時間段調用的函數。這就需要用到PerfView提供的過濾(Filtering)功能了。PerfVIew提供了3種過濾功能:折疊(Folding),匹配過濾,和時間范圍選擇(Set Time Range)
折疊(Folding)
PerfView在界面上提供了兩種Folding功能:“Fold%” 和 “FoldPats”.
“Fold%”意思是,將采樣少于該百分比的函數調用信息條目“折疊”掉,并將其采樣時間累加到這些函數的調用者。這里“折疊”的意思其實應該說是隱藏掉。
“FoldPats”:與“Fold%”類似,會將符合條件的函數調用記錄“折疊”(或“隱藏”)掉,而它的采樣時間將會算在它的直接調用者頭上。而與“Fold%”不同的是它的條件不是“Inc%”的值,而是函數名稱中符合給定的匹配模式。同時,這個功能也支持指定多個模式,不同模式之間用";"(分號)隔開。
過濾(Filtering)
折疊功能能實現過濾的效果,只是被過濾的項的采樣時間會被累加到調用者身上。有時我們其實只想簡單地去掉一些我們不關心的函數,同時也不需要保留它們的采樣時間。這時,我們可以采用“簡單粗暴”的過濾方式。
"IncPats":可以設置一個或多個模式(多個時用分號";"隔開),只有當函數名稱匹配一個或者多個給定的模式時,這個函數會被顯示在列表上,不匹配的會被從列表上刪除。
"ExcPats":可以設置一個或多個模式(多個時用分號";"隔開),只有當函數名稱匹配一個或者多個給定的模式時,這個函數會從列表上刪除。
因此,"IncPats"和"ExcPats"是一對反義詞,前者是“匹配則保留”,后者是“匹配則刪除”
這里說的模式,和上一篇文章中說的“通配符”是一樣的,或者可以參考幫助文件,或者在界面上對于的ToolTip提示中也有說明。
例如,以下圖例的設置中,在”IncPats“框中設置”Process% ConsoleApp7 (4108)“,起到只顯示我的程序”ConsoleApp7“的函數調用堆棧
時間范圍選擇
有時我們希望能只顯示某一段時間內的函數調用情況,Perfview也提供時間范圍選擇的功能,在界面上提供了"Start"和"End"兩個輸入框。但要注意的是,這里的時間范圍不是以絕對的時間值(比如:2018/12/27 23:15:02),而是從開始收集時經歷的毫秒數。假設你某個時間開始收集,5秒后停止,你想只看第3秒到第4秒的數據,就可以在Start中設置"3000",在End中設置"4000",Perfview只顯示大于或等于3000毫秒小于或等于4000毫秒的函數調用數據。
不僅只提供文本框輸入值的功能,Perfview還提供一個更便利的時間范圍選擇功能,如下圖:
在”When“一列中,顯示了一串文本值,一共32個,由數字字母和一些符號組成。這串由符號組成的”字符串“代表了該函數在收集過程中被采樣數(可以理解成出現的頻繁度),具體來說,Pferview將每一個函數的”Inclusive“時間分成32"段",每一段的不同符號代表了該函數的”Inclusive“時間的比例值:(以下內容是從幫助文檔中COPY的,稍作翻譯)
”_“ 代表在該段"時間段"內沒有采樣(means no samples occurred in that bucket. )
”.“代表在該段"時間段"內有0%到0.1%的Inclusive時間?(means that interval consumed between 0% and .1%. )
”o” 代表在該段"時間段"內有1%?到 1%的Inclusive時間(means that interval consumed between .1% and 1%. )
”0” 代表在該段"時間段"內有1% 到10%的Inclusive時間(means that interval consumed between 1% and 10%. )
”1” 代表在該段"時間段"內有10%?到 20%的Inclusive時間(means that interval consumed between 10% and 20% )
...
”9 代表在該段"時間段"內有90% 到100%的Inclusive時間(means that interval consumed between 90% and 100% )
”A” 代表在該段"時間段"內有100% 到110%的Inclusive時間(means that interval consumed between 100% and 110% )
...
”Z” 代表在該段"時間段"內有350% 到360%的Inclusive時間(means that interval consumed between 350% and 360% )
”*” 代表在該段"時間段"內有超過360%的Inclusive時間(means that interval consumed over 360%)
”a” 代表在該段"時間段"內有0% 到-10%的Inclusive時間(means that interval consumed between 0% and -10% )
”b” 代表在該段"時間段"內有-10% 到-20%的Inclusive時間(means that interval consumed between -10% and -20% )
...
”z” 代表在該段"時間段"內有-250% 到-260%的Inclusive時間(means that interval consumed between -250% and -260% )
”*” 代表在該段"時間段"內有超過 -260 %的Inclusive時間(means that interval consumed over -260 %)? 這個我不是很清楚,不是代表360%嗎??
另外,為什么會有些值是超過100%呢? 按文檔的解釋,應該是有多個可消耗的資源單元,例如200%代表某個函數在2個CPU上都有100%的INCLUSIVE時間,但我不確定是否理解正確。
那怎么在“When”欄中進行時間范圍選擇呢?
可以雙擊“When”欄的文本值,然后用鼠標光標選擇一段或整段文本(就像平時選擇一段文字進行ctrl+c ctrl+v一樣),然后右擊并在右鍵菜單中選擇“Set Time Range”,如圖:
這樣,該時間范圍的值就會被設置到"Start"和“End”中,并進行過濾。
以上便是Perfiveiw中的幾種數據過濾功能,還是那句話,這里涉及只介紹了基本的功能,更多更具體的功能說明,可以查看幫助文檔。
總結
以上是生活随笔為你收集整理的netpref 使用_使用PerfView监测.NET程序性能(转发)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python用outlook自动发邮件_
- 下一篇: win10子系统ubuntu图形界面_w