国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

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

高性能托管應(yīng)用程序設(shè)計(jì)入門

2019-11-17 04:40:09
字體:
供稿:網(wǎng)友

  摘要:從性能的角度來學(xué)習(xí) .NET Framework 公共語言運(yùn)行庫。學(xué)習(xí)如何找出托管代碼性能的最佳方法,以及如何測(cè)量托管應(yīng)用程序的性能。
  下載 CLR PRofiler。(330KB)

  軟件開發(fā)和玩雜耍

  我們可以將軟件開發(fā)過程比作玩雜耍。通常,人們?cè)谕骐s耍時(shí)至少需要三件物品,而對(duì)于可以使用的物品數(shù)量并沒有任何限制。剛開始學(xué)習(xí)如何玩雜耍時(shí),您會(huì)發(fā)現(xiàn)在接球和拋球時(shí)您看到的是單個(gè)的球。隨著熟練程度的增加,您會(huì)開始將注重力集中在這些球的運(yùn)動(dòng)上,而不是集中在每個(gè)單個(gè)的球上。當(dāng)您把握了玩雜耍的方法時(shí),您就會(huì)再次將注重力集中在單個(gè)球上,一邊努力使這個(gè)球保持平衡,一邊繼續(xù)拋出其他的球。憑直覺您就可以知道這些球的運(yùn)動(dòng)方向,并且也總是能將手放在正確的地方接球和拋球。可是,這與軟件開發(fā)又有什么類似之處呢?

  不同的角色在軟件開發(fā)的過程中會(huì)“耍弄”不同的三件物品:項(xiàng)目和程序治理人員耍弄的是功能、資源和時(shí)間,而軟件開發(fā)人員則耍弄正確性、性能和安全性。人們?cè)谕骐s耍時(shí)總是試圖使用更多的物品,但只要是學(xué)過雜耍的人都會(huì)知道,即使只添加一個(gè)球都會(huì)讓在空中保持所有球的平衡變得更加困難。從技術(shù)上講,假如耍弄的球少于三個(gè),那根本就不叫雜耍。作為軟件開發(fā)人員,假如您不考慮正在編寫的代碼的正確性、性能和安全性,則說明您根本就沒有投入到您的工作中。當(dāng)您剛剛開始考慮正確性、性能和安全性時(shí),您會(huì)發(fā)現(xiàn)您每次只能將注重力集中在一個(gè)方面。當(dāng)它們成為您日常工作的一部分時(shí),您會(huì)發(fā)現(xiàn)您再也不需要將注重力集中在某一個(gè)特定的方面,因?yàn)榇藭r(shí)它們?cè)缫讶谌肓四墓ぷ鳌6坏┌盐樟怂鼈儯湍軌驊{直覺進(jìn)行權(quán)衡,并相應(yīng)地調(diào)整自己的注重點(diǎn)。這就跟玩雜耍一樣,要害在于實(shí)踐。

  編寫高性能的代碼本身也存在三件物品:設(shè)置目標(biāo)、測(cè)量和了解目標(biāo)平臺(tái)。假如您不知道代碼必須達(dá)到的運(yùn)行速度,那么您又如何知道您完成了呢?假如您不測(cè)量和分析代碼,那么您又如何知道您已經(jīng)達(dá)到了目標(biāo),或?yàn)槭裁礇]有實(shí)現(xiàn)目標(biāo)呢?假如您不了解目標(biāo)平臺(tái),那么當(dāng)您沒有實(shí)現(xiàn)目標(biāo)時(shí),又如何知道要對(duì)什么進(jìn)行優(yōu)化呢?這些原則對(duì)所有的高性能代碼開發(fā)過程幾乎都適用,不管您以哪種平臺(tái)作為目標(biāo)平臺(tái)都一樣。要完成一篇有關(guān)編寫高性能代碼的文章,就必須提到以上這三個(gè)方面。雖然這三個(gè)方面都同等重要,但是本文的重點(diǎn)在于后兩個(gè)方面,因?yàn)樗鼈冞m用于編寫以 Microsoft? .NET Framework 作為目標(biāo)平臺(tái)的高性能應(yīng)用程序。

  在任意平臺(tái)上編寫高性能代碼的基本原則為:

  1、設(shè)置性能目標(biāo)
  2、測(cè)量,測(cè)量,再測(cè)量
  3、了解應(yīng)用程序的目標(biāo)硬件和軟件平臺(tái) .NET 公共語言運(yùn)行庫

  .NET Framework 的核心是公共語言運(yùn)行庫 (CLR)。CLR 為您的代碼提供所有的運(yùn)行時(shí)服務(wù):實(shí)時(shí)[Denver1]編譯、內(nèi)存治理、安全性和大量其他服務(wù)。CLR 的設(shè)計(jì)考慮了高性能的要求。也就是說,您既可以充分利用其性能,也可以不發(fā)揮這些性能。

  本文將從性能的角度概要介紹公共語言運(yùn)行庫,找出托管代碼性能的最佳辦法,還將展示如何測(cè)量托管應(yīng)用程序的性能。本文并不打算對(duì) .NET Framework 的性能特點(diǎn)進(jìn)行全面討論。根據(jù)本文的目的,文中提到的性能方面的內(nèi)容將會(huì)包括吞吐量、可擴(kuò)展性、啟動(dòng)時(shí)間和內(nèi)存使用量等。

  托管數(shù)據(jù)和垃圾回收器

  在以性能為中心的應(yīng)用程序中使用托管代碼時(shí),開發(fā)人員最關(guān)心的問題之一就是 CLR 內(nèi)存治理的開銷 - 這種治理由垃圾回收器 (GC) 執(zhí)行。內(nèi)存治理的開銷由與類型實(shí)例關(guān)聯(lián)的內(nèi)存的分配開銷、在實(shí)例的整個(gè)生命周期中內(nèi)存的治理開銷以及當(dāng)不再需要時(shí)內(nèi)存的釋放開銷計(jì)算得出。

  托管分配的開銷通常都非常小,在大多數(shù)情況下,比 C/C++ malloc 或 new 所需的時(shí)間還要少。這是因?yàn)?CLR 不需要通過掃描可用列表來查找下一個(gè)足以容納新對(duì)象的可用連續(xù)內(nèi)存塊,而是一直都將指針保持指向內(nèi)存中的下一個(gè)可用位置。我們可以將對(duì)托管堆的分配看作“類似于棧”。假如 GC 需要釋放內(nèi)存才能分配新對(duì)象,那么分配可能會(huì)引發(fā)回收操作。在這種情況下,分配的開銷就會(huì)比 malloc 和 new 大。固定的對(duì)象也會(huì)對(duì)分配的開銷造成影響。固定的對(duì)象是指那些 GC 接到指令在回收操作期間不能移動(dòng)其位置的對(duì)象,通常是由于這些對(duì)象的地址已傳遞到本機(jī) API 中。

  與 malloc 或 new 不同的是,在對(duì)象的整個(gè)生命周期中治理內(nèi)存都會(huì)帶來開銷。CLR GC 區(qū)分不同的代,這就意味著它不是每次都回收整個(gè)堆。但是,GC 仍然需要了解剩余堆中的活動(dòng)對(duì)象的根是否是堆中正在回收的那部分對(duì)象。假如內(nèi)存中包含的對(duì)象具有對(duì)其下一代對(duì)象的引用,那么在這些對(duì)象的生命周期中,治理內(nèi)存的開銷將會(huì)非常大。

  GC 是代標(biāo)記和空閑內(nèi)存的回收器。托管堆包含三代:第 0 代包含所有的新對(duì)象,第 1 代包含存活期較長的對(duì)象,第 2 代包含長期存活的對(duì)象。在釋放足夠的內(nèi)存以維持應(yīng)用程序繼續(xù)運(yùn)行的前提下,GC 將盡可能從堆中回收最小的部分。代的回收操作包括對(duì)所有下一代的回收,在這種情況下,第 1 代回收也會(huì)回收第 0 代。第 0 代的大小會(huì)根據(jù)處理器緩存的大小和應(yīng)用程序的分配率動(dòng)態(tài)變化,它用于回收的時(shí)間通常都不會(huì)超過 10 毫秒。第 1 代的大小根據(jù)應(yīng)用程序的分配率動(dòng)態(tài)變化,它用于回收的時(shí)間通常介于 10 到 30 毫秒之間。第 2 代的大小取決于應(yīng)用程序的分配配置文件,它用于回收的時(shí)間也取決于此文件。對(duì)治理應(yīng)用程序內(nèi)存的性能開銷影響最大的正是這些第 2 代的回收操作。

  提示:GC 具備自我調(diào)節(jié)能力,它會(huì)根據(jù)應(yīng)用程序內(nèi)存的要求對(duì)自身進(jìn)行調(diào)節(jié)。多數(shù)情況下,通過編程方式調(diào)用 GC 時(shí),它的調(diào)節(jié)功能都未啟用。通過調(diào)用 GC.Collect 來“幫助”GC 通常無法提高您的應(yīng)用程序的性能。

  GC 可以在回收對(duì)象期間重定位存活的對(duì)象。假如這些對(duì)象比較大,那么重定位的開銷也會(huì)較大,因此這些對(duì)象都將分配到堆中的一個(gè)稱為大對(duì)象堆 (Large Object Heap) 的非凡區(qū)域中。大對(duì)象堆可以被回收,但不能壓縮,例如,這些大對(duì)象不能進(jìn)行重定位。大對(duì)象是指那些大于 80k 的對(duì)象。請(qǐng)注重,在將來發(fā)行的 CLR 版本中此概念可能會(huì)有所變化。需要回收大對(duì)象堆時(shí),將強(qiáng)制進(jìn)行完全回收,而且是在回收第 2 代時(shí)才回收它們。大對(duì)象堆中對(duì)象的分配率和死亡率都會(huì)對(duì)治理應(yīng)用程序內(nèi)存的性能開銷產(chǎn)生很大的影響。

  分配配置文件

  托管應(yīng)用程序的全局分配配置文件定義了垃圾回收器對(duì)與應(yīng)用程序相關(guān)的內(nèi)存進(jìn)行治理的工作量有多大。GC 治理內(nèi)存的工作量越大,GC 所經(jīng)歷的 CPU 周期數(shù)就越多,而 CPU 運(yùn)行應(yīng)用程序代碼所花費(fèi)的時(shí)間也就越短。分配配置文件由已分配對(duì)象數(shù)、對(duì)象的大小及其生命周期計(jì)算得出。緩解 GC 壓力的一種最明顯的方法就是減少分配的對(duì)象數(shù)量。使用面向?qū)ο笤O(shè)計(jì)技術(shù)將應(yīng)用程序設(shè)計(jì)為具有可擴(kuò)展性、模塊化和可復(fù)用的特性,往往會(huì)導(dǎo)致分配的數(shù)量增多。抽象和“精確”都會(huì)導(dǎo)致性能下降。

  GC 友好的分配配置文件中將包含一些在開始執(zhí)行應(yīng)用程序時(shí)分配的對(duì)象,這些對(duì)象的生命周期與應(yīng)用程序一致,而其他對(duì)象的生命周期都比較短。存活時(shí)間較長的對(duì)象很少或不包含對(duì)存活時(shí)間較短的對(duì)象的引用。當(dāng)分配配置文件偏離此原則時(shí),GC 就必須努力工作以治理應(yīng)用程序的內(nèi)存。

  GC 不友好的分配配置文件中將包含一些可以存活到第 2 代但隨后就會(huì)死去的對(duì)象,或者將包含一些要分配到大對(duì)象堆的存活時(shí)間較短的對(duì)象。那些存活時(shí)間長得足以進(jìn)入第 2 代而隨后就會(huì)死去的對(duì)象,是治理開銷最大的對(duì)象。就像我在前面提到的,假如在執(zhí)行 GC 期間上一代中的對(duì)象包含對(duì)新一代對(duì)象的引用,也會(huì)增加回收的開銷。

  典型的實(shí)際分配配置文件介于上面提到的兩種分配配置文件之間。分配配置文件的一個(gè)重要度量標(biāo)準(zhǔn)是 CPU 花在 GC 上的時(shí)間占其總時(shí)間的百分比。您可以通過 .NET CLR Memory:% Time in GC 性能計(jì)數(shù)器獲得這一數(shù)字。假如此計(jì)數(shù)器的平均值大于 30%,則您可能需要對(duì)您的分配配置文件進(jìn)行一次仔細(xì)的檢查。這并不一定意味著您的分配配置文件有問題,在某些占用大量內(nèi)存的應(yīng)用程序中,GC 達(dá)到這種水平是必然的,也是正常的。當(dāng)您碰到性能問題時(shí),首先應(yīng)該檢查此計(jì)數(shù)器,它將立即顯示出您的分配配置文件是否出現(xiàn)了問題。

  提示:假如 .NET CLR Memory:% Time in GC 性能計(jì)數(shù)器指示您的應(yīng)用程序花在 GC 上的平均時(shí)間高于它的總時(shí)間的 30%,則表明您需要對(duì)您的分配配置文件進(jìn)行一次仔細(xì)的檢查。

  提示:GC 友好的應(yīng)用程序中包含的第 0 代對(duì)象遠(yuǎn)遠(yuǎn)多于第 2 代對(duì)象。此比率可以通過比較 NET CLR Memory:# Gen 0 Collections 和 NET CLR Memory:# Gen 2 Collections 性能計(jì)數(shù)器的結(jié)果來得出。 用于分析的API和CLR分析器

  CLR 中包含一個(gè)功能強(qiáng)大的用于分析的 API,第三方可用它來為托管應(yīng)用程序編寫自定義分析器。CLR 分析器是一種分配分析示例工具,由 CLR ProdUCt Team 編寫,但不提供技術(shù)支持,該分析器使用的就是這種用于分析的 API。開發(fā)人員可以使用 CLR 分析器查看其治理應(yīng)用程序的分配配置文件。

