一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

老趙談IL(3):IL可以看到的東西,其實(shí)大都也可以用C#來發(fā)現(xiàn)

  在上一篇文章中,我們通過一些示例談?wù)摿薎L與CLR中的一些特性。IL與C#等高級語言的作用類似,主要用于表示程序的邏輯。由于它同樣了解太多CLR中的高級特性,因此它在大部分情況下依舊無法展現(xiàn)出比那些高級語言更多的CLR細(xì)節(jié)。因此,如果您想要通過學(xué)習(xí)IL來了解CLR,那么這個(gè)過程很可能會“事倍功半”。因此,從這個(gè)角度來說,老趙并不傾向于學(xué)習(xí)IL。不過嚴(yán)格說來,即使IL無法看出CLR的細(xì)節(jié),也不足以說明“IL無用”——這里說“無用”自然有些夸張。但是,如果我們還發(fā)現(xiàn),那些原本被認(rèn)為需要通過IL挖掘到的東西,現(xiàn)在都可以使用更好的方法來獲得,并且可以起到“事半功倍”的效果,那么似乎我們真的沒有太多理由去追逐IL了。

  在這篇文章中,我們使用最多的工具便是.NET Reflector,從.NET 1.x開始,.NET Reflector就是一個(gè)探究.NET框架(主要是BCL)內(nèi)部實(shí)現(xiàn)的有力工具,它可以把一個(gè)程序集高度還原成C#等高級語言的代碼。在它的幫助下,幾乎所有程序集實(shí)現(xiàn)都變得一目了然,這大大方便了我們的工作。老趙對此深有感觸,因?yàn)樵谀扯尾凰愣痰臅r(shí)間內(nèi),我使用.NET Reflector閱讀過的代碼數(shù)量遠(yuǎn)遠(yuǎn)超過了自己編寫的代碼。與此相反的是,老趙幾乎沒有使用IL探索過.NET框架下的任何問題。這可能還涉及到方式方法和個(gè)人做事方式,但是如果這真有效果的話,為什么要舍近求遠(yuǎn)呢?希望您看過了這篇文章,也可以像我一樣擺脫IL,投入.NET Reflector的懷抱。

示例一:探究語言細(xì)節(jié)

  C#語言從1.0到3.0版本的進(jìn)化過程中,大部分新特性都是依靠編譯器的魔法。就拿C#3.0的各種新特性來說,Lambda表達(dá)式,LINQ,自動屬性等等,完全都是基于CLR 2.0中已有的功能,再配合新的C#編譯器而產(chǎn)生的各種神奇效果。有些朋友認(rèn)為,掌握IL之后便把握了.NET的根本,以不變應(yīng)萬變,只要讀懂IL,那么這些新特性都不會對您形成困擾。這話說的并沒有錯(cuò),只是老趙認(rèn)為,“掌握IL”在這里只是一個(gè)“充分條件”而不是一個(gè)“必要條件”,我們完全可以使用.NET Reflector將程序集反編譯成C#代碼來觀察這些。

  這里我們使用.NET Reflector來觀察最最常見,最最普通的foreach關(guān)鍵字的功能。我們都知道foreach是遍歷一個(gè)IEnumerble對象內(nèi)元素的方式,我們也都知道foreach其實(shí)是GoF Iterator模式的實(shí)現(xiàn),通過MoveNext方法和Current屬性進(jìn)行配合共同完成。不過大部分朋友似乎都是從IL進(jìn)行觀察,或是“聽別人說”而了解這些的。事實(shí)上,.NET Reflector也可以很容易地證實(shí)這一點(diǎn),只是這中間還有些“特別”的地方。那么首先,我們還是來準(zhǔn)備一個(gè)最簡單的foreach語句:

static void DoEnumerable(IEnumerable<int> source){    foreach (int i in source)    {        Console.WriteLine(i);    }}

  如果觀察它的IL代碼,即使不了解IL的朋友也一定可以看出,其中涉及到了GetEnumerator,MoveNext和Current等成員的訪問:

.method private hidebysig static void DoEnumerable(    class [mscorlib]System.Collections.Generic.IEnumerable`1 source) cil managed{    .maxstack 1    .locals init (        [0] int32 i,        [1] class [mscorlib]System.Collections.Generic.IEnumerator`1 CS$5$0000)    L_0000: ldarg.0     L_0001: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1        [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator()    L_0006: stloc.1     L_0007: br.s L_0016    L_0009: ldloc.1     L_000a: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current()    L_000f: stloc.0     L_0010: ldloc.0     L_0011: call void [mscorlib]System.Console::WriteLine(int32)    L_0016: ldloc.1     L_0017: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()    L_001c: brtrue.s L_0009    L_001e: leave.s L_002a    L_0020: ldloc.1     L_0021: brfalse.s L_0029    L_0023: ldloc.1     L_0024: callvirt instance void [mscorlib]System.IDisposable::Dispose()    L_0029: endfinally     L_002a: ret     .try L_0007 to L_0020 finally handler L_0020 to L_002a}

  但是,如果使用.NET Reflector觀察它的C#代碼又會如何呢?

private static void DoEnumerable(IEnumerable source){    foreach (int i in source)    {        Console.WriteLine(i);    }}

  請注意,以上這段是由.NET Reflector從IL反編譯后得到的C#代碼,這簡直……不是簡直,是完完全全真真正正地和我們剛才寫的代碼一模一樣!這就是.NET Reflector的強(qiáng)大之處,由于它意識到IL調(diào)用了IEnumerable.GetEnumerator方法,因此它就“留心”判斷這個(gè)調(diào)用的“模式”是否符合一個(gè)標(biāo)準(zhǔn)的foreach,如果是,那么就會顯示為一個(gè)foreach語句而不是while...MoveNext。不過,這難道不就掩蓋了“事物本質(zhì)”了嗎?要知道我們的目標(biāo)可是探究foreach的形式,既然.NET Reflector幫不了的話,我們不還是需要去觀察IL嗎?

  剛才老趙提到,.NET Reflector在判斷IL代碼時(shí)發(fā)現(xiàn)一些標(biāo)準(zhǔn)的模式時(shí)會進(jìn)行代碼“優(yōu)化”。那么我們能否讓.NET Reflector不要做這種“優(yōu)化”呢?答案是肯定的,只是需要您在.NET Reflector中進(jìn)行一些簡單的設(shè)置:

  打開View菜單中的Options對話框,在左側(cè)Disassembler選項(xiàng)卡中修改Optimization級別,默認(rèn)很可能是.NET 3.5,而現(xiàn)在我們要將其修改為None。這么做會讓.NET Reflector最大程度地“直接”翻譯IL代碼,而不做一些額外優(yōu)化。將Optimization級別設(shè)為None以后,DoEnumerable方法的代碼就變?yōu)榱耍?/p>

static void DoEnumerable(IEnumerable<int> source){    int num;    IEnumerator<int> enumerator;    enumerator = source.GetEnumerator();Label_0007:    try    {        goto Label_0016;    Label_0009:        num = enumerator.Current;        Console.WriteLine(num);    Label_0016:        if (enumerator.MoveNext() != null)        {            goto Label_0009;        }        goto Label_002A;    }    finally    {    Label_0020:        if (enumerator == null)        {            goto Label_0029;        }        enumerator.Dispose();    Label_0029: ;    }Label_002A:    return;}

  這是C#代碼嗎?為什么會有那么多的goto?為什么MoveNext方法返回的布爾值可以和null進(jìn)行比較?其實(shí)您把這段代碼復(fù)制粘貼后會發(fā)現(xiàn),它能夠正常編譯通過,效果也和剛才的foreach語句完全一樣。這就是去除“優(yōu)化”的效果。老趙在上一篇文章中談到說:IL和C#一樣,都是用于表現(xiàn)程序邏輯。C#使用if...else、while、for等等豐富語法,而在IL中就會變成判斷+跳轉(zhuǎn)語句。上面的C#代碼便直接保留了IL的這個(gè)“特性”。不過還好,我們還是可以看出try...finally,可以看出MoveNext方法和Current屬性的訪問,可以看到程序使用Console.WriteLine輸出數(shù)據(jù)。至此,我們便發(fā)現(xiàn)了foreach語句的真面目。從現(xiàn)在開始,在您準(zhǔn)備深入IL之前,老趙也建議您可以嘗試一下使用None Optimization來觀察C#代碼。

  實(shí)事求是地說,上面的C#代碼的“轉(zhuǎn)向邏輯”并不那么清晰,因此您在理解的時(shí)候可以把它復(fù)制到編輯器中,進(jìn)行一些簡單調(diào)整。但是從老趙的經(jīng)驗(yàn)上來看,需要使用None Optimization進(jìn)行探索的地方非常少見。foreach是一個(gè),還有便是C#中的其他一些“別名”,如使用using關(guān)鍵字管理IDisposable對象,以及l(fā)ock關(guān)鍵字。而且,其實(shí)這段邏輯也只是沒有優(yōu)化IL中的跳轉(zhuǎn)語句而已,已經(jīng)比IL本身要直觀許多了。此外,關(guān)于對象創(chuàng)建,變量聲明,方法調(diào)用,屬性訪問,事件加載……一切的一切都還是最常用的C#代碼。因?yàn)檫€是那個(gè)原因:從大部分情況上來看,IL也只是表現(xiàn)了程序邏輯,并沒有比C#等語言體現(xiàn)出更多的細(xì)節(jié)。

  我在這里舉了一個(gè)較為極端的例子,因?yàn)槲野l(fā)現(xiàn)不少朋友并沒有嘗試過使用None Optimization來觀察過代碼。這里也可以看出,.NET Reflector的“優(yōu)化級別”還不夠“細(xì)致”。不過這應(yīng)該是一個(gè)“產(chǎn)品設(shè)計(jì)”的正常結(jié)果,因?yàn)閒oreach/using/lock的關(guān)鍵字都是從.NET 1.0誕生伊始就存在的,也就是說,即使.NET Reflector選擇將IL編譯為C# 1.0,它的表現(xiàn)形式依舊是“標(biāo)準(zhǔn)模式”,這方面可能就不能過于強(qiáng)求了吧。至于其他一些探索,例如C#中的自動屬性,Lambda表達(dá)式構(gòu)建表達(dá)式樹或匿名委托,乃至C# 4.0中的dynamic關(guān)鍵字,都是使用.NET 3.5 Optimization進(jìn)行探索便可得知的結(jié)果。您可以回憶一下自己看過的文章,其中有多少是使用IL解釋問題的呢?

示例二:學(xué)習(xí).NET平臺上的其他語言

  在.NET平臺上,任何語言都會先編譯為IL,然后再運(yùn)行時(shí)由JIT轉(zhuǎn)化為機(jī)器碼。因此有種說法是,只要把握了IL,.NET平臺上各種語言之間的遷移都會變得容易。對此老趙有不同看法。在以前討論語言是否重要的時(shí)候,老趙提到,語言它并不僅僅是一種文字表現(xiàn)形式,而是一種“思維方式”的改變,這可能會影響到您程序的編碼風(fēng)格,API設(shè)計(jì)乃至架構(gòu)(這個(gè)鏈接可能打不開,因?yàn)?hellip;…)。實(shí)際上,如果您只是在C#與VB.NET之間進(jìn)行遷移,原本就是一件相當(dāng)容易的事情,因?yàn)樗鼈冎g“語言”的各種概念和特性都非常接近。而一種改變您思維的語言,才是真正有價(jià)值,而且值得進(jìn)行比較和探索的。如果一味地追求“把握本源”,那么甚至還有比IL更低抽象的事務(wù),但這些就已經(jīng)違背了“創(chuàng)造一門語言”,以及您學(xué)習(xí)它的目的了,不是嗎?

  當(dāng)然,探索也是需要的,尤其是.NET平臺上的各種語言,他們被統(tǒng)一在同樣的平臺上,這本身就是一種很好的資源。這種資源就是所謂的“比較學(xué)習(xí)”。您可以把新的語言和您熟悉的語言進(jìn)行對比,吸收其中的長處(如優(yōu)秀的思維方式),這樣便可以更好地使用舊有語言。例如,您把F#類庫轉(zhuǎn)化為C#代碼進(jìn)行觀察之后,發(fā)現(xiàn)其中大量函數(shù)式編程風(fēng)格的API是使用“委托”來實(shí)現(xiàn)的,您可能就會想到是否可以設(shè)計(jì)出函數(shù)式編程風(fēng)格的C# API,是否可以把F#中List或Seq模塊中的各種高階函數(shù)移植到您自己的項(xiàng)目中來。這就有了更好的價(jià)值,這價(jià)值也不僅僅只是您“學(xué)會了新的語言”。

  例如,我們現(xiàn)在使用尾遞歸來計(jì)算斐波那契數(shù)列。在之前的文章中,我們的作法是:

private static int FibTail(int n, int acc1, int acc2){    if (n == 0) return acc1;    return FibTail(n - 1, acc2, acc1 + acc2);}public static int Fib(int n){    return FibTail(n, 0, 1);}

  為了“尾遞歸”,我們必須定義一個(gè)私有的FibTail方法,接收三個(gè)參數(shù)。而對外的接口還是一個(gè)公有的Fib方法,它返回斐波那契數(shù)列第n項(xiàng)的結(jié)果。這個(gè)示例很簡單,作法也沒有任何問題。但是老趙有時(shí)候會覺得,我們?yōu)槭裁捶且x一個(gè)額外的“輔助方法”,然后在現(xiàn)有的方法里只是進(jìn)行一個(gè)簡單的轉(zhuǎn)發(fā)?如果這個(gè)輔助方法會在其他地方得到調(diào)用也就罷了(我們遵守了DRY原則),但是現(xiàn)在卻有點(diǎn)“平白無故”地在代碼里增加了一個(gè)方法,這樣在VS的Class View或編輯器上方的下拉列表中也會多出一項(xiàng)。此外,為了表示兩個(gè)方法的關(guān)系,您可能還會使用region把它們包裹起來……

  不過在F#中,上面的尾遞歸就可以這樣寫:

let fib n =    let rec fibTail x acc1 acc2 =        match x with        | 0 -> acc1;        | _ -> fibTail (x - 1) acc2 (acc1 + acc2)    fibTail n 0 1

  在fib方法內(nèi)部,我們可以重新定義一個(gè)fibTail方法,其中實(shí)現(xiàn)了尾遞歸。對于外部來說,只有fib方法是公開的,外界絲毫不知道fibTail方法的存在,這種定義內(nèi)部函數(shù)的作法在F#中非常常見。而編譯后,我們在.NET Reflector中便可看到與之對應(yīng)的C#實(shí)現(xiàn):

public static int fib(int n){    switch (n)    {        case 0:            return 0;    }    return fibTail@7@7(n - 1, 1, 1);}internal static int fibTail@7@7(int x, int acc1, int acc2){    ...}

  在F#中沒有internal的訪問級別,您可以認(rèn)為這里internal便是private。于是我們得知(可能您本身也猜得到):由于.NET本身并沒有“嵌套方法”特性,因此在這里編譯器會重新生成一個(gè)特殊的私有方法,并且在fib方法里進(jìn)行調(diào)用。于是我們想到,這個(gè)“自動生成方法”的特性,在C#中也有體現(xiàn)啊。例如,IEnmuerable有一個(gè)擴(kuò)展方法是Where,我們可以用Lambda表達(dá)式構(gòu)造一個(gè)匿名委托作為參數(shù)……唔唔,這不就相當(dāng)于把一個(gè)方法定義在另一個(gè)方法內(nèi)部了嗎?于是,我們修改一下之前C#的尾遞歸的實(shí)現(xiàn):

public static int Fib(int n){    Func<int, int, int, int> fibTail = null;    fibTail = (x, acc1, acc2) =>    {        if (x == 0) return acc1;        return fibTail(x - 1, acc2, acc1 + acc2);    };    return fibTail(n, 0, 1);}

  如果沒有F#的“提示”,可能我們只能想到list.Where(i => i % 2 == 0)這種形式的用法,我們平時(shí)不會在方法內(nèi)部額外地“創(chuàng)建一個(gè)委托”,然后加以調(diào)用,而且還用到了“遞歸”——甚至還是“尾遞歸”(雖然C#編譯器在這里沒有進(jìn)行優(yōu)化,而且這里其實(shí)也只是個(gè)“偽遞歸”,因?yàn)閒ibTail其實(shí)是個(gè)可改變的“函數(shù)指針”)。不過,由于我們剛才通過C#來觀察F#的編譯結(jié)果,聯(lián)想到它和我們以前觀察到的C#中“某個(gè)特性”非常相似,再加上合理的嘗試,最終同樣得出了一個(gè)還算“令人滿意”的使用方式。

  這只是一個(gè)示例,我并不是說這種作法是所謂的“最佳實(shí)踐”。任何辦法一旦遭到濫用也肯定不會有好處,您要根據(jù)當(dāng)前情況判斷是否應(yīng)該采取某種作法。剛才的演示只是為了說明,我們應(yīng)該如何從其他語言中吸取優(yōu)勢思想,改進(jìn)我們的編程工作。當(dāng)然,您使用IL來探索新的語言也沒有太大問題,C#能看到的東西用IL也可以看到。但是請您回想一下,即使您平時(shí)學(xué)習(xí)IL,您想過直接使用IL來寫程序嗎?您學(xué)習(xí)和探索新語言的目的,只是為了搞清楚它的IL表現(xiàn)形式嗎?為什么您不使用簡單易懂的C#,卻要糾纏于IL中那些紛繁復(fù)雜的指令呢?

示例三:性能相關(guān)

  學(xué)習(xí)IL對寫出高性能的.NET程序有幫助嗎?

  記得以前在學(xué)習(xí)“計(jì)算機(jī)系統(tǒng)概論”課程時(shí),有一個(gè)實(shí)驗(yàn)就是為幾段C程序進(jìn)行優(yōu)化。當(dāng)時(shí)的手段可謂無所不用其極,例如內(nèi)聯(lián)一個(gè)子過程以避免call指令的消耗,或把一段C代碼使用匯編進(jìn)行替換等等。從結(jié)果上看,它們都能對性能有“明顯”的提高。不過,那些都是為了加深概念而進(jìn)行的練習(xí),并不是說在現(xiàn)代程序中應(yīng)該使用這種方式進(jìn)行優(yōu)化?,F(xiàn)在早已不是在“指令級別”進(jìn)行性能優(yōu)化的時(shí)期了,連操作系統(tǒng)內(nèi)核也只是在一些對性能要求非常高的地方,如內(nèi)存管理,線程調(diào)度中的細(xì)微方面使用匯編來編寫,其余部分也都是用C語言來完成。這并不是僅僅是因?yàn)?ldquo;可維護(hù)性”等考慮,也有部分原因是因?yàn)樵谀壳熬幾g技術(shù)的發(fā)展下,一些極端的做法已經(jīng)很難產(chǎn)生有效的優(yōu)化效果了(例如一般來說來,程序員寫出的C代碼的性能會優(yōu)于他寫的匯編代碼)。

  此外,在您不知道JIT究竟作了什么事情的情況下,觀察IL這樣一種高度抽象的語言,您還是無法真正判斷出一個(gè)程序從微觀上的性能如何。不過這并不是說,現(xiàn)代程序不應(yīng)該“主動”追究性能,而是說,現(xiàn)代程序在性能優(yōu)化問題上并非如此簡單,它涉及到的東西會更多,需要更加合適的手段。例如,即使您內(nèi)聯(lián)了一個(gè)子過程,也只是減少了call指令的所帶來的消耗,但是這與這個(gè)子過程本身“一長串”指令相比,所帶來的提高是微乎其微的。而如果您一旦破壞了Locality或造成了False Sharing,或造成了資源競爭等等,這可能就會造成數(shù)倍甚至更多的性能損耗。換句話說,影響現(xiàn)代應(yīng)用程序的性能的因素大都是“宏觀”的,用通俗的話來說,一般都是“寫法”上造成的問題。

  這也是為什么說“Make clean code fast”遠(yuǎn)比“Make fast code clean”來的容易,現(xiàn)代程序更注重的是“清晰”而并非是“性能”。因?yàn)槌绦蚯逦?,更容易讓人發(fā)現(xiàn)性能瓶頸究竟在何處,可以進(jìn)行有針對性地優(yōu)化(即使是那種在極端性能要求下故意進(jìn)行的“丑陋”寫法,也是為了高性能而“丑陋”,而不是因?yàn)?ldquo;丑陋”而高性能,分清這一點(diǎn)很重要)。換句話說,如果我們有一種更清晰地方式來查看同樣的程序?qū)崿F(xiàn),不也降低了探索程序性能瓶頸的難度嗎?那么,同樣一段程序,您會通過C#進(jìn)行觀察,還是使用IL呢?

  有朋友可能會說:即使無法把握J(rèn)IT對于IL的優(yōu)化,但是從IL中可以看出高級語言,如C#的編譯器的優(yōu)化效果啊。這話本沒有錯(cuò),但問題還是在于,C#的編譯器優(yōu)化效果,是否在“反編譯”回來之后就無法觀察到了呢?“優(yōu)化過程”往往都是不可逆的,它會造成信息丟失,導(dǎo)致我們很難從“優(yōu)化結(jié)果”中看出“原始模樣”,這一點(diǎn)在上一篇文章中也有過論述。換句話說,我們通過C# => IL => C#這一系列“轉(zhuǎn)化”之后,幾乎都可以清楚地發(fā)現(xiàn)C#編譯器做過哪些優(yōu)化。這里還是使用經(jīng)典的foreach作為示例,您知道以下兩個(gè)方法的性能高低如何?

static void DoArray(int[] source){    foreach (int i in source)    {        Console.WriteLine(i);    }}static void DoEnumerable(IEnumerable<int> source){    foreach (int i in source)    {        Console.WriteLine(i);    }}

  經(jīng)過了C#編譯器的優(yōu)化,再使用.NET Reflector查看IL反編譯成C#(None Optimization)的結(jié)果,就會發(fā)現(xiàn)它們變成了此般模樣:

private static void DoArray(int[] source){    int num;    int[] numArray;    int num2;    numArray = source;    num2 = 0;    goto Label_0014;Label_0006:    num = numArray[num2];    Console.WriteLine(num);    num2 += 1;Label_0014:    if (num2 < ((int)numArray.Length))    {        goto Label_0006;    }    return;}private static void DoEnumerable(IEnumerable<int> source){    int num;    IEnumerator<int> enumerator;    enumerator = source.GetEnumerator();Label_0007:    try    {        goto Label_0016;    Label_0009:        num = enumerator.Current;        Console.WriteLine(num);    Label_0016:        if (enumerator.MoveNext() != null)        {            goto Label_0009;        }        goto Label_002A;    }    finally    {    Label_0020:        if (enumerator == null)        {            goto Label_0029;        }        enumerator.Dispose();    Label_0029: ;    }Label_002A:    return;}

  C#編譯器的優(yōu)化效果表露無遺:對于int數(shù)組的foreach其實(shí)是被轉(zhuǎn)化為類似于for的下標(biāo)訪問遍歷,而對于IEnumerable還是保持了foreach關(guān)鍵字中標(biāo)準(zhǔn)的“while...MoveNext”模式(如果您把Console.WriteLine語句去掉的話,就可以使用.NET 3.5 Optimization直接看出兩者的不同,您不妨一試)。由此看來,DoArray的性能會比后兩者要高。事實(shí)上,由于性能主要是由“實(shí)現(xiàn)方式”決定的,因此我們可以通過反編譯成C#代碼的方式來閱讀.NET框架中的大部分代碼,IL在這里起到的效果很小。例如在文章《泛型真的會降低性能嗎?》里,Teddy大牛就通過閱讀.NET代碼來發(fā)現(xiàn)數(shù)組的IEnumerable實(shí)現(xiàn),為什么性能遠(yuǎn)低于IEnumerable。

  不過,判斷兩者性能高低,最簡單,也最直接的方式還是進(jìn)行性能測試。例如您可以使用CodeTimer來比較DoArray和DoEnumerable方法的性能,一目了然。

  值得一提的是,如果要進(jìn)行性能優(yōu)化,需要做的事情有很多,而“閱讀代碼”在其中的重要性其實(shí)并不高,而且它也最容易誤入歧途的一種。“閱讀代碼”充其量是一種人工的“靜態(tài)分析”,而程序的運(yùn)行效果是“動態(tài)”的。這篇文章解釋了為什么使用foreach對ArrayList進(jìn)行遍歷的性能會比List低,其中使用了Profiler來說明問題。Profiler能告訴我們很多難以觀察到的事情,例如在遍歷中究竟是ArrayList哪個(gè)方法消耗時(shí)間最長。此外它還發(fā)現(xiàn)了ArrayList在遍歷時(shí)創(chuàng)建了大量的對象,這種對于內(nèi)存資源的消耗,幾乎不可能從一小段代碼中觀察得出。此外,不同環(huán)境下,同樣的代碼可能執(zhí)行效果會有不同。如果沒有Profiler,我們可能會選擇把一段執(zhí)行了100遍的代碼性能提升1秒鐘,卻不會把一段執(zhí)行100000遍的代碼性能提升100毫秒。性能優(yōu)化的關(guān)鍵是“有的放矢”,如果沒有Profiler幫我們指明道路,做到這一點(diǎn)相當(dāng)困難。

  其實(shí)老趙對于性能方面說的這些,可以大致歸納為以下三點(diǎn):

  • 關(guān)注IL,對于從微觀角度觀察程序性能很難有太大幫助,因?yàn)槟茈y具體指出JIT對IL的編譯方式。
  • 關(guān)注IL,對于從宏觀角度觀察程序性能同樣很難有太大幫助,因?yàn)樗谋硎瞿芰Σ粫菴#來的直觀清晰。
  • 性能優(yōu)化,最關(guān)鍵的一點(diǎn)是使用Profiler來找出性能瓶頸,有的放矢。

  所以,如果您問老趙:“學(xué)習(xí)IL,對寫出高性能的.NET程序有幫助嗎?”我會回答:“有,肯定有啊”。

  但是,如果您問老趙:“我想寫出高性能的.NET程序,應(yīng)該學(xué)習(xí)IL嗎?”我會回答:“別,別學(xué)IL”。

總結(jié)

  feilng在前文留下的一些評論,我認(rèn)為說得非常有道理:

IL只是在CLR的抽象級別上說明干什么,而不是怎么干……重要的是要清楚在現(xiàn)實(shí)條件下,需要進(jìn)入那個(gè)層次才能獲取足夠的信息,掌握接口的完整語義和潛在副作用。

  IL的確比C#等高級語言來的所謂“底層”,但是很明顯,IL本身也是一種高級抽象。而即使是機(jī)器碼,它也可以說是基于CPU的抽象,CPU上如流水線,并行,內(nèi)存模型,Cache Lock等東西對于匯編/機(jī)器碼來說也可以說是一種“封裝”。從不同層次可以獲得不同信息,我們追求“底層”的目的肯定也不是“底層”這兩個(gè)字,而是一種收獲。了解自身需要什么,然后能夠選擇一個(gè)合理的層次進(jìn)入,并得到更好的收益,這本身也是一種能力。追求IL的做法,本身并沒有錯(cuò),只是追求IL一定是當(dāng)前情況下的最優(yōu)選擇嗎?這是一個(gè)值得不斷討論的問題,我的這篇文章也只是表達(dá)了我個(gè)人對某些問題的看法。

 

NET技術(shù)老趙談IL(3):IL可以看到的東西,其實(shí)大都也可以用C#來發(fā)現(xiàn),轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 国产亚洲精品俞拍视频 | 69视频免费观看 | 超级97人人公开视频 | 日本免费一区二区三区看片 | 久久久久久九九 | 国产在线观看人成激情视频 | 国产成人精品久久综合 | 黄色网页免费观看 | 久久亚洲精品成人 | 日韩欧美91 | 丁香六月婷婷激情 | 色播亚洲精品网站 亚洲第一 | 伊人精品网 | 欧美亚洲激情在线 | 国产成人精品s8p视频 | 激情深爱五月 | 欧美黑人巨大xxxxx | 久久久久久久久久免观看 | 四虎永久网址在线观看 | 丁香六月色婷婷 | 在线观看一区二区三区四区 | 亚洲精品91在线 | 欧美一区二区三区在线可观看 | 亚洲国产精品一区二区三区久久 | 国产精品秒播无毒不卡 | 亚洲欧美精品久久 | 国产极品久久 | 久久精品国产精品青草不卡 | 欧美一区色 | 国产精品免费精品自在线观看 | 黄视频网址 | 欧美成人性色区 | 国产乱来视频 | 色在线免费 | 91福利国产在线在线播放 | 五月综合激情网 | 中文字幕伦伦在线中文字 | 国产精品视频一区二区噜噜 | 成人午夜视频网站 | 激情图片小说网 | 一本久久道 |