麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

.NET程序性能的基本要領(lǐng)

2019-11-17 02:56:29
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
.NET程序性能的基本要領(lǐng)

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

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

本文提供了一些性能優(yōu)化的建議,這些經(jīng)驗(yàn)來(lái)自于使用托管代碼重寫(xiě)C# 和 VB編譯器,并以編寫(xiě)C# 編譯器中的一些真實(shí)場(chǎng)景作為例子來(lái)展示這些優(yōu)化經(jīng)驗(yàn)。.NET 平臺(tái)開(kāi)發(fā)應(yīng)用程序具有極高的生產(chǎn)力。.NET 平臺(tái)上強(qiáng)大安全的編程語(yǔ)言以及豐富的類庫(kù),使得開(kāi)發(fā)應(yīng)用變得卓有成效。但是能力越大責(zé)任越大。我們應(yīng)該使用.NET框架的強(qiáng)大能力,但同時(shí)如果我們需要處理大量的數(shù)據(jù)比如文件或者數(shù)據(jù)庫(kù)也需要準(zhǔn)備對(duì)我們的代碼進(jìn)行調(diào)優(yōu)。

為什么來(lái)自新的編譯器的性能優(yōu)化經(jīng)驗(yàn)也適用于您的應(yīng)用程序

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

Visual Studio使用了編譯器的API來(lái)實(shí)現(xiàn)了強(qiáng)大的智能感知(Intellisense)功能,如代碼關(guān)鍵字著色,語(yǔ)法填充列表,錯(cuò)誤波浪線提示,參數(shù)提示,代碼問(wèn)題及修改建議等,這些功能深受開(kāi)發(fā)者歡迎。Visual Studio在開(kāi)發(fā)者輸入或者修改代碼的時(shí)候,會(huì)動(dòng)態(tài)的編譯代碼來(lái)獲得對(duì)代碼的分析和提示。

當(dāng)用戶和App進(jìn)行交互的時(shí)候,通常希望軟件具有好的響應(yīng)性。輸入或者執(zhí)行命令的時(shí)候,應(yīng)用程序界面不應(yīng)該被阻塞。幫助或者提示能夠迅速顯示出來(lái)或者當(dāng)用戶繼續(xù)輸入的時(shí)候停止提示。現(xiàn)在的App應(yīng)該避免在執(zhí)行長(zhǎng)時(shí)間計(jì)算的時(shí)候阻塞UI線程從而讓用戶感覺(jué)程序不夠流暢。

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

基本要領(lǐng)

在對(duì).NET 進(jìn)行性能調(diào)優(yōu)以及開(kāi)發(fā)具有良好響應(yīng)性的應(yīng)用程序的時(shí)候,請(qǐng)考慮以下這些基本要領(lǐng):

要領(lǐng)一:不要過(guò)早優(yōu)化

編寫(xiě)代碼比想象中的要復(fù)雜的多,代碼需要維護(hù),調(diào)試及優(yōu)化性能。 一個(gè)有經(jīng)驗(yàn)的程序員,通常會(huì)對(duì)自然而然的提出解決問(wèn)題的方法并編寫(xiě)高效的代碼。 但是有時(shí)候也可能會(huì)陷入過(guò)早優(yōu)化代碼的問(wèn)題中。比如,有時(shí)候使用一個(gè)簡(jiǎn)單的數(shù)組就夠了,非要優(yōu)化成使用哈希表,有時(shí)候簡(jiǎn)單的重新計(jì)算一下可以,非要使用復(fù)雜的可能導(dǎo)致內(nèi)存泄漏的緩存。發(fā)現(xiàn)問(wèn)題時(shí),應(yīng)該首先測(cè)試性能問(wèn)題然后再分析代碼。

要領(lǐng)二:沒(méi)有評(píng)測(cè),便是猜測(cè)

