亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

.NET程序性能的基本要領

2019-11-17 02:56:29
字體:
來源:轉載
供稿:網友
.NET程序性能的基本要領

前幾天在老趙的博客上看到,Bill Chiles (Roslyn 編譯器的PRogram Manager)寫了一篇文章叫做《Essential Performance Facts and .NET Framework Tips》。這篇文章是一個14頁的pdf,當時我是在地鐵上在Lumia手機上看的,覺得很是不錯,這里也建議大家直接下載閱讀原文,我這里試著翻譯一下,以加深自己印象,后面也有一些思考,以下是原文內容:

---------------------------------------------------------------------------

本文提供了一些性能優化的建議,這些經驗來自于使用托管代碼重寫C# 和 VB編譯器,并以編寫C# 編譯器中的一些真實場景作為例子來展示這些優化經驗。.NET 平臺開發應用程序具有極高的生產力。.NET 平臺上強大安全的編程語言以及豐富的類庫,使得開發應用變得卓有成效。但是能力越大責任越大。我們應該使用.NET框架的強大能力,但同時如果我們需要處理大量的數據比如文件或者數據庫也需要準備對我們的代碼進行調優。

為什么來自新的編譯器的性能優化經驗也適用于您的應用程序

微軟使用托管代碼重寫了C#和Visual Basic的編譯器,并提供了一些列新的API來進行代碼建模和分析、開發編譯工具,使得Visual Studio具有更加豐富的代碼感知的編程體驗。重寫編譯器,并且在新的編譯器上開發Visual Studio的經驗使得我們獲得了非常有用的性能優化經驗,這些經驗也能用于大型的.NET應用,或者一些需要處理大量數據的APP上。你不需要了解編譯器,也能夠從C#編譯器的例子中得出這些見解。

Visual Studio使用了編譯器的API來實現了強大的智能感知(Intellisense)功能,如代碼關鍵字著色,語法填充列表,錯誤波浪線提示,參數提示,代碼問題及修改建議等,這些功能深受開發者歡迎。Visual Studio在開發者輸入或者修改代碼的時候,會動態的編譯代碼來獲得對代碼的分析和提示。

當用戶和App進行交互的時候,通常希望軟件具有好的響應性。輸入或者執行命令的時候,應用程序界面不應該被阻塞。幫助或者提示能夠迅速顯示出來或者當用戶繼續輸入的時候停止提示?,F在的App應該避免在執行長時間計算的時候阻塞UI線程從而讓用戶感覺程序不夠流暢。

想了解更多關于新的編譯器的信息,可以訪問 .NET Compiler Platform ("Roslyn")

基本要領

在對.NET 進行性能調優以及開發具有良好響應性的應用程序的時候,請考慮以下這些基本要領:

要領一:不要過早優化

編寫代碼比想象中的要復雜的多,代碼需要維護,調試及優化性能。 一個有經驗的程序員,通常會對自然而然的提出解決問題的方法并編寫高效的代碼。 但是有時候也可能會陷入過早優化代碼的問題中。比如,有時候使用一個簡單的數組就夠了,非要優化成使用哈希表,有時候簡單的重新計算一下可以,非要使用復雜的可能導致內存泄漏的緩存。發現問題時,應該首先測試性能問題然后再分析代碼。

要領二:沒有評測,便是猜測

剖析和測量不會撒謊。測評可以顯示CPU是否滿負荷運轉或者是存在磁盤I/O阻塞。測評會告訴你應用程序分配了什么樣的以及多大的內存,以及是否CPU花費了很多時間在垃圾回收上。

應該為關鍵的用戶體驗或者場景設置性能目標,并且編寫測試來測量性能。通過使用科學的方法來分析性能不達標的原因的步驟如下:使用測評報告來指導,假設可能出現的情況,并且編寫實驗代碼或者修改代碼來驗證我們的假設或者修正。如果我們設置了基本的性能指標并且經常測試,就能夠避免一些改變導致性能的回退(regression),這樣就能夠避免我們浪費時間在一些不必要的改動中。