高性能托管應(yīng)用程序設(shè)計(jì)入門


圖 1:CLR 分析器主窗口

  CLR 分析器包括大量非常有用的分配配置文件視圖,包括:已分配類型的柱狀圖、分配和調(diào)用圖表、顯示不同代的 GC 和進(jìn)行這些回收之后托管堆的結(jié)果狀態(tài)的時(shí)間線,以及顯示各個(gè)方法分配和程序集加載情況的調(diào)用樹。

高性能托管應(yīng)用程序設(shè)計(jì)入門
圖 2:CLR 分析器分配圖表

  提示:有關(guān)如何使用 CLR 分析器的具體信息,請(qǐng)參閱 Zip 文件中包含的自述文件。
請(qǐng)注重,CLR 分析器具有高性能的特點(diǎn),可以顯著地改變應(yīng)用程序的性能特點(diǎn)。假如您在運(yùn)行應(yīng)用程序時(shí)也運(yùn)行了 CLR 分析器,因壓力而產(chǎn)生的問題很可能都會(huì)消失。

  集成服務(wù)器 GC

  有兩種不同的垃圾回收器可供 CLR 使用:工作站 GC 和服務(wù)器 GC。控制臺(tái)和 Windows 窗體應(yīng)用程序中集成了工作站 GC,而 asp.net 中集成了服務(wù)器 GC 。服務(wù)器 GC 針對(duì)吞吐量和多處理器的可擴(kuò)展性進(jìn)行了優(yōu)化。服務(wù)器 GC 在整個(gè)回收期間(包括標(biāo)記階段和清除階段)會(huì)暫停所有運(yùn)行托管代碼的線程,并且 GC 會(huì)在可供高優(yōu)先級(jí)的 CPU 專用線程中的進(jìn)程使用的所有 CPU 上并行執(zhí)行。假如線程在執(zhí)行 GC 期間運(yùn)行了本機(jī)代碼,那么只有當(dāng)本機(jī)調(diào)用返回時(shí)這些線程才會(huì)暫停。假如您要構(gòu)建的服務(wù)器應(yīng)用程序?qū)⒁诙嗵幚砥饔?jì)算機(jī)上運(yùn)行,強(qiáng)烈建議您使用服務(wù)器 GC。假如您的應(yīng)用程序不是由 ASP.NET 提供的,那么您就必須編寫一個(gè)本機(jī)應(yīng)用程序來顯式集成 CLR。

  工作站 GC 經(jīng)過優(yōu)化,其滯后時(shí)間非常短,非常適合客戶端應(yīng)用程序。沒有人會(huì)希望客戶端應(yīng)用程序在執(zhí)行 GC 期間出現(xiàn)明顯的停頓,這是因?yàn)榭蛻舳说男阅芡ǔ6疾皇峭ㄟ^原始吞吐量,而是通過反應(yīng)性能來測(cè)量的。工作站 GC 是并發(fā) GC,這意味著它會(huì)在托管代碼運(yùn)行的同時(shí)執(zhí)行標(biāo)記階段。工作站 GC 僅在需要執(zhí)行清除階段時(shí)才會(huì)暫停運(yùn)行托管代碼的線程。在工作站 GC 中,由于 GC 僅在一個(gè)線程上運(yùn)行,因而它只在一個(gè) CPU 上運(yùn)行。

  終結(jié) (Finalization)

  CLR 提供一種在釋放與類型實(shí)例關(guān)聯(lián)的內(nèi)存之前自動(dòng)進(jìn)行清除的機(jī)制。這一機(jī)制稱為終結(jié) (Finalization)。通常,終結(jié)用于釋放本機(jī)資源,在這種情況下,則釋放由對(duì)象使用的數(shù)據(jù)庫連接或操作系統(tǒng)句柄。

  終結(jié)是一個(gè)開銷很大的功能,而且它還會(huì)加大 GC 的壓力。GC 會(huì)跟蹤 Finalizable 隊(duì)列中需要執(zhí)行終結(jié)操作的對(duì)象。假如在回收期間,GC 發(fā)現(xiàn)了一個(gè)不再存活且需要終結(jié)的對(duì)象,它就會(huì)將該對(duì)象在 Finalizable 隊(duì)列中的條目移至 FReachable 隊(duì)列中。終結(jié)操作在一個(gè)稱為終結(jié)器線程 (Finalizer Thread) 的獨(dú)立線程中執(zhí)行。因?yàn)樵诮K結(jié)器的執(zhí)行過程中,可能需要用到該對(duì)象的所有狀態(tài),因此該對(duì)象或其指向的所有對(duì)象都會(huì)升級(jí)至下一代。與對(duì)象或?qū)ο髨D相關(guān)的內(nèi)存僅在執(zhí)行后續(xù)的 GC 時(shí)才會(huì)釋放。

  需要釋放的資源應(yīng)該包裝在一個(gè)盡可能小的可終結(jié)對(duì)象中,例如,假如您的類需要引用托管資源和非托管資源,那么您應(yīng)該在新的可終結(jié)類中包裝非托管資源,并使該類成為您的類的成員。父類不能是可終結(jié)類。這意味著只有包含非托管資源的類會(huì)被升級(jí)(假如您沒有在包含非托管資源的類中引用父類)。另外還要記住只有一個(gè)終結(jié)線程。假如有終結(jié)器導(dǎo)致此線程被阻塞,就不能調(diào)用后續(xù)的終結(jié)器,也不能釋放資源,而且您的應(yīng)用程序會(huì)導(dǎo)致資源泄漏。

  提示:應(yīng)該盡可能使終結(jié)器保持簡單,且永遠(yuǎn)不會(huì)阻塞。

  提示:僅將需要清除的非托管對(duì)象的包裝類設(shè)為可終結(jié)。

  可以將終結(jié)認(rèn)為是引用計(jì)數(shù)的一種替換形式。執(zhí)行引用計(jì)數(shù)的對(duì)象將跟蹤有多少其他對(duì)象對(duì)其進(jìn)行引用(這會(huì)導(dǎo)致一些非常有名的問題),以便在引用計(jì)數(shù)為零時(shí)釋放其資源。CLR 沒有實(shí)現(xiàn)引用計(jì)數(shù),因此它需要提供一種機(jī)制,以便在不存在對(duì)象的引用時(shí)自動(dòng)釋放資源。終結(jié)就是這種機(jī)制。通常,終結(jié)僅需要在要清除的對(duì)象的生命周期不明確的情況下執(zhí)行。 清理模式 (Dispose Pattern)

  當(dāng)對(duì)象的生命周期不明確時(shí),應(yīng)該盡快釋放與該對(duì)象關(guān)聯(lián)的非托管資源。這一過程稱為“清理”對(duì)象。清理模式通過 IDisposable 接口實(shí)現(xiàn)(盡管您自己實(shí)現(xiàn)也很輕易)。