剖析和測(cè)量不會(huì)撒謊。測(cè)評(píng)可以顯示CPU是否滿負(fù)荷運(yùn)轉(zhuǎn)或者是存在磁盤I/O阻塞。測(cè)評(píng)會(huì)告訴你應(yīng)用程序分配了什么樣的以及多大的內(nèi)存,以及是否CPU花費(fèi)了很多時(shí)間在垃圾回收上。

應(yīng)該為關(guān)鍵的用戶體驗(yàn)或者場(chǎng)景設(shè)置性能目標(biāo),并且編寫(xiě)測(cè)試來(lái)測(cè)量性能。通過(guò)使用科學(xué)的方法來(lái)分析性能不達(dá)標(biāo)的原因的步驟如下:使用測(cè)評(píng)報(bào)告來(lái)指導(dǎo),假設(shè)可能出現(xiàn)的情況,并且編寫(xiě)實(shí)驗(yàn)代碼或者修改代碼來(lái)驗(yàn)證我們的假設(shè)或者修正。如果我們?cè)O(shè)置了基本的性能指標(biāo)并且經(jīng)常測(cè)試,就能夠避免一些改變導(dǎo)致性能的回退(regression),這樣就能夠避免我們浪費(fèi)時(shí)間在一些不必要的改動(dòng)中。

要領(lǐng)三:好工具很重要

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

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

要領(lǐng)四:所有的都與內(nèi)存分配相關(guān)

你可能會(huì)想,編寫(xiě)響應(yīng)及時(shí)的基于.NET的應(yīng)用程序關(guān)鍵在于采用好的算法,比如使用快速排序替代冒泡排序,但是實(shí)際情況并不是這樣。編寫(xiě)一個(gè)響應(yīng)良好的app的最大因素在于內(nèi)存分配,特別是當(dāng)app非常大或者處理大量數(shù)據(jù)的時(shí)候。

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

常見(jiàn)的內(nèi)存分配以及例子

這部分的例子雖然背后關(guān)于內(nèi)存分配的地方很少。但是,如果一個(gè)大的應(yīng)用程序執(zhí)行足夠多的這些小的會(huì)導(dǎo)致內(nèi)存分配的表達(dá)式,那么這些表達(dá)式會(huì)導(dǎo)致幾百M(fèi),甚至幾G的內(nèi)存分配。比如,在性能測(cè)試團(tuán)隊(duì)把問(wèn)題定位到輸入場(chǎng)景之前,一分鐘的測(cè)試模擬開(kāi)發(fā)者在編譯器里面編寫(xiě)代碼會(huì)分配幾G的內(nèi)存。

裝箱

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

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

例1 string方法和其值類型參數(shù)

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

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);    }}

這是一個(gè)日志基礎(chǔ)類,因此app會(huì)很頻繁的調(diào)用Log函數(shù)來(lái)記日志,可能該方法會(huì)被調(diào)用millons次。問(wèn)題在于,調(diào)用string.Format方法會(huì)調(diào)用其重載的接受一個(gè)string類型和兩個(gè)Object類型的方法:

String.Format Method (String, Object, Object)

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

你可能會(huì)認(rèn)為這個(gè)基本的調(diào)用string.Format 僅僅是字符串的拼接,所以你可能會(huì)寫(xiě)出這樣的代碼:

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

實(shí)際上,上面這行代碼也會(huì)導(dǎo)致裝箱,因?yàn)樯厦娴恼Z(yǔ)句在編譯的時(shí)候會(huì)調(diào)用:

string.Concat(Object, Object, Object);

這個(gè)方法,.NET Framework 必須對(duì)字符常量進(jìn)行裝箱來(lái)調(diào)用Concat方法。

解決方法:

完全修復(fù)這個(gè)問(wèn)題很簡(jiǎn)單,將上面的單引號(hào)替換為雙引號(hào)即將字符常量換為字符串常量就可以避免裝箱,因?yàn)閟tring類型的已經(jīng)是引用類型了。

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

例2 枚舉類型的裝箱

下面的這個(gè)例子是導(dǎo)致新的C# 和VB編譯器由于頻繁的使用枚舉類型,特別是在Dictionary中做查找操作時(shí)分配了大量?jī)?nèi)存的原因。

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

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

解決方法:

通過(guò)在調(diào)用GetHashCode的時(shí)候?qū)⒚杜e的底層表現(xiàn)形式進(jìn)行強(qiáng)制類型轉(zhuǎn)換就可以避免這一裝箱操作。

((int)color).GetHashCode()

另一個(gè)使用枚舉類型經(jīng)常產(chǎn)生裝箱的操作時(shí)enum.HasFlag。傳給HasFlag的參數(shù)必須進(jìn)行裝箱,在大多數(shù)情況下,反復(fù)調(diào)用HasFlag通過(guò)位運(yùn)算測(cè)試非常簡(jiǎn)單和不需要分配內(nèi)存。

要牢記基本要領(lǐng)第一條,不要過(guò)早優(yōu)化。并且不要過(guò)早的開(kāi)始重寫(xiě)所有代碼。 需要注意到這些裝箱的耗費(fèi),只有在通過(guò)工具找到并且定位到最主要問(wèn)題所在再開(kāi)始修改代碼。

字符串

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

例3 字符串操作

在C#編譯器中有如下方法來(lái)輸出方法前面的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    {        /* ... */    }}

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

WriteFormattedDocComment方法每次被調(diào)用時(shí),第一行代碼調(diào)用Split()就會(huì)分配三個(gè)元素的字符串?dāng)?shù)組。編譯器也需要產(chǎn)生代碼來(lái)分配這個(gè)數(shù)組。因?yàn)榫幾g器并不知道,如果Splite()存儲(chǔ)了這一數(shù)組,那么其他部分的代碼有可能會(huì)改變這個(gè)數(shù)組,這樣就會(huì)影響到后面對(duì)WriteFormattedDocComment方法的調(diào)用。每次調(diào)用Splite()方法也會(huì)為參數(shù)text分配一個(gè)string,然后在分配其他內(nèi)存來(lái)執(zhí)行splite操作。

WriteFormattedDocComment方法中調(diào)用了三次TrimStart()方法,在內(nèi)存環(huán)中調(diào)用了兩次,這些都是重復(fù)的工作和內(nèi)存分配。更糟糕的是,TrimStart()的無(wú)參重載方法的簽名如下:

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

該方法簽名意味著,每次對(duì)TrimStart()的調(diào)用都回分配一個(gè)空的數(shù)組以及返回一個(gè)string類型的結(jié)果。

最后,調(diào)用了一次Substring()方法,這個(gè)方法通常會(huì)導(dǎo)致在內(nèi)存中分配新的字符串。

解決方法:

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

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

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
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 看91| 日本欧美一区二区三区在线观看 | 久草中文网 | 久色成人 | 久久人人爽人人爽人人片av高清 | 久久精品亚洲一区二区三区观看模式 | 欧美成人激情在线 | 欧美性生活久久 | 羞羞草视频 | 成人精品一区二区 | 视频一区 日韩 | 国产精品99精品 | 手机在线看片国产 | 久草视频在线资源 | 久久久精品视频免费 | 日产精品久久久一区二区开放时间 | 精品国产呦系列在线看 | 国产毛片自拍 | 国产 一区 精品 | 毛片视频网址 | 鲁丝片一区二区三区免费入口 | 大学生一级毛片在线视频 | 久久精品之 | 5xsq在线视频| 99最新地址| 91精品国产91久久久久久吃药 | 欧美精品成人一区二区三区四区 | 成人毛片100部 | 欧美18—19sex性护士中国 | 91网在线播放 | 久久久久一区二区三区四区五区 | 久久久久久久久成人 | 欧美黄 片免费观看 | 91精品国产乱码久久久久久久久 | 日日夜av| 粉嫩av一区二区三区四区在线观看 | 特逼视频 | 美女黄网站免费观看 | 91精品国产777在线观看 | 免费一级欧美 | 久草在线免费看 |