要領三:好工具很重要

好的工具能夠讓我們能夠快速的定位到影響性能的最大因素(CPU,內存,磁盤)并且能夠幫助我們定位產生這些瓶頸的代碼。微軟已經發布了很多性能測試工具比如:Visual Studio Profiler, Windows Phone Analysis Tool, 以及 PerfView.

PerfView是一款免費且性能強大的工具,他主要關注影響性能的一些深層次的問題(磁盤 I/O,GC 事件,內存),后面會展示這方面的例子。我們能夠抓取性能相關的 Event Tracing for Windows(ETW)事件并能以應用程序,進程,堆棧,線程的尺度查看這些信息。PerfView能夠展示應用程序分配了多少,以及分配了何種內存以及應用程序中的函數以及調用堆棧對內存分配的貢獻。這些方面的細節,您可以查看隨工具下載發布的關于PerfView的非常詳細的幫助,Demo以及視頻教程(比如Channel9 上的視頻教程)

要領四:所有的都與內存分配相關

你可能會想,編寫響應及時的基于.NET的應用程序關鍵在于采用好的算法,比如使用快速排序替代冒泡排序,但是實際情況并不是這樣。編寫一個響應良好的app的最大因素在于內存分配,特別是當app非常大或者處理大量數據的時候。

在使用新的編譯器API開發響應良好的IDE的實踐中,大部分工作都花在了如何避免開辟內存以及管理緩存策略。PerfView追蹤顯示新的C# 和VB編譯器的性能基本上和CPU的性能瓶頸沒有關系。編譯器在讀入成百上千甚至上萬行代碼,讀入元數據活著產生編譯好的代碼,這些操作其實都是I/O bound 密集型。UI線程的延遲幾乎全部都是由于垃圾回收導致的。.NET框架對垃圾回收的性能已經進行過高度優化,他能夠在應用程序代碼執行的時候并行的執行垃圾回收的大部分操作。但是,單個內存分配操作有可能會觸發一次昂貴的垃圾回收操作,這樣GC會暫時掛起所有線程來進行垃圾回收(比如 Generation 2型的垃圾回收)

常見的內存分配以及例子

這部分的例子雖然背后關于內存分配的地方很少。但是,如果一個大的應用程序執行足夠多的這些小的會導致內存分配的表達式,那么這些表達式會導致幾百M,甚至幾G的內存分配。比如,在性能測試團隊把問題定位到輸入場景之前,一分鐘的測試模擬開發者在編譯器里面編寫代碼會分配幾G的內存。

裝箱

裝箱發生在當通常分配在線程棧上或者數據結構中的值類型,或者臨時的值需要被包裝到對象中的時候(比如分配一個對象來存放數據,活著返回一個指針給一個Object對象)。.NET框架由于方法的簽名或者類型的分配位置,有些時候會自動對值類型進行裝箱。將值類型包裝為引用類型會產生內存分配。.NET框架及語言會盡量避免不必要的裝箱,但是有時候在我們沒有注意到的時候會產生裝箱操作。過多的裝箱操作會在應用程序中分配成M上G的內存,這就意味著垃圾回收的更加頻繁,也會花更長時間。

在PerfView中查看裝箱操作,只需要開啟一個追蹤(trace),然后查看應用程序名字下面的GC Heap Alloc 項(記住,PerfView會報告所有的進程的資源分配情況),如果在分配相中看到了一些諸如System.Int32和System.Char的值類型,那么就發生了裝箱。選擇一個類型,就會顯示調用棧以及發生裝箱的操作的函數。

例1 string方法和其值類型參數

下面的示例代碼演示了潛在的不必要的裝箱以及在大的系統中的頻繁的裝箱操作。

public class Logger{    public static void WriteLine(string s)    {        /*...*/    }}public class BoxingExample{    public void Log(int id, int size)    {        var s = string.Format("{0}:{1}", id, size);        Logger.WriteLine(s);    }}

這是一個日志基礎類,因此app會很頻繁的調用Log函數來記日志,可能該方法會被調用millons次。問題在于,調用string.Format方法會調用其重載的接受一個string類型和兩個Object類型的方法:

String.Format Method (String, Object, Object)

該重載方法要求.NET Framework 把int型裝箱為object類型然后將它傳到方法調用中去。為了解決這一問題,方法就是調用id.ToString()size.ToString()方法,然后傳入到string.Format 方法中去,調用ToString()方法的確會導致一個string的分配,但是在string.Format方法內部不論怎樣都會產生string類型的分配。

你可能會認為這個基本的調用string.Format 僅僅是字符串的拼接,所以你可能會寫出這樣的代碼:

var s = id.ToString() + ':' + size.ToString();

實際上,上面這行代碼也會導致裝箱,因為上面的語句在編譯的時候會調用:

string.Concat(Object, Object, Object);

這個方法,.NET Framework 必須對字符常量進行裝箱來調用Concat方法。

解決方法:

完全修復這個問題很簡單,將上面的單引號替換為雙引號即將字符常量換為字符串常量就可以避免裝箱,因為string類型的已經是引用類型了。

var s = id.ToString() + ":" + size.ToString();

例2 枚舉類型的裝箱

下面的這個例子是導致新的C# 和VB編譯器由于頻繁的使用枚舉類型,特別是在Dictionary中做查找操作時分配了大量內存的原因。

public enum Color { Red, Green, Blue }public class BoxingExample{    private string name;    private Color color;    public override int GetHashCode()    {        return name.GetHashCode() ^ color.GetHashCode();    }}

問題非常隱蔽,PerfView會告訴你enmu.GetHashCode()由于內部實現的原因產生了裝箱操作,該方法會在底層枚舉類型的表現形式上進行裝箱,如果仔細看PerfView,會看到每次調用GetHashCode會產生兩次裝箱操作。編譯器插入一次,.NET Framework插入另外一次。

解決方法:

通過在調用GetHashCode的時候將枚舉的底層表現形式進行強制類型轉換就可以避免這一裝箱操作。

((int)color).GetHashCode()

另一個使用枚舉類型經常產生裝箱的操作時enum.HasFlag。傳給HasFlag的參數必須進行裝箱,在大多數情況下,反復調用HasFlag通過位運算測試非常簡單和不需要分配內存。

要牢記基本要領第一條,不要過早優化。并且不要過早的開始重寫所有代碼。 需要注意到這些裝箱的耗費,只有在通過工具找到并且定位到最主要問題所在再開始修改代碼。

字符串

字符串操作是引起內存分配的最大元兇之一,通常在PerfView中占到前五導致內存分配的原因。應用程序使用字符串來進行序列化,表示JSON和REST。在不支持枚舉類型的情況下,字符串可以用來與其他系統進行交互。當我們定位到是由于string操作導致對性能產生嚴重影響的時候,需要留意string類的Format(),Concat(),Split(),Join(),Substring()等這些方法。使用StringBuilder能夠避免在拼接多個字符串時創建多個新字符串的開銷,但是StringBuilder的創建也需要進行良好的控制以避免可能會產生的性能瓶頸。

例3 字符串操作

在C#編譯器中有如下方法來輸出方法前面的xml格式的注釋。

public void WriteFormattedDocComment(string text){    string[] lines = text.Split(new[] {"/r/n", "/r", "/n"},        StringSplitOptions.None);    int numLines = lines.Length;    bool skipSpace = true;    if (lines[0].TrimStart().StartsWith("http:///"))    {        for (int i = 0; i < numLines; i++)        {            string trimmed = lines[i].TrimStart();            if (trimmed.Length < 4 || !char.IsWhiteSpace(trimmed[3]))            {                skipSpace = false;                break;            }        }        int substringStart = skipSpace ? 4 : 3;        for (int i = 0; i < numLines; i++)            Console.WriteLine(lines[i].TrimStart().Substring(substringStart));    }    else    {        /* ... */    }}

可以看到,在這片代碼中包含有很多字符串操作。代碼中使用類庫方法來將行分割為字符串,來去除空格,來檢查參數text是否是XML文檔格式的注釋,然后從行中取出字符串處理。

WriteFormattedDocComment方法每次被調用時,第一行代碼調用Split()就會分配三個元素的字符串數組。編譯器也需要產生代碼來分配這個數組。因為編譯器并不知道,如果Splite()存儲了這一數組,那么其他部分的代碼有可能會改變這個數組,這樣就會影響到后面對WriteFormattedDocComment方法的調用。每次調用Splite()方法也會為參數text分配一個string,然后在分配其他內存來執行splite操作。

WriteFormattedDocComment方法中調用了三次TrimStart()方法,在內存環中調用了兩次,這些都是重復的工作和內存分配。更糟糕的是,TrimStart()的無參重載方法的簽名如下:

namespace System{     public class String     {         public string TrimStart(params char[] trimChars);    }}

該方法簽名意味著,每次對TrimStart()的調用都回分配一個空的數組以及返回一個string類型的結果。

最后,調用了一次Substring()方法,這個方法通常會導致在內存中分配新的字符串。

解決方法:

和前面的只需要小小的修改即可解決內存分配的問題不同。在這個例子中,我們需要從頭看,查看問題然后采用不同的方法解決。比如,可以意識到WriteFormattedDocComment()方法的參數是一個字符串,它包含了方法中需要的所有信息,因此,代碼只需要做更多的index操作,而不是分配那么多小的string片段。

下面的方法并沒有完全解,但是可以看到如何使用類似的技巧來解決本例中存在的問題。C#編譯器使用如下的方式來消除所有的額外內存分配。

private int IndexOfFirstNonWhiteSpaceChar(string text, int start){    while (start < text.Length && char.IsWhiteSpace(text[start]))         start++;    return start;}private bool TrimmedStringStartsWith(string text, int start, string prefix){    start = IndexOfFirstNonWhiteSpaceChar(text, start);     int len = text.Length - start;     if (len < prefix.Length) return false;    fo
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久综合久久八八| 国产精品久久久久久亚洲影视| 欧美精品video| 欧美日韩国产一区中文午夜| 欧美精品成人91久久久久久久| 国产在线观看精品一区二区三区| 亚洲成人在线视频播放| 国产suv精品一区二区三区88区| 欧美专区福利在线| 久久成人一区二区| 亚洲电影免费在线观看| 亚洲欧洲在线观看| 在线观看国产成人av片| 亚洲精品久久久一区二区三区| 久久久久久欧美| 热久久免费国产视频| 欧洲中文字幕国产精品| 91大神在线播放精品| 久久琪琪电影院| 成人免费淫片aa视频免费| 国产精品看片资源| 亚洲欧美成人在线| 国产亚洲精品成人av久久ww| 亚洲精品美女久久| 色偷偷偷综合中文字幕;dd| 4438全国亚洲精品在线观看视频| 欧美一级在线亚洲天堂| 日韩在线观看电影| 国产极品精品在线观看| 欧美一级大片在线免费观看| 日韩av免费在线观看| 91国产美女视频| 久久久国产91| 中文字幕日韩精品在线观看| 久久综合亚洲社区| 欧美日韩电影在线观看| 精品视频9999| 国产免费一区二区三区在线能观看| 91午夜理伦私人影院| 人人澡人人澡人人看欧美| 国产精品日本精品| 中文日韩在线观看| 欧美日韩一区免费| 亚洲成人在线网| 高清欧美一区二区三区| 欧美极品美女电影一区| 91免费综合在线| 中文字幕久热精品在线视频| 国内精品免费午夜毛片| 欧美日韩一区二区免费在线观看| 亚洲精品456在线播放狼人| www日韩欧美| 丰满岳妇乱一区二区三区| 日韩在线观看高清| 成人中文字幕+乱码+中文字幕| 亚洲精品91美女久久久久久久| 欧美激情精品久久久久久免费印度| 欧美一级免费视频| 日韩精品丝袜在线| 亚洲精品aⅴ中文字幕乱码| 韩国美女主播一区| 51ⅴ精品国产91久久久久久| 午夜精品久久久99热福利| 中文字幕亚洲欧美日韩高清| 97香蕉久久夜色精品国产| 久久中文字幕一区| 日本韩国欧美精品大片卡二| 亚洲精品在线看| 美女性感视频久久久| 日日摸夜夜添一区| 欧美老女人xx| 日韩欧美主播在线| 日韩禁在线播放| 日韩欧美成人网| 国产suv精品一区二区| yw.139尤物在线精品视频| 亚洲日韩中文字幕在线播放| 日韩中文字幕免费| 欧美精品午夜视频| 亚洲欧美制服中文字幕| 久久久久久九九九| 亚洲成av人影院在线观看| 国产精品电影在线观看| 国内揄拍国内精品| 亚洲一区二区三区成人在线视频精品| 国产精品激情av在线播放| 亚洲视频综合网| 亚洲欧美日韩直播| 色偷偷av一区二区三区| 91精品久久久久久久久不口人| 国产精品一区二区三区在线播放| 永久555www成人免费| 97婷婷大伊香蕉精品视频| 热久久视久久精品18亚洲精品| 久久久久久成人精品| 亚洲人成电影在线播放| 国产精品91久久久| 91精品久久久久久久久不口人| 大荫蒂欧美视频另类xxxx| 中文字幕国产日韩| 中文字幕欧美精品日韩中文字幕| 91精品视频免费看| 亚洲黄色片网站| 久久国产色av| 欧美丰满老妇厨房牲生活| www.xxxx精品| 亚洲国产精品国自产拍av秋霞| 国产精品羞羞答答| 在线观看欧美日韩国产| 人九九综合九九宗合| 日韩精品www| 欧美极品少妇与黑人| 国产在线98福利播放视频| 日本高清不卡的在线| 亚洲91精品在线| 黑人欧美xxxx| 日韩精品久久久久久久玫瑰园| 欧美日韩亚洲精品一区二区三区| 欧美成年人在线观看| 57pao国产精品一区| 久久久91精品| 精品香蕉一区二区三区| 欧美成人一区二区三区电影| 久久精品国产视频| 久久精品视频中文字幕| 欧美乱人伦中文字幕在线| 日本久久久久久久| 欧美老少配视频| 日韩精品高清在线观看| 18一19gay欧美视频网站| 亚洲精品国产综合区久久久久久久| 国产91精品久久久久久久| 亚洲片在线观看| 精品成人乱色一区二区| 黄色精品在线看| 宅男66日本亚洲欧美视频| 日韩av一卡二卡| 98精品国产自产在线观看| 国产欧美最新羞羞视频在线观看| 日韩av一区在线| 三级精品视频久久久久| 欧美精品午夜视频| 国产精品久久久久久久久久99| 庆余年2免费日韩剧观看大牛| 欧亚精品中文字幕| 亚洲黄色免费三级| 日韩美女福利视频| 日韩激情视频在线| 久久国产精品久久久久久| 日韩中文字幕国产精品| 91精品久久久久久久久久久久久久| 91av在线免费观看视频| 中文在线不卡视频| 91成人精品网站| 欧美激情一二三| 伊人久久男人天堂| 日韩在线欧美在线| 欧美成人中文字幕在线| 大桥未久av一区二区三区| 亚洲中国色老太| 国产精品情侣自拍| 日本中文字幕久久看| 日本午夜精品理论片a级appf发布| 日韩欧美国产成人|