假如您希望對(duì)類應(yīng)用終結(jié),例如,要使類實(shí)例可清理,就需要讓對(duì)象實(shí)現(xiàn) IDisposable 接口并執(zhí)行 Dispose 方法。使用 Dispose 方法您可以調(diào)用終結(jié)器中的同一段清除代碼,并通知 GC 不需要通過調(diào)用 GC.SuppressFinalization 方法來終結(jié)該對(duì)象。最好同時(shí)使用 Dispose 方法和終結(jié)器來調(diào)用通用終結(jié)函數(shù),這樣就只需要維護(hù)一份清除代碼。而且,假如對(duì)象的語義為:Close 方法比 Dispose 方法更符合邏輯,那么還應(yīng)實(shí)現(xiàn) Close 方法,在這種情況下,數(shù)據(jù)庫連接或套接字邏輯上都被“關(guān)閉”。Close 可以只是簡單地調(diào)用 Dispose 方法。

  使用終結(jié)器為類提供 Dispose 方法始終是一種很好的做法,因?yàn)橛肋h(yuǎn)沒有人能確切地知道使用類的方法,例如,是否可以明確知道它的生命周期。假如您使用的類實(shí)現(xiàn)了清理模式,而且您也確切地知道何時(shí)清理好對(duì)象,最好明確地調(diào)用 Dispose。

  提示:請(qǐng)為所有可終結(jié)的類提供 Dispose 方法。

  提示:請(qǐng)將終結(jié)操作隱藏在 Dispose 方法中。

  提示:請(qǐng)調(diào)用通用清除函數(shù)。

  提示:假如您使用的對(duì)象實(shí)現(xiàn)了 IDisposable,并且您知道已不再需要該對(duì)象,請(qǐng)調(diào)用 Dispose 方法。

  C# 提供了一種非常方便的自動(dòng)清理對(duì)象的方法。使用要害字 using 來標(biāo)記代碼塊,之后,將對(duì)大量可清理對(duì)象調(diào)用 Dispose。

  C# 的 using 要害字


using(DisposableType T)
{
//對(duì) T 執(zhí)行一些操作
}
//自動(dòng)調(diào)用 T.Dispose()
  弱引用注釋

  在堆棧中、寄存器中、其他對(duì)象上或某一其他 GC 根對(duì)象上,對(duì)對(duì)象的任何引用都會(huì)使得該對(duì)象在執(zhí)行 GC 期間保持存活。一般來說,這是一件好事,因?yàn)檫@通常都表示應(yīng)用程序不是借助該對(duì)象來執(zhí)行的。然而,有些時(shí)候您會(huì)需要引用某個(gè)對(duì)象,但又不想影響其生命周期。在這種情況下,CLR 為實(shí)現(xiàn)這一目的而提供了一種稱為“弱引用”的機(jī)制。任何強(qiáng)引用(例如,以對(duì)象為根的引用)都可以轉(zhuǎn)換成弱引用。例如,當(dāng)您需要?jiǎng)?chuàng)建可以遍歷數(shù)據(jù)結(jié)構(gòu)的外部游標(biāo)對(duì)象,但又不影響該對(duì)象的生命周期時(shí),可能需要使用弱引用。又例如,當(dāng)您需要?jiǎng)?chuàng)建一個(gè)存在內(nèi)存壓力時(shí)就會(huì)刷新的緩存時(shí)也可能會(huì)需要使用弱引用,例如,發(fā)生 GC 時(shí)。

  在 C# 中創(chuàng)建弱引用

MyRefType mrt = new MyRefType();
//...

//創(chuàng)建弱引用
WeakReference wr = new WeakReference(mrt);
mrt = null; //對(duì)象不再是根對(duì)象
//...

//對(duì)象是否已回收?
if(!wr.IsAlive)
{
//獲得該對(duì)象的強(qiáng)引用
mrt = wr.Target;
//對(duì)象為根對(duì)象而且可以再次使用
}
else
{
//重新創(chuàng)建該對(duì)象
mrt = new MyRefType();
}
  托管代碼和 CLR JIT

  托管程序集是托管代碼的分發(fā)單位,它由 Microsoft 中間語言(MSIL 或 IL)構(gòu)成,適用于所有的處理器。CLR 的實(shí)時(shí) (JIT) 功能可將 IL 編譯成優(yōu)化的本機(jī) X86 指令。JIT 是一種執(zhí)行優(yōu)化操作的編譯器,但是由于編譯是在軟件運(yùn)行時(shí)進(jìn)行的,并且僅當(dāng)?shù)谝淮握{(diào)用方法時(shí)才會(huì)進(jìn)行,因此進(jìn)行優(yōu)化的次數(shù)需要與執(zhí)行編譯所花費(fèi)的時(shí)間保持平衡。通常,這對(duì)于服務(wù)器應(yīng)用程序并不重要,因?yàn)閱?dòng)時(shí)間和響應(yīng)對(duì)于它們來說通常都不構(gòu)成問題;但對(duì)于客戶端應(yīng)用程序來說,卻十分重要。請(qǐng)注重,安裝時(shí)可以通過使用 NGEN.exe 執(zhí)行編譯來加快啟動(dòng)時(shí)間。

  許多由 JIT 執(zhí)行的優(yōu)化操作都沒有與其關(guān)聯(lián)的編程模式,例如,您無法對(duì)它們進(jìn)行顯式編碼,但是也有一些優(yōu)化操作具有關(guān)聯(lián)的編程模式。下一節(jié)將討論后者中的部分操作。

  提示:使用 NGEN.exe 實(shí)用程序在安裝時(shí)編譯應(yīng)用程序,可以加快客戶端應(yīng)用程序的啟動(dòng)時(shí)間。

  方法內(nèi)聯(lián)

  所有的方法調(diào)用都會(huì)帶來開銷。例如,需要將參數(shù)推入棧中或存儲(chǔ)在寄存器中,需要執(zhí)行的方法起頭 (prolog) 和結(jié)尾 (epilog) 等。只需要將被調(diào)用方法的方法主體移入調(diào)用方的主體,就可以避免某些方法的調(diào)用開銷。這一操作稱為方法內(nèi)聯(lián)。JIT 使用大量的探測(cè)方法來確定是否應(yīng)內(nèi)聯(lián)某個(gè)方法。下面是其中一些比較重要的探測(cè)方法的列表(請(qǐng)注重這并不是一個(gè)詳盡的列表):

   IL 超過 32 字節(jié)的方法不會(huì)內(nèi)聯(lián)。

   虛函數(shù)不會(huì)內(nèi)聯(lián)。

  包含復(fù)雜流程控制的函數(shù)不會(huì)內(nèi)聯(lián)。復(fù)雜流程控制是除 if/then/else 以外的任意流程控制,在這種情況下,為 switch 或 while。

  包含異常處理塊的方法不會(huì)內(nèi)聯(lián),但是引發(fā)異常的方法可以內(nèi)聯(lián)。

  假如某個(gè)方法的所有定參都為結(jié)構(gòu),則該方法不會(huì)內(nèi)聯(lián)。

  我會(huì)認(rèn)真考慮一下對(duì)這些探測(cè)方法進(jìn)行顯式編碼的問題,因?yàn)樵谝院蟮?JIT 版本中它們可能會(huì)有所變化。請(qǐng)不要為了確保方法可以內(nèi)聯(lián)而放棄方法的正確性。您也許已經(jīng)注重到了一個(gè)有趣的現(xiàn)象,C++ 中的要害字 inline 和 __inline 不能保證編譯器將一種方法內(nèi)聯(lián)(盡管 __forceinline 可以)。

  一般情況下,屬性的 Get 和 Set 方法都非常適合內(nèi)聯(lián),因?yàn)樗鼈冎饕糜诔跏蓟接袛?shù)據(jù)成員。

  提示:請(qǐng)不要為了試圖保證內(nèi)聯(lián)而放棄方法的正確性。

  去除范圍檢查

  托管代碼有許多優(yōu)點(diǎn),其中一項(xiàng)是可以自動(dòng)進(jìn)行范圍檢查。每次使用 array[index] 語義訪問數(shù)組時(shí),JIT 都會(huì)進(jìn)行檢查以確保索引在數(shù)組范圍中。在具有大量迭代和少量由每個(gè)迭代執(zhí)行的指令的循環(huán)環(huán)境中,范圍檢查的開銷可能會(huì)很大。在某些情況下,JIT 也可能會(huì)在檢測(cè)到這些范圍檢查不必要時(shí)將其從循環(huán)體中刪除,即僅在循環(huán)開始執(zhí)行之前對(duì)其進(jìn)行一次檢查。在 C# 中有一種編程模式,用來確保這些范圍檢查會(huì)被刪除:對(duì)“for”語句中數(shù)組的長度進(jìn)行的顯式測(cè)試。請(qǐng)注重,只要此模式中存在細(xì)微的偏差都會(huì)導(dǎo)致檢查無法去除,在這種情況下,需向索引中添加一個(gè)值。

  在 C# 中去除范圍檢查

//范圍檢查將被去除
for(int i = 0; i < myArray.Length; i++)
{
Console.WriteLine(myArray[i].ToString());
}

//范圍檢查將無法去除
for(int i = 0; i < myArray.Length + y; i++)
{
Console.WriteLine(myArray[i+x].ToString());
}
  搜索大型不規(guī)則數(shù)組時(shí),優(yōu)化操作非凡明顯,因?yàn)榇藭r(shí)將同時(shí)刪除內(nèi)循環(huán)和外循環(huán)范圍檢查。

  要求進(jìn)行變量使用情況跟蹤的優(yōu)化操作

  大量 JIT 編譯器優(yōu)化操作都要求 JIT 跟蹤定參和局部變量的使用情況,例如,它們?cè)诜椒ㄖ黧w中的最早以及最近一次使用的時(shí)間。在 CLR 1.0 和 1.1 版中,JIT 可以跟蹤使用情況的變量總數(shù)限制在 64 個(gè)之內(nèi)。例如“寄存操作”就是一個(gè)需要進(jìn)行使用情況跟蹤的優(yōu)化操作。寄存操作是指將變量存儲(chǔ)在處理器寄存器中,而不是棧框架中(例如,在內(nèi)存中)。與對(duì)棧框架中的變量進(jìn)行訪問的時(shí)間相比,對(duì)寄存變量的訪問要快得多,即便框架中的變量位于處理器的緩存中也一樣。只能對(duì) 64 個(gè)變量進(jìn)行寄存,所有其他變量都將推至棧中。除寄存操作以外,另外也有一些優(yōu)化操作需要進(jìn)行使用情況跟蹤。應(yīng)該將方法中的定參和局部參數(shù)的數(shù)量保持在 64 個(gè)以下,以確保實(shí)現(xiàn)最大數(shù)目的 JIT 優(yōu)化操作。請(qǐng)記住在以后的 CLR 版本中此數(shù)目可能會(huì)有所變化。

  提示:使方法保持簡短。要這樣做的原因有很多,包括方法內(nèi)聯(lián)、寄存操作和 JIT 持續(xù)時(shí)間的需要。

  其他 JIT 優(yōu)化操作

  JIT 編譯器還可以執(zhí)行大量其他的優(yōu)化操作:常量和副本傳播、循環(huán)不變式提升以及若干其他操作。需要用來實(shí)現(xiàn)優(yōu)化的編程模式都是免費(fèi)的,無需花錢購買。

  為什么我在 Visual Studio 中沒有看到這些優(yōu)化功能?

  當(dāng)您在 Visual Studio 中使用 Debug(調(diào)試)菜單或按下 F5 鍵啟動(dòng)應(yīng)用程序時(shí),無論您生成的是發(fā)行版還是調(diào)試版,所有的 JIT 優(yōu)化功能都將被禁用。當(dāng)托管應(yīng)用程序通過調(diào)試器啟動(dòng)時(shí),即使它不是該應(yīng)用程序的調(diào)試版本,JIT 也會(huì)發(fā)出非優(yōu)化的 x86 指令。假如您希望 JIT 發(fā)出優(yōu)化代碼,那么請(qǐng)從 Windows 資源治理器中啟動(dòng)該應(yīng)用程序,或者在 Visual Studio 中使用 CTRL+F5 組合鍵。假如希望查看優(yōu)化的反匯編程序,并將其與非優(yōu)化代碼進(jìn)行對(duì)比,則可以使用 cordbg.exe。

 提示:使用 cordbg.exe 可以查看 JIT 發(fā)出的優(yōu)化和非優(yōu)化代碼的反匯編程序。使用 cordbg.exe 啟動(dòng)該應(yīng)用程序后,可以通過鍵入以下代碼來設(shè)置 JIT 模式:


(cordbg) mode JitOptimizations 1
// JIT 將生成優(yōu)化的代碼

(cordbg) mode JitOptimizations 0
  JIT 將生成可調(diào)試(非優(yōu)化)代碼。
值類型

  CLR 可提供兩組不同的類型:引用類型和值類型。引用類型總是分配到托管堆中,并按引用傳遞(正如它的名稱所暗示的)。值類型分配到棧中或在堆中內(nèi)聯(lián)為對(duì)象的一部分,默認(rèn)情況下按值傳遞,不過您也可以按引用來傳遞它們。分配值類型時(shí),所需的開銷非常小,假設(shè)它們總是又小又簡單,當(dāng)它們作為參數(shù)進(jìn)行傳遞時(shí)開銷也會(huì)很小。正確使用值類型的一個(gè)很好的示例就是包含 x 和 y 坐標(biāo)的 Point 值類型。

  Point 值類型

struct Point
{
public int x;
public int y;

//
}
  值類型也可以視為對(duì)象,例如,可以在值類型上調(diào)用對(duì)象方法,它們可以轉(zhuǎn)換為對(duì)象,或傳遞到需要使用對(duì)象的位置。無論使用什么方法,只要將值類型轉(zhuǎn)換為引用類型,都需要經(jīng)過裝箱 (Boxing) 處理。對(duì)值類型進(jìn)行裝箱處理時(shí),會(huì)在托管堆中分配一個(gè)新對(duì)象,而值則復(fù)制到此新對(duì)象中。這項(xiàng)操作將占用很大的系統(tǒng)開銷,還可能會(huì)降低或完全消除通過使用值類型而獲得的性能。將裝箱的類型隱式或顯式轉(zhuǎn)換回值類型的過程稱為取消裝箱 (Unboxed)。

  裝箱/取消裝箱值類型

  C#:

int BoxUnboxValueType()
{
int i = 10;
object o = (object)i; //i 被裝箱
return (int)o + 3; //i 被取消裝箱
}
  MSIL:

.method private hidebysig instance int32
BoxUnboxValueType() cil managed
{
// 代碼大小 20 (0x14)
.maxstack 2
.locals init (int32 V_0,
object V_1)
IL_0000: ldc.i4.s 10
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: box [mscorlib]System.Int32
IL_0009: stloc.1
IL_000a: ldloc.1
IL_000b: unbox [mscorlib]System.Int32
IL_0010: ldind.i4
IL_0011: ldc.i4.3
IL_0012: add
IL_0013: ret
} // 方法 Class1::BoxUnboxValueType 結(jié)束
  假如實(shí)現(xiàn)自定義值類型(C# 中的結(jié)構(gòu)),則應(yīng)該考慮覆蓋 ToString 方法。假如不覆蓋此方法,那么對(duì)值類型上的 ToString 的調(diào)用將導(dǎo)致該類型被裝箱。對(duì)于從 System.Object 繼續(xù)的其他方法也是如此。在這種情況下,請(qǐng)使用 Equals 來進(jìn)行覆蓋,盡管 ToString 很可能是最常用的調(diào)用方法。假如您希望了解值類型是否被裝箱以及何時(shí)裝箱,可以使用 ildasm.exe 實(shí)用程序在 MSIL 中查找 box 指令(如上所述)。

  覆蓋 C# 中的 ToString() 方法以防止裝箱

struct Point
{
public int x;
public int y;

//此操作將防止在調(diào)用 ToString 時(shí)對(duì)類型進(jìn)行裝箱
public override string ToString()
{
return x.ToString() + "," + y.ToString();
}
}
  請(qǐng)注重,在創(chuàng)建集合(例如,浮點(diǎn)數(shù)組列表)時(shí),添加到集合中的每一項(xiàng)都將進(jìn)行裝箱。您應(yīng)該考慮使用數(shù)組或?yàn)橹殿愋蛣?chuàng)建自定義集合類。

  使用 C# 中的集合類時(shí)進(jìn)行隱式裝箱

ArrayList al = new ArrayList();
al.Add(42.0F); //由于 Add() 接受對(duì)象因此進(jìn)行隱式裝箱
float f = (float)al[0]; //取消裝箱
  異常處理

  通常,錯(cuò)誤條件都將作為常規(guī)流程控制使用。在此情況下,假如試圖通過編程將用戶添加到 Active Directory 實(shí)例中,則只能試著添加該用戶,假如系統(tǒng)返回 E_ADS_OBJECT_EXISTS HRESULT,則說明它們已經(jīng)存在于該目錄中。此外,您也可以通過搜索目錄查找該用戶,假如搜索失敗則只需添加該用戶。

  按照常規(guī)流程控制使用錯(cuò)誤,在 CLR 環(huán)境中會(huì)降低性能。CLR 中的錯(cuò)誤處理是借助結(jié)構(gòu)化異常處理實(shí)現(xiàn)的。引發(fā)異常之前,托管異常的開銷非常小。在 CLR 中,引發(fā)異常時(shí),需要使用堆棧遍歷為已引發(fā)的異常找到相應(yīng)的異常處理程序。堆棧遍歷是一種開銷較大的操作。正如它的名稱所表示的,異常應(yīng)該用于異常或意外的情況。

  提示:對(duì)于以性能為中心的方法,請(qǐng)返回預(yù)期結(jié)果的枚舉結(jié)果,而不是引發(fā)異常。

  提示:有多種 .NET CLR 異常性能計(jì)數(shù)器都可以通知您在應(yīng)用程序中引發(fā)了多少異常。

  提示:假如您使用 VB.NET 來使用除 On Error Goto 以外的異常,錯(cuò)誤對(duì)象就是不必要的開銷。 線程和同步

  CLR 提供豐富的線程和同步功能,包括創(chuàng)建自己的線程、線程池和各種同步原語的能力。在充分利用 CLR 中支持的線程之前,應(yīng)該仔細(xì)考慮一下線程的用法。請(qǐng)記住,添加線程實(shí)際上會(huì)降低吞吐量,而不會(huì)增加吞吐量,但它肯定會(huì)增加內(nèi)存的利用率。
在將要在多處理器計(jì)算機(jī)上運(yùn)行的服務(wù)器應(yīng)用程序中,采用并行操作(盡管這取決于將要執(zhí)行多少鎖爭用,例如,序列化執(zhí)行方式)來添加線程可以顯著地提高吞吐量;在客戶端應(yīng)用程序中,添加顯示活動(dòng)和/或進(jìn)度的線程可以提高反應(yīng)性能(低吞吐量開銷)。

  假如應(yīng)用程序中的線程不是專門用于特定任務(wù)的線程,或者關(guān)聯(lián)有非凡的狀態(tài),則應(yīng)該考慮使用線程池。假如您過去使用過 Win32 線程池,那對(duì) CLR 線程池也一定會(huì)比較熟悉。每個(gè)托管進(jìn)程僅存在一個(gè)線程池實(shí)例。線程池可以智能地識(shí)別出它所創(chuàng)建的線程數(shù)量,并且會(huì)根據(jù)計(jì)算機(jī)上的負(fù)載對(duì)自身進(jìn)行調(diào)節(jié)。

  要討論線程處理,就必須討論同步。所有由多線程為應(yīng)用程序帶來的吞吐量收益都可能會(huì)因?yàn)橥竭壿嬀帉懖徽_而全部喪失。鎖定的粒度會(huì)大大影響應(yīng)用程序的總體吞吐量,這是因?yàn)閯?chuàng)建和治理鎖會(huì)帶來系統(tǒng)開銷,并且鎖定很可能會(huì)序列化執(zhí)行步驟所致。我將使用在樹中添加節(jié)點(diǎn)的示例來說明這一觀點(diǎn)。例如,假如樹將成為共享的數(shù)據(jù)結(jié)構(gòu),則多線程需要在執(zhí)行應(yīng)用程序期間對(duì)其進(jìn)行訪問,而且您需要對(duì)該樹進(jìn)行同步訪問。您可以選擇在添加節(jié)點(diǎn)的同時(shí)鎖定整個(gè)樹,這意味著您只會(huì)在單一鎖定時(shí)帶來開銷,但其他試圖訪問該樹的線程都可能會(huì)因此而阻塞。這將是一個(gè)粗粒度的鎖定示例。或者,您可以在遍歷該樹時(shí)鎖定每個(gè)節(jié)點(diǎn),這意味著您會(huì)在每個(gè)節(jié)點(diǎn)上創(chuàng)建鎖時(shí)帶來開銷,但是其他線程不會(huì)因此而阻塞,除非它們?cè)噲D訪問您已經(jīng)鎖定的特定節(jié)點(diǎn)。這是細(xì)粒度的鎖定示例。僅為要對(duì)其進(jìn)行操作的子樹添加鎖也許是更合適的鎖定粒度。請(qǐng)注重,在本示例中,您可能會(huì)使用共享鎖 (RWLock),因?yàn)橹挥羞@樣才能讓多個(gè)讀者同時(shí)進(jìn)行訪問。

  執(zhí)行同步操作時(shí)最簡單有效的方法就是使用 System.Threading.Interlocked 類。Interlocked 類提供大量低級(jí)別的原子操作:Increment、Decrement、Exchange 和 CompareExchange。

  在 C# 中使用 System.Threading.Interlocked 類

using System.Threading;
//...
public class MyClass
{
void MyClass() //構(gòu)造函數(shù)
{
//以原子方式遞增全局實(shí)例計(jì)數(shù)器的值
Interlocked.Increment(ref MyClassInstanceCounter);
}

~MyClass() //終結(jié)器
{
//以原子方式遞減全局實(shí)例計(jì)數(shù)器的值
Interlocked.Decrement(ref MyClassInstanceCounter);
//...
}
//...
}
  最常用的同步機(jī)制可能是監(jiān)測(cè)器 (Monitor) 或臨界區(qū) (Critical Section)。監(jiān)測(cè)器鎖可直接使用,也可以借助 C# 中的 lock 要害字來使用。對(duì)于給定的對(duì)象來說,lock 要害字會(huì)對(duì)特定的代碼塊進(jìn)行同步訪問。從性能的角度來說,假如監(jiān)測(cè)器鎖的爭用率較低,則系統(tǒng)開銷相對(duì)較小;但是假如其爭用率較高,系統(tǒng)開銷也會(huì)相對(duì)較大。

  C# lock 要害字

//線程試圖獲取

//和代碼塊
lock(mySharedObject)
{
//假如塊中包含鎖,
//線程只能執(zhí)行此塊中的代碼
}//線程釋放鎖
  RWLock 提供的是共享鎖定機(jī)制:例如,該鎖可以在“讀者”之間共享,但是不能在“作者”之間共享。在這種鎖也適用的情況下,使用 RWLock 可以比使用監(jiān)測(cè)器帶來更好的吞吐量,它每次只答應(yīng)一位讀者或作者獲得該鎖。System.Threading 命名空間也包括 Mutex 類。Mutex 是一種同步原語,可用來進(jìn)行跨進(jìn)程的同步操作。請(qǐng)注重,它比臨界區(qū)的開銷要大很多,僅當(dāng)需要進(jìn)行跨進(jìn)程的同步操作時(shí)才應(yīng)使用它。

  反射

  反射是由 CLR 提供的一種機(jī)制,用于在運(yùn)行時(shí)通過編程方式獲得類型信息。反射在很大程度上取決于嵌入在托管程序集中的元數(shù)據(jù)。許多反射 API 都要求搜索并分析元數(shù)據(jù),這些操作的開銷都很大。

  這些反射 API 可以分為三個(gè)性能區(qū)間:類型比較、成員枚舉和成員調(diào)用。這些區(qū)間的系統(tǒng)開銷一直在變大。類型比較操作,例如 C# 中的 typeof、GetType、is、IsInstanceOfType 等,都是開銷最小的反射 API,盡管它們的實(shí)際開銷一點(diǎn)也不小。成員枚舉操作可以通過編程方式對(duì)類的方法、屬性、字段、事件、構(gòu)造函數(shù)等進(jìn)行檢查。例如,可能會(huì)在設(shè)計(jì)時(shí)的方案中使用這一類的成員枚舉操作,在這種情況下,此操作將枚舉 Visual Studio 中的 Property Browser(屬性瀏覽器)的 Customs Web Controls(自定義 Web 控件)的屬性。那些用于動(dòng)態(tài)調(diào)用類成員或動(dòng)態(tài)發(fā)出 JIT 并執(zhí)行某個(gè)方法的的反射 API 是開銷最大的反射 API。當(dāng)然,假如需要?jiǎng)討B(tài)加載程序集、類型實(shí)例化以及方法調(diào)用,還存在一種后期綁定方案,但是這種松散的耦合關(guān)系需要進(jìn)行明確的性能權(quán)衡。一般情況下,應(yīng)該在對(duì)性能影響很大的代碼路徑中避免使用反射 API。請(qǐng)注重,盡管您沒有直接使用反射,但是您使用的 API 可能會(huì)使用它。因此,也要注重是否間接使用了反射 API。

  后期綁定

  后期綁定調(diào)用是一種在內(nèi)部使用反射的功能。Visual Basic.NET 和 JScript.NET 都支持后期綁定調(diào)用。例如,使用變量之前您不必進(jìn)行聲明。后期綁定對(duì)象實(shí)際上是類型對(duì)象,可以在運(yùn)行時(shí)使用反射將該對(duì)象轉(zhuǎn)換為正確的類型。
后期綁定調(diào)用比直接調(diào)用要慢幾個(gè)數(shù)量級(jí)。除非您確實(shí)需要后期綁定行為,否則應(yīng)該避免在性能要害代碼路徑中使用它。

  提示:假如您正在使用 VB.NET,且并不一定需要后期綁定,您可以在源文件的頂部包含 Option EXPlicit On 和 Option Strict On 以便通知編譯器拒絕后期綁定。這些選項(xiàng)將強(qiáng)制您進(jìn)行聲明,并要求您設(shè)置變量類型并關(guān)閉隱式轉(zhuǎn)換。

  安全性

  安全性是必要的而且也是主要的 CLR 的組成部分,使用它時(shí)會(huì)降低性能。當(dāng)代碼為 Fully Trusted(完全信任)且安全策略為默認(rèn)設(shè)置時(shí),安全性對(duì)應(yīng)用程序的吞吐量和啟動(dòng)時(shí)間的影響會(huì)很小。對(duì)代碼持不完全信任態(tài)度(例如,來自 Internet 或 Intranet 區(qū)域的代碼)或縮小 MyComputer Grant Set 都將增加安全性的性能開銷。

  COM 互操作和平臺(tái)調(diào)用

  COM 互操作和平臺(tái)調(diào)用會(huì)以幾乎透明的方式為托管代碼提供本機(jī) API,通常調(diào)用大多數(shù)本機(jī) API 時(shí)都不需要任何非凡代碼,但是可能需要使用鼠標(biāo)進(jìn)行多次單擊。正如您所預(yù)計(jì)的,從托管代碼中調(diào)用本機(jī)代碼會(huì)帶來開銷,反之亦然。這筆開銷由兩部分組成:一部分是固定開銷,此開銷與在本機(jī)代碼和托管代碼之間進(jìn)行的轉(zhuǎn)換有關(guān);另一部分是可變開銷,此開銷與那些可能要用到的參數(shù)封送和返回值有關(guān)。COM 互操作和平臺(tái)調(diào)用的固定開銷在開銷中占的比例較小:通常不超過 50 條指令。在各托管類型之間進(jìn)行封送處理的開銷取決于它們?cè)谶吔鐑蓚?cè)的表示形式的相似程度。需要進(jìn)行大量轉(zhuǎn)換的類型開銷相對(duì)較大。例如,CLR 中的所有字符串都為 Unicode 字符串。假如要通過平臺(tái)調(diào)用需要 ANSI 字符數(shù)組的 Win32 API,則必須縮小該字符串中的每個(gè)字符。但是,假如是將托管的整數(shù)數(shù)組傳遞到需要本機(jī)整數(shù)數(shù)組的類型中時(shí),就不需要進(jìn)行封送處理。

  由于存在與調(diào)用本機(jī)代碼相關(guān)的性能開銷,因此您應(yīng)該確保該開銷是合理的開銷。假如您打算進(jìn)行本機(jī)調(diào)用,請(qǐng)確保本機(jī)調(diào)用所做的工作使得因執(zhí)行此調(diào)用而產(chǎn)生的性能開銷劃算,即盡量使方法“小而精”而非“大而全”。測(cè)量本機(jī)調(diào)用開銷的一種好方法是測(cè)量不接受任何參數(shù)也不具備任何返回值的本機(jī)方法的性能,然后再測(cè)量您希望調(diào)用的本機(jī)方法的性能。它們之間的差異即封送處理的開銷。

  提示:應(yīng)創(chuàng)建“小而精”的 COM 互操作和平臺(tái)調(diào)用,而不是“大而全”的調(diào)用,并確保調(diào)用的開銷對(duì)于調(diào)用的工作量是劃算的。

  請(qǐng)注重,不存在與托管線程相關(guān)的線程模式。當(dāng)您打算進(jìn)行 COM 互操作調(diào)用時(shí),需要確保已將執(zhí)行調(diào)用的線程初始化為正確的 COM 線程模式。此操作通常是使用 MTAThreadAttribute 和 STAThreadAttribute 來實(shí)現(xiàn)的(盡管也可以通過編程來實(shí)現(xiàn))。

  性能計(jì)數(shù)器

  有大量的 Windows 性能計(jì)數(shù)器可供 .NET CLR 使用。當(dāng)開發(fā)人員首次診斷性能問題,或試圖識(shí)別托管應(yīng)用程序的性能特點(diǎn)時(shí),這些性能計(jì)數(shù)器就是他們可以選擇的武器。我已經(jīng)簡要介紹了幾個(gè)與內(nèi)存治理和異常有關(guān)的性能計(jì)數(shù)器。在 CLR 和 .NET Framework 中,性能計(jì)數(shù)器幾乎無處不在。通常,這些性能計(jì)數(shù)器都可以使用且對(duì)系統(tǒng)無害,它們的開銷較低,而且不會(huì)改變應(yīng)用程序的性能特征。

  其他工具

  除了性能計(jì)數(shù)器和 CLR 分析器以外,您還需要使用常規(guī)的分析器來確定應(yīng)用程序中的哪些方法花費(fèi)的時(shí)間最多,且最常被調(diào)用。這些方法將是您需要最先進(jìn)行優(yōu)化的方法。有多種支持托管代碼的商用分析器可供使用,包括 Compuware 的 DevPartner Studio Professional Edition 7.0 和 Intel? 的 VTune? Performance Analyzer 7.0。Compuware 還生產(chǎn)一種免費(fèi)的托管代碼分析器,名為 DevPartner Profiler Community Edition。

  小結(jié)

  本文只是從性能的角度初步介紹了 CLR 和 .NET Framework。在 CLR 和 .NET Framework 中,還有許多別的方面也會(huì)對(duì)應(yīng)用程序的性能產(chǎn)生影響。我最希望告訴各位開發(fā)人員的是:請(qǐng)不要對(duì)您的應(yīng)用程序的目標(biāo)平臺(tái)以及您正在使用的 API 的性能做任何的假設(shè)。請(qǐng)測(cè)量它們!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 和林格尔县| 木里| 和硕县| 睢宁县| 隆回县| 策勒县| 财经| 眉山市| 烟台市| 文安县| 蕲春县| 榕江县| 若尔盖县| 枣强县| 兴安县| 汕尾市| 砀山县| 龙胜| 长寿区| 陆川县| 禹城市| 时尚| 棋牌| 惠州市| 宝鸡市| 太原市| 阳朔县| 十堰市| 滦南县| 绥芬河市| 穆棱市| 东平县| 明溪县| 扎囊县| 临桂县| 连山| 颍上县| 阿坝| 白银市| 乐陵市| 许昌县|