日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#会重蹈覆辙吗?系列之2:反射及元数据的性能问题

發(fā)布時間:2025/4/16 C# 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#会重蹈覆辙吗?系列之2:反射及元数据的性能问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?

理清幾個基本點

在開始談論性能問題之前,有必要首先理清幾個基本點。我們談C#,就是在談.NET Framework(或者更準確一點是CLR,因為.NET Framework除了CLR還包括BCL);談.NET Framework(CLR),也就是在談C#。因為支撐C#語法之后的就是整個CLR的機制。因此,我說C#性能不好,和說CLR性能不好,說的是一個事情(就像說Java性能不好,就是說JVM性能不好一樣)。我不希望在我下面說C#某個地方性能不好的時候,再有論者立即指出來“那不是C#的問題,那是CLR的問題,或者.NET Framework的問題”——如果對C#和.NET還停留在這個認識上,請先去讀讀Jeffrey Richter的《CLR via C#》一書,再來看我下面的文章。

?

另外,我說C#性能有問題,僅針對C#而言,與我對其他語言的態(tài)度無關。我既不是Java的支持者(因為Java的性能比C#還慢),也不是C++的支持者(C++太過臃腫復雜),也不是C的支持者(沒有基本的面向?qū)ο蟪橄蠛屠厥?#xff09;。我既不喜歡任何語言,也不討厭任何語言。編程語言在我只是一個工具——我只是希望這個工具是把鋒利的牛刀,而不是把功能齊全的瑞士小刀。

?

最后我不是毫無選擇地反對“新功能”,我反對的是“添加的功能、沒有重大抽象意義,卻帶來性能損失”,如果有“提高性能的新功能”——比如并發(fā)編程,或者“對管理軟件復雜度”有重大意義,同時性能損失很小很小——比如面向?qū)ο?/span>,那我舉雙手贊成?!?/p>

?

在理清了前面幾個基本點之后,下面開始來針對我前文說過的一些問題一一“講原理”。這篇文章中,我首先來剖析反射的性能問題。

?

反射的兩大類性能問題

?

【一】反射綁定與調(diào)用——使用反射帶來的性能問題

?

反射的綁定與調(diào)用性能差,我想大概做過.NET開發(fā)的人都不會懷疑這一點。但是我還是希望那些嚴肅的程序員認真看看微軟CLR程序經(jīng)理Joel Pobar在MSDN上的這篇文章:Dodge Common Performance Pitfalls to Craft Speedy Applications http://msdn.microsoft.com/en-us/magazine/cc163759.aspx,清楚理解反射綁定與調(diào)用的效率到底為什么那么差?有多差?差在哪里?

?

限于篇幅關系,我簡單在這里總結(jié)一下,反射綁定與調(diào)用的性能問題(具體原理,大家參照MSDN這篇文章):

  • 首先要經(jīng)過一個綁定過程,非常耗時(用字符串名稱和metadata里面的字符串進行比對,字符串查找的算法大家都知道是很慢的操作)
  • 然后要進行參數(shù)個數(shù)、類型等的校驗;如果不匹配還要搜索可能的類型轉(zhuǎn)換
  • 進行CAS代碼訪問安全的驗證,看允不允許調(diào)用。
  • 以上幾個工作,如果不用反射應該是由C#編譯器負責在編譯時檢查的。但是現(xiàn)在如果用反射,全都放到了運行時檢查。
  • 這其中會產(chǎn)生一大堆的臨時對象(比如MemberInfo Cache),給垃圾收集器造成巨大負擔
  • 縱然有一些對反射綁定和調(diào)用的cache優(yōu)化策略,Joel Pobar在這篇文章中給的最大的建議還是:能不用反射,則不用反射,因為性能成本太高。
  • 結(jié)論:反射調(diào)用的性能成本很高(參見msdn文章中中圖2 Relative Performance of Invocation Mechanism)。
  • 我想這些性能問題,大家都會認可。但有些朋友會說“我.NET程序中用反射的很少啊?”,首先且不論你用的少不少,但是微軟開發(fā)的很多Application Framework對反射的使用現(xiàn)在越來越多,比如大量使用反射“綁定與調(diào)用”的例子(注意是大量,不是一點點!):

  • WPF和Silverlight中的XAML序列化-反序列化,依賴屬性,數(shù)據(jù)綁定
  • ASP.NET MVC中路由、控制器,視圖等的匹配查找(反射綁定)和調(diào)用(反射調(diào)用)
  • WCF分布式通信中大量的實例激活,方法調(diào)用,序列化與反序列化
  • WF中大量的工作流流程激活、控制、調(diào)用
  • ………..上面幾乎把.NET平臺的主要應用框架都包括了,不用再舉更多例子了吧?誰能脫離這些應用框架去寫程序?
  • 所以說,你用反射用的少,并不代表你最后做出的軟件用反射的少(你的軟件的代碼不可能全都是自己寫的,很多都是依附于微軟的Application Framework,只要這些Application Framework很重地使用了反射,那么你的軟件也就很重的使用了反射)

    ?

    但有朋友會立即指出“我不用WPF/SL,不用WCF、不用WF、不用ASP.NET MVC,類庫都是自己寫,代碼全都是自己寫,保證反射用的很少,甚至確保壓根沒有使用反射,這些性能負擔不久沒有了嗎?”這個問題很好! 也是前面談到.NET各種功能帶來的性能問題的時候,很多朋友最喜歡的辯詞——不用它不就是了嘛!

    ?

    首先如果有這樣的C#程序員,我定佩服你如滔滔江水…….但是,我這里要告訴大家的事實是,“即便你程序中確實所有的代碼都不使用反射,由于C#/.NET內(nèi)置地支持反射,那么你也要為此付出性能代價,而且是很高的性能代價”。這是本文的重點,甚至是我后續(xù)很多論戰(zhàn)文章的重點——很多C#/.NET機制,不管你用不用它,只要內(nèi)置支持這種機制,就不可避免要付出性能代價(當然如果你要用它,還有更多性能代價)。

    ?

    好,下面讓我們來談談為什么,即便不用反射也要付出很高的性能代價?(這也是MSDN那篇文章所刻意回避的話題)。

    ?

    ?

    【二】反射背后需要的支撐機制:元數(shù)據(jù)的性能問題——不使用反射的性能問題

    ?

    ???????? 要談這個問題,首先大家應該清楚C#/.NET中反射的功能是由metadata來支持的,即便你所有的代碼中、你用的所有Application Framework的代碼中都沒有使用一點反射的API,C#編譯器還是會在最后生成的EXE或者DLL中生成所有的metadata。(如果這個不清楚,請先讀Jeffrey Richter的《CLR via C#》一書)。而?Metadata就是C#/.NET性能的罪魁禍首!要理解這一點,大家先來做兩個簡單的針對metadata的分析。

    1.??用ILDASM工具將C:\Windows\Microsoft.NET\Framework\v4.0.30128 下面的MSCorlib.dll(.NET核心類庫程序集,其他版本也可以,不必非要4.0)打開。點擊:View->Statistics,看一下其中的元數(shù)據(jù)大小:

    ?

    CLR header size???? : 72???????????????? ( 0.00%)

    CLR meta-data size? : 2083724??????????? (40.09%)

    CLR additional info : 931312???????????   (17.92%)

    CLR method headers? : 136967???????????? ( 2.64%)

    Managed code???????? : 1212346?????????? (23.32%)

    Data???????????????? : 753152??????????? (14.49%)

    注意:這四個部分,其要么是metadata,要么是metadata的輔助信息,所以我在后面文章中都算作元數(shù)據(jù)部分:

    整個MSCorlib.dll大小為4.95M。

    Metadata總共占用大約3.01M,占總大小大約60.6%。

    真正傳統(tǒng)的Code+Data總共占用大約1.87M,占總大小約37.8%。

    ?

    MSCorlib.dll總共大小4.95M,為了支持反射,需要添加的元數(shù)據(jù)竟然有3.01M,占到60%的大小!!!我想大家已經(jīng)看出問題來了。有些朋友可能會說,這是特例吧?別的DLL呢?

    ?

    2.??我們再來隨便找一個DLL,比如WPF的DLL:C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll,同樣適用ILDASM打開,點擊:View->Statistics看一下其中的元數(shù)據(jù)大小:

    ?

    ?

    整個PresentationFramework.dll大小為5.03M。Metadata總共占用大約55.15%!

    ?

    大家可以隨便拿一個自己項目中.NET的DLL或者EXE來分析,看看Metadata的大小占用多少? 基本都在50%以上,甚至有的高達70%! 

    ?

    這意味著什么?即使你不用任何反射的代碼,C#/.NET為了讓它支持反射,還要給你最后生成的DLL/EXE強加50%以上的metadata(這是強制的,即便你不用反射,C#/.NET也沒有提供任何編譯選項將這些metadata去掉)。這就是.NET Framework Redistributable本身要40M左右的原因!

    ?

    我想這個鐵的事實是“老趙們”無論如何都不能否認的。但是“老趙們”的典型言論馬上又來了:

    (1)不就是程序有點大嗎?現(xiàn)在大硬盤很便宜,運行起來還是很快的

    (2)就是.NET Framwork有點大,客戶安裝起來不方便

    (3)大只是空間效率,不影響程序的時間效率

    ?

    這些調(diào)調(diào)顯然都是沒有真正搞過“性能優(yōu)化”的“老趙們”的淺見。空間效率并非對時間效率沒有影響,而是有致命影響。一個100M的應用程序,運行起來肯定要比一個40M的程序慢許多。理由如下:

    ?

    (1)程序(EXE/DLL)最后都是要加載到內(nèi)存中運行的,不是光放在硬盤上的——這也是為什么.NET程序占用內(nèi)存都超多

    (2)占用內(nèi)存多的程序,運行起來必然慢。因為內(nèi)存大的程序必然會出現(xiàn)較多的page fault(即換頁錯誤),cache missing(即緩存失效)(簡單來說,要盡可能在CPU緩存中操作working set,CPU緩存裝不下,就要跑到主存里面找;主存裝不下就要跑到虛擬內(nèi)存-也就是硬盤里面找,那樣軟件運行的性能代價非常高). Page fault和cache missing已經(jīng)成為現(xiàn)代軟件性能的一大公害。很多程序慢下來,如果不是蹩腳的算法,Page fault和cache missing往往都是罪魁禍首!關于這方面的理論,很多牛人都專門講過,國外也有比較牛叉的咨詢公司專門做這方面的優(yōu)化,大家如果想深度理解這方面,可以參照:

    a. CACHE MEMORY:IMPLEMENTATION ANDDESIGN TECHNIQUES

    http://www.faculty.iu-bremen.de/birk/lectures/PC101-2003/07cache/cache%20memory.htm 

    ?

    b. Improving Managed Code Performance-Working SetConsiderations

    http://msdn.microsoft.com/en-us/library/ff647790.aspx#scalenetchapt05_topic33

    ?

    c.以及微軟的.NET性能經(jīng)理Rico Mariani在這里的文章:

    My mom doesn't care about space,http://blogs.msdn.com/b/ricom/archive/2004/03/15/89934.aspx

    ?

    所以,總結(jié)下來就是:

    (1)Metadata非常占用空間,一般占到整個EXE/DLL總大小的50%~70%

    (2)高昂的空間成本會由于Page fault和cache missing等因素轉(zhuǎn)嫁為高昂的時間成本

    (3)即便在代碼中不寫一行反射調(diào)用代碼,所有的metadata仍然會生成,我們?nèi)匀灰獮榇烁冻龈甙旱目臻g代價和時間代價。

    ?

    比如,我們公司開發(fā)的一個大型醫(yī)療軟件,之前的版本使用C++開發(fā),整個生成代碼體積為40M左右,但是轉(zhuǎn)移到.NET平臺上(被微軟的.NET平臺戰(zhàn)略忽悠過來)后發(fā)現(xiàn)代碼體積為130M左右(功能差不多的前提下,第一版主要是移植,新增功能的代碼量占不到5%),我們反反復復怎么優(yōu)化都優(yōu)化不到原來的40M左右,最后發(fā)現(xiàn)都是反射惹的禍!——我相信我在前文舉出的很多世界著名、或者中國著名的軟件最終沒有選擇.NET,都有過這樣一個評測過程。

    ?

    其他的例子大家可以自己找,比如就拿mspaint.exe 與paint.net(到這里下載:http://www.softpedia.com/progDownload/Paint-NET-Download-19322.html)比較比較,功能差不多相同。運行一下看看,它們各占多少內(nèi)存:前者5.7M,后者占用17.7M!3倍多!

    ?

    軟件size大,沒關系,你要大在地方,比如因為功能原因,code多一些導致size大我接受。但是你50%-70%的size都去裝metadata了,而我又不怎么用metadata(反射),你還要這么大放在那里,極大地損害軟件性能。

    ?

    這還是一個小小paint玩具軟件!你讓QQ、photoshop,office等軟件用C#/.NET開發(fā)試試?除非是“老趙們”自己開公司玩。

    ?

    ?

    反射性能問題總結(jié)

    ?

    好了,我相信問題已經(jīng)分析清楚了,總結(jié)一下到目前為止,這篇文章的重點:

    1.??反射的綁定和調(diào)用成本很高

       ——?C#反射綁定與調(diào)用過程中元數(shù)據(jù)字符串比對,參數(shù)校驗,安全校驗,大量臨時對象,會讓使用C#反射時的軟件性能很差,盡量避免使用

    2.??你不使用某些性能低的功能,不代表你依附的Application Framework不使用這些功能

       —— 目前.NET平臺中WPF/SL, WCF,WF, ASP.NET MVC等幾大核心的框架都很重地使用了反射

    3.??有些功能即便程序中不使用,為了支持這種機制,也要付出很高的代價

       ——?哪怕所有的代碼都是你寫(不用Application Framework),而且不用一點反射的功能,C#編譯器還是給你的軟件中加了很多支持反射的metadata,占用很高昂的空間成本(大約是整個軟件size的50%)

    4.??只要有較大的空間成本,那么時間成本也一定很高

      —— 反射背后的metadata占用的高昂的空間成本,由于內(nèi)存加載、working set、cache missing 等各種問題,直接導致的時間成本很大,嚴重影響軟件的運行性能。

    ?

    上面的分析方法、依據(jù)、包括數(shù)據(jù)都是我和公司美國、德國同事,在開發(fā)C#/.NET產(chǎn)品時(大型醫(yī)療軟件),遇到的非常實際的問題(客戶接受不了C#/.NET寫的軟件速度),用符合工程的系統(tǒng)、全面的分析方法,研究各領域?qū)<业姆治鲆庖?#xff08;包括很多微軟技術專家),對C#/.NET進行的性能研究(不是寫個CodeTimer玩具比較比較兩段代碼就叫性能分析),我們嘗試了很多優(yōu)化策略——最后的結(jié)論就是繞不開C#/.NET底層設計帶來的根深蒂固的性能問題!反射就是一個性能公害!

    ?

    好,相信看到這里,絕大多數(shù)朋友已經(jīng)深入理解了“反射所帶來的嚴重的性能問題”。但是有很多朋友可能還會有疑問,咦?怎么有些人寫C#性能也不錯,而且寫得頭頭是道,似乎很有道理啊。到底誰說的對啊?

    ?

    這樣的疑問很正常,這些論調(diào)就是我前文說的“只見樹木,不見森林”。為了理清網(wǎng)友的疑問,我在下面的小節(jié)中針對這些“一葉障目”的觀點進行一一戳穿,以便于大家今后明辨是非。

    ?

    ?

    幾種典型的錯誤的性能論調(diào)或方法

    ?

    1.??函數(shù)計時論

    要比較性能嗎?那好我們寫一段函數(shù),用一個時間計數(shù)器,在函數(shù)執(zhí)行開始處記錄下時間,在函數(shù)執(zhí)行結(jié)束前記錄下時間,最后一減得到的時間差,同樣的功能,哪個語言(或者哪種方式)用的時間少,哪個語言(或者哪個方式)用的時間多,性能差別,一目了然。多客觀啊!!!

    ?

    比如,老趙曾經(jīng)在這篇博文中:一個簡單的性能計數(shù)器:CodeTimer http://www.cnblogs.com/jeffreyzhao/archive/2009/03/10/codetimer.html 抄襲.NET技術大會上Jeffrey Richter老人家show的性能計數(shù)器。

    ?

    然后下面這兩篇文章都是用這種“函數(shù)計時論”:

    《C# vs C++ 全局照明渲染性能比試》: http://www.cnblogs.com/miloyip/archive/2010/06/23/cpp_vs_cs_GI.html

    《回firelong之C#慢》 http://www.cnblogs.com/sumtec/archive/2010/06/22/1762564.html

    ?

    問題是這種做法真的全面、客觀的反映了編程語言的性能了嗎???用這種辦法你可以說某一段C#代碼性能還湊合(比如《C# vs C++ 全局照明渲染性能比試》一文中的實驗結(jié)果,比C/C++差也就20~30%嘛,差的不多嘛!),但是問題是,這就是它們性能差別的全部真相嗎?

    ?

    函數(shù)記時論,測量的只是某一個微觀代碼段的性能。不是一個軟件的總體性能。比如“函數(shù)記時論”就常常忽略掉我們前面metadata所帶來的高額的“空間成本”和“時間成本”。正規(guī)公司,只要是care性能的,對于性能評測都有一個系統(tǒng)的、全面的、完整的過程(比如在我們公司稱作Performance Process,和單元測試、重構(gòu)、等都作為一個嚴肅的軟件開發(fā)過程中的一個環(huán)節(jié)而存在),會借助一些系統(tǒng)性的工具:比如Compuware的Application Performance Management Solutions:參見這里:http://www.compuware.com/solutions/application-performance-management.asp來做一些系統(tǒng)性的評測報告。不是拿個CodeTimer這樣的玩具輸出幾個時間值,就拍腦袋下結(jié)論的。

    ?

    函數(shù)計時論經(jīng)常在各種技術社區(qū)中,吵架時展示的tricky demo中用于比較性能,但是放到一個正規(guī)公司的嚴肅項目里面,絕對不會使用這種方法來評估一個編程語言,平臺,或者軟件的性能。

    ?

    我希望 “老趙們”以后不要再拿CodeTimer這種玩具說事,要真全面比較性能,用Compuware的Application Performance Management Solutions一整套工具和過程來比較整個軟件的性能,而不是某一段微觀代碼的性能。

    ?

    ?

    2.??性能選擇論

    某個功能影響性能,你不用不就沒影響了嗎?又沒有人逼你用!

    ?

    前面已經(jīng)證明,C#/.NET的反射功能,你哪怕一點也不用,也有很大的性能成本(即:代碼中完全不用反射,為了支持反射的metadata帶來的空間成本和時間成本也非常高昂)。所以希望以后“老趙們”不要再說這樣的話。

    ?

    3.??損失忽視論

    這個功能帶來的性能損失是很小的,可以忽略不計。

    ?

    性能是一個軟件最核心的使用指標——如果一個軟件性能不行,就是差軟件!沒有哪些個性能損失是可以忽略不計的。因為在程序代碼中,任何一個性能損失點,都有可能因為各種因素被放大(比如長循環(huán),大規(guī)模并發(fā)用戶等)。

    ?

    “老趙們”喜歡寫“性能不咋地的高級企業(yè)應用”,然后忽悠客戶加硬件。但是請不要忽悠整個.NET社區(qū)的程序員以為天下的軟件都是“很高級的企業(yè)應用”。

    ?

    4.??性能墊背論

    ?

    “Java的這個feature性能比C#的差,所以C#這個feature性能好”——C#的某些feature(比如反射)性能比Java好,但并不能說明這個feature本身沒有性能問題(這只能說明Java在這個上面性能太差,說明不了C#性能好)。

    ?

    請“老趙們”以后不要天天在.NET社區(qū)里說“C#這個比Java好,那個比Java cool”,這就像天天告訴自己的孩子,你比你們班最后一名的那個孩子好多了,你說孩子還能學好嗎???你怎么總拿C#跟差的比,不跟好的比呢?

    ?

    ?

    最后結(jié)語

    ?

    好,文章寫完了,我希望.NET技術社區(qū)的“老趙們”圍繞“反射的性能話題”來辯駁,不要扯別的話題來放煙霧彈(C#/.NET中別的技術話題,我會在下面的文章中一篇一篇來討論,請大家耐心等待給我一點時間)。謝謝!

    ?

    正要貼本文的時候,看到《關于C#開發(fā)山寨操作系統(tǒng),程序語言,瀏覽器,IDE,Office,Photoshop等大型程序的可行性歪論及意義》http://www.cnblogs.com/DSharp/archive/2010/06/24/1764210.html 這篇文章。我的回答非常明確:沒有任何可行性,且不論商業(yè)可行性、其他技術問題,光反射一項帶來的兩大性能負擔就把路堵死了——這也是我前文說的那么多軟件為什么不采用C#開發(fā)的一個關鍵原因——你搞一個100M的程序,中間有50M都是metadata,你還讓人程序活下去嗎?(記住,50M不僅僅是空間成本,帶來的時間成本照樣很大!)

    ?

    P.S. 本文中的“老趙們”指的是那些天天拿著C#語言新特性??岜硌?、而不研究真實技術問題的“所謂的技術精英們”,并不特指老趙一個人,或者老趙的每一個階段(老趙有一段時間還算在研究真問題)。請不要對號入座,謝謝!

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/firelong/archive/2010/06/24/1764597.html

    總結(jié)

    以上是生活随笔為你收集整理的C#会重蹈覆辙吗?系列之2:反射及元数据的性能问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。