ハむパフォヌマンス.NET

芪愛なる読者の皆さん、こんにちは

少し前に、「 High-Performance .NET codeを曞く 」ずいう本の䜜業を開始したした。この本はただロシア語に翻蚳されおいたせんが、たもなく1幎になるでしょう。



もちろん、そのような本がすでに匕甚されおいるこずは驚きではありたせんでしたが、尊敬されおいる著者ベン・ワト゜ンは、章の1぀に基づいおCodeproject Webサむトに蚘事党䜓を投皿しさえしたした 。 残念ながら、この資料の量はhabropublishingには倧きすぎたすが、本の資料を評䟡できるように蚘事の最初の郚分を翻蚳するこずにしたした。 調査を読んで参加しおください。 さらに、2番目の郚分を翻蚳するこずをお勧めしたす-コメントを蚘入しお、私たちはあなたの垌望を考慮に入れようずしたす。


コンテキスト

この蚘事は、私の著曞 『High-Performance .NET Codeの䜜成』の第5章に基づいおいたす。

はじめに

この蚘事では、コヌドずタむプデザむンを蚘述する䞀般的な原則に぀いお説明したす。 .NETプラットフォヌムでは、さたざたなシナリオを実装する機䌚があり、それらのいく぀かがせいぜいパフォヌマンスに圱響を䞎えない堎合、それを深刻に台無しにするものがありたす。 特定のシナリオは良いこずも悪いこずもありたせんが、それが䜕であるかだけです。 各状況に最適な゜リュヌションを遞択する必芁がありたす。

この蚘事の根底にある䞀般原則を定匏化しようずするず、次のようになりたす。

深いパフォヌマンスの最適化は、倚くの堎合、゜フトりェアの抜象化を壊したす。

これは、非垞に高いパフォヌマンスを達成しようずする堎合、すべおのレベルで実装の詳现を理解する必芁があり、堎合によっおはこれらの埮劙さを詊しおみる必芁があるこずを意味したす。 これらの詳现の倚くは、この蚘事で説明されおいたす。

クラスず構造の比范

クラスむンスタンスは垞にヒヌプ䞊に割り圓おられ、これらのむンスタンスぞのアクセスはポむンタヌの逆参照によっお行われたす。 ポむンタヌのコピヌ4たたは8バむトであるため、それらを転送するのは安䟡です。 ただし、オブゞェクトにはいく぀かの固定オヌバヌヘッドもありたす。32ビットプロセスでは8バむト、64ビットプロセスでは16バむトです。 これらのコストには、メ゜ッドテヌブルぞのポむンタず同期ブロックフィヌルドが含たれたす。 フィヌルドなしでオブゞェクトを䜜成し、デバッガで衚瀺するず、実際にはそのサむズは8バむトではなく12バむトであるこずがわかりたす。 64ビットプロセスの堎合、オブゞェクトのサむズは24バむトになりたす。 実際には、最小サむズはメモリブロックのアラむメントに䟝存したす。 幞いなこずに、これらの「䜙分な」4バむトはフィヌルドによっお䜿甚されたす。

構造䜓構造はオヌバヌヘッドを䌎わず、䜿甚するメモリのサむズはそのすべおのフィヌルドのサむズの合蚈です。 構造䜓がメ゜ッド内でロヌカル倉数ずしお宣蚀されおいる堎合、 構造䜓はスタックに割り圓おられたす。 構造䜓がクラスの䞀郚ずしお宣蚀されおいる堎合、 構造 䜓メモリは、このクラスが占有するメモリフラグメントの䞀郚になりたすしたがっお、ヒヌプ䞊にありたす。 構造䜓をメ゜ッドに枡すず、 構造䜓はバむトごずに順番にコピヌされたす。 ヒヌプ䞊にないため、構造䜓を割り圓おおもガベヌゞコレクションは開始されたせん。

したがっお、ここで劥協する必芁がありたす。 構造の最倧サむズに関するさたざたな掚奚事項に出くわすかもしれたせんが、私は特定の数字に執着したせん。 原則ずしお、 構造䜓のサむズを非垞に小さく保぀こずをお勧めしたす 。特にこの構造䜓を前埌に枡す堎合は、構造䜓を参照枡しするこずもできるため、サむズに倧きな問題は生じたせん。 この手法が有甚かどうかを自信を持っお刀断する唯䞀の方法は、䜿甚パタヌンを泚意深く芋お、プロファむリングを実行するこずです。

状況によっおは、効果は劇的に異なる堎合がありたす。 単䞀のオブゞェクトのコストの䟡倀はごくわずかであるように芋えたすが、オブゞェクトの配列を怜蚎し、構造の配列ず比范しおみおください。 デヌタ構造に16バむトのデヌタが含たれ、配列の長さが1,000,000であり、32ビットシステムで䜜業するずしたす。

オブゞェクトの配列の堎合、合蚈スペヌス消費量は次のずおりです。

12バむトの配列オヌバヌヘッド+
ポむンタヌサむズ4バむト×1,000,000+
オヌバヌヘッド8バむト+ 16バむトのデヌタ×1,000,000
= 28 MB

構造䜓の配列では、根本的に異なる結果が埗られたす。

12バむトの配列オヌバヌヘッド+
16バむトのデヌタ×1,000,000
= 16 MB

64ビットプロセスの堎合、オブゞェクトの配列は40 MB以䞊を占有したすが、 構造䜓配列は16 MBしか必芁ずしたせん。

ご芧のずおり、 構造䜓配列では、オブゞェクトの配列よりも同じ量のデヌタがメモリを消費したせん。 オブゞェクトの䜿甚に関連するコストに加えお、メモリ負荷が高くなるこずで説明される、より集䞭的なガベヌゞコレクションも行われたす。

スペヌスの䜿甚に加えお、プロセッサヌの効率の問題もありたす。 プロセッサにはいく぀かのレベルのキャッシュがありたす。 プロセッサに最も近いものは非垞に小さいですが、非垞に高速に動䜜し、シヌケンシャルアクセス甚に最適化されおいたす。

構造䜓配列には、メモリ内に順番に配眮された倚くの倀が含たれたす。 構造䜓配列内の芁玠ぞのアクセスは非垞に簡単です。 正しい゚ントリが芋぀かるず、すでに正しい倀がありたす。 したがっお、倧きな配列を反埩凊理する堎合、アクセス速床に倧きな違いが生じる可胜性がありたす。 倀が既にプロセッサキャッシュにある堎合、RAMにある堎合よりも1桁速くアクセスできたす。

オブゞェクト配列の芁玠にアクセスするには、配列のメモリにアクセスし、この芁玠ぞのポむンタを間接参照する必芁がありたす。この芁玠はヒヌプ内のどこにでも配眮できたす。 オブゞェクト配列の列挙には、远加のポむンタヌの逆参照、ヒヌプに沿った「ゞャンプ」、およびプロセッサキャッシュの比范的頻繁な空化が含たれ、必芁なデヌタが無駄になる可胜性がありたす。

倚くの堎合、プロセッサレベルずメモリの䞡方でこのようなコストが発生しないこずが、構造を優先する䞻な理由です。 この手法を賢明に䜿甚するず、メモリの局所性が向䞊するため、パフォヌマンスが倧幅に向䞊したす。

構造は垞に倀によっおコピヌされるため、䞍泚意により興味深い䜍眮に移動する可胜性がありたす。 たずえば、次のコヌドぱラヌ付きで蚘述されおおり、コンパむルされたせん。

struct Point { public int x; public int y; } public static void Main() { List<Point> points = new List<Point>(); points.Add(new Point() { x = 1, y = 2 }); points[0].x = 3; } 


問題は、リスト内の既存のポむントを倉曎しようずしおいる最埌の行で発生したす。 ポむント[0 ]を呌び出すず、元の倀のコピヌが返され、他のどこにも保存されないため、これは䞍可胜です。 Pointを倉曎する正しい方法

 Point p = points[0]; px = 3; points[0] = p; 


ただし、構造を倉曎できないようにするために、さらに厳しいポリシヌを実装するこずをお勧めしたす。 構造を䜜成するず、そのような構造は新しい倀を取埗できなくなりたす。 この堎合、䞊蚘の状況は原則的に䞍可胜になり、構造に関するすべおの䜜業が簡玠化されたす。

前述のように、構造はコンパクトであるため、コピヌに倚くの時間を費やす必芁はありたせんが、倧きな構造を䜿甚する必芁がある堎合もありたす。 商業プロセスに関する倚くの詳现が远跡されるオブゞェクトを考えおください-䟋えば、倚くのタむムスタンプが蚭定されたす。

 class Order { public DateTime ReceivedTime {get;set;} public DateTime AcknowledgeTime {get;set;} public DateTime ProcessBeginTime {get;set;} public DateTime WarehouseReceiveTime {get;set;} public DateTime WarehouseRunnerReceiveTime {get;set;} public DateTime WarehouseRunnerCompletionTime {get;set;} public DateTime PackingBeginTime {get;set;} public DateTime PackingEndTime {get;set;} public DateTime LabelPrintTime {get;set;} public DateTime CarrierNotifyTime {get;set;} public DateTime ProcessEndTime {get;set;} public DateTime EmailSentToCustomerTime {get;set;} public DateTime CarrerPickupTime {get;set;} //    ... } 


コヌドを簡玠化するには、これらのラベルをそれぞれ独自のサブ構造で遞択するず䟿利です。これらのラベルは、次のようにOrderクラスのコヌドから匕き続き利甚できたす。

 Order order = new Order(); Order.Times.ReceivedTime = DateTime.UtcNow; 


これらの䞋䜍構造はすべお、別のクラスに移動できたす。

 class OrderTimes { public DateTime ReceivedTime {get;set;} public DateTime AcknowledgeTime {get;set;} public DateTime ProcessBeginTime {get;set;} public DateTime WarehouseReceiveTime {get;set;} public DateTime WarehouseRunnerReceiveTime {get;set;} public DateTime WarehouseRunnerCompletionTime {get;set;} public DateTime PackingBeginTime {get;set;} public DateTime PackingEndTime {get;set;} public DateTime LabelPrintTime {get;set;} public DateTime CarrierNotifyTime {get;set;} public DateTime ProcessEndTime {get;set;} public DateTime EmailSentToCustomerTime {get;set;} public DateTime CarrerPickupTime {get;set;} } class Order { public OrderTimes Times; } 


ただし、これにより、各Orderオブゞェクトに12たたは24バむトのコストが远加で発生したす。

OrderTimesオブゞェクト党䜓をさたざたなメ゜ッドに枡す必芁がある堎合、そのようなオヌバヌヘッドはおそらく正圓化されたすが、なぜOrderオブゞェクト党䜓ぞの参照を枡さないのですか 同時に数千のOrderオブゞェクトを凊理するず、ガベヌゞコレクションが倧幅に増加したす。 さらに、远加の逆参照操䜜がメモリに栌玍されたす。

OrderTimes構造を䜜成しおください。 Orderオブゞェクトのプロパティたずえば、 order.Times.ReceivedTime を䜿甚しおOrderTimes構造の個々のプロパティにアクセスしおも、構造のコピヌは行われたせん.NETでは、この可胜性の高いシナリオは特別に最適化されおいたす。 したがっお、 OrderTimesの構造は、ここに䞋䜍構造がないかのように、基本的にOrderクラスに割り圓おられたメモリの䞀郚です。 コヌド自䜓もより正確になりたす。

この手法は䞍倉構造の原則に違反したせんが、ここでの秘trickは次のずおりです。OrderTimes構造のフィヌルドは、Orderオブゞェクトのフィヌルドであるかのように凊理したす。 OrderTimes構造を゚ンティティ党䜓ずしお枡す必芁がない堎合、提案されるメカニズムは玔粋に組織的なものです。

構造䜓のEqualsおよびGetHashCodeメ゜ッドのオヌバヌラむド

構造を䜿甚する堎合、 Equalsメ゜ッドずGetHashCodeメ゜ッドをオヌバヌラむドするこずが非垞に重芁です。 これが行われない堎合、デフォルトバヌゞョンが取埗されたすが、これは決しお高性胜に貢献したせん。 これがどれほど悪いかを評䟡するには、䞭間蚀語ビュヌアヌを開き、 ValueType.Equalsメ゜ッドのコヌドを芋おください。 構造のすべおのフィヌルドの反射に関連付けられおいたす。 ただし、これはバむナリ互換タむプの最適化です。 バむナリ互換blittableは、マネヌゞコヌドずアンマネヌゞコヌドの䞡方でメモリ内で同じ衚珟を持぀タむプです。 これらには、プリミティブ数倀型たずえば、 Int32 、 UInt64が含たれたすが、プリミティブではないDecimalは含たれたせんおよびIntPtr / UIntPtrのみが含たれたす。 構造がバむナリ互換型のみで構成されおいる堎合、Equals実装は、構造党䜓でメモリのバむト比范を実際に実行できたす。 そのようなあいたいさを避け、独自のEqualsメ゜ッドを実装しおください。

Equalsその他のオブゞェクトを単玔にオヌバヌラむドする堎合、このメ゜ッドは倀型の倉換ずパッケヌゞ化に関連付けられおいるため、䟝然ずしお䞍圓に䜎いパフォヌマンスが埗られたす。 代わりに、 EqualsT otherを実装したす Tは構造䜓のタむプです。 IEquatableむンタヌフェむスはこのために蚭蚈されおおり、すべおの構造䜓が実装する必芁がありたす。 コンパむラは、可胜であれば、より匷く型付けされたバヌゞョンを垞に優先したす。 䟋を考えおみたしょう

 struct Vector : IEquatable<Vector> { public int X { get; set; } public int Y { get; set; } public int Z { get; set; } public int Magnitude { get; set; } public override bool Equals(object obj) { if (obj == null) { return false; } if (obj.GetType() != this.GetType()) { return false; } return this.Equals((Vector)obj); } public bool Equals(Vector other) { return this.X == other.X && this.Y == other.Y && this.Z == other.Z && this.Magnitude == other.Magnitude; } public override int GetHashCode() { return X ^ Y ^ Z ^ Magnitude; } } 


型がIEquatableむンタヌフェむスを実装しおいる堎合、汎甚.NETコレクションはそれを怜出し、より効率的な怜玢ず䞊べ替えに䜿甚したす。

たた、倀のタむプに== and=挔算子を䜿甚し、 既存のEqualsTメ゜ッドを呌び出すように匷制するこずもできたす。

構造を比范したりコレクションに入れたりしない堎合でも、これらのメ゜ッドを実装するこずをお勧めしたす。 それらが将来どのように䜿甚されるかを掚枬するこずは決しおありたせん。たた、メ゜ッドの䜜成には数分しかかからず、䞭間蚀語のバむトを読み取りたす。

クラスのEqualsメ゜ッドずGetHashCodeメ゜ッドをオヌバヌラむドするこずはそれほど重芁ではありたせん。この堎合、オブゞェクトぞの参照に基づいお同等性を蚈算するだけだからです。 コヌドにこれらのメ゜ッドの暙準実装が十分にあるず思われる堎合は、倉曎しないでください。

仮想メ゜ッドずシヌルドクラス

デフォルトでは、「䞇が䞀に備えお」メ゜ッドを仮想化しないでください。 ただし、プログラムの䞀貫した蚭蚈のために仮想メ゜ッドが必芁な堎合は、おそらく削陀しお無理をしないでください。

メ゜ッドを仮想化するず、動的コンパむラヌ偎からのいく぀かの最適化、特にメ゜ッドの埋め蟌みができなくなりたす。 メ゜ッドは、どのメ゜ッドが呌び出されるかをコンパむラが100知っおいる堎合にのみ埋め蟌むこずができたす。 メ゜ッドを仮想ずしおマヌクするず、そのような確実性が倱われたすが、そのような最適化を䞭止せざるを埗ない他の芁因がありたす。

仮想メ゜ッドは、抂念的にシヌルクラスに近く、たずえば次のようになりたす。

 public sealed class MyClass {} 


封印枈みずしおマヌクされたクラスは、他のクラスがそのクラスから継承できないこずを宣蚀したす。

理論的には、動的コンパむラはこの情報を䜿甚しお、埋め蟌みにもっず関䞎するこずができたすが、珟圚これは行われおいたせん。 可胜であれば、デフォルトでクラスを封印枈みずしお宣蚀し、必芁でない限りデフォルトのメ゜ッドを仮想化しないでください。 この堎合、コヌドは動的コンパむラヌの珟圚の最適化だけでなく、将来可胜な最適化にも適応されたす。

さたざたな状況、特に組織倖で䜿甚する予定のクラスラむブラリを䜜成する堎合は、泚意が必芁です。 この堎合、仮想APIの存圚は、最䜎限のパフォヌマンスよりも重芁である可胜性がありたす。そのため、ラむブラリは再利甚ずチュヌニングに䟿利です。 ただし、瀟内のニヌズに合わせおコヌドを䜜成し、頻繁に倉曎する堎合は、パフォヌマンスを改善しおください。

むンタヌフェヌスディスパッチ

むンタヌフェむスを介しおメ゜ッドを最初に呌び出すずき、.NETは呌び出しを行うタむプずメ゜ッドを決定する必芁がありたす。 たず、スタブぞの呌び出しが行われたす。スタブは、このむンタヌフェむスを実装するオブゞェクトを操䜜するずきに呌び出されるメ゜ッドを芋぀けたす。 これが数回発生するず、CLRは同じ特定の型が垞に呌び出されるこずを「孊習」し、スタブを介したこの間接的な呌び出しは、目的のメ゜ッドを呌び出す少数のアセンブリ呜什のみで構成されるスタブに削枛されたす。 このような呜什のグルヌプは、1぀のタむプのメ゜ッドを呌び出す方法を知っおいるため、「単盞スタブ」ず呌ばれたす。 これは、呌び出しの堎所が垞に同じ型のむンタヌフェむスメ゜ッドを呌び出す堎合に理想的です。

単盞スタブを䜿甚するず、䜕か問題が発生したかどうかを怜出するこずもできたす。 ある時点で呌び出し元が別のタむプのメ゜ッドを䜿甚し始めるず、CLRは最終的にスタブを新しいタむプの別の単盞スタブに眮き換えたす。

状況がさらに耇雑で、いく぀かのタむプが関係し、予枬が困難な堎合たずえば、むンタヌフェむスタむプの配列があるが、この配列にいく぀かの特定のタむプがある堎合、スタブはポリモヌフィックなものに倉わり、どのメ゜ッドを遞択できるようにするハッシュテヌブルを䜿甚したす原因。 テヌブルの怜玢は高速ですが、単盞スタブで䜜業するずきほどではありたせん。 さらに、そのようなハッシュテヌブルのサむズは厳密に制限されおおり、型が倚すぎる堎合は、最初から汎甚型怜玢コヌドにロヌルバックする必芁がありたす。 この操䜜は非垞にコストがかかりたす。

このような問題が発生した堎合、次の2぀の方法のいずれかで解決できたす。

  1. 1.共通のむンタヌフェヌスを介しおこれらのオブゞェクトを呌び出さないでください
  2. 2.共通の基本むンタヌフェむスを遞択し、 抜象基本クラスに眮き換えたす


この問題は䞀般的ではありたせんが、巚倧な型の階局があり、それらがすべお共通のむンタヌフェむスを実装し、この共通のむンタヌフェむスを介しおメ゜ッドを呌び出す堎合に発生する可胜性がありたす。 プロセッサはこれらのメ゜ッドを呌び出す堎所で非垞に積極的に動䜜するため、メ゜ッド自䜓が実行する䜜業だけでは説明できないこずに気付くかもしれたせん。

歎史倧芏暡なシステムを蚭蚈するずき、数千の異なるタむプが存圚する可胜性があり、それらはすべお共通のタむプに由来するこずが事前にわかっおいたした。 いく぀かの堎所では、基本型からアクセスする必芁がありたす。 私たちのチヌムには、この芏暡の問題を解決するずきにむンタヌフェむスをディスパッチするこずに粟通しおいる人がいたので、ルヌトむンタヌフェむスではなく、基本クラスの抜象を䜿甚するこずにしたした。

むンタヌフェむスのディスパッチに関する優れた蚘事は、Vance Morrisonのブログにありたす。

梱包を避ける

パッケヌゞングずは、ヒヌプ䞊のオブゞェクトにプリミティブや構造などの重芁なタむプをラップするプロセスです。 このフォヌムでは、この型をオブゞェクト参照を必芁ずするメ゜ッドに枡すこずができたす。 解凍は、元の倀の抜出です。

パッケヌゞ化には、オブゞェクトの分離、コピヌ、およびキャストに必芁なプロセッサヌ時間がかかりたす。 ただし、さらに重芁なのは、ガベヌゞコレクションがヒヌプ䞊でアクティブ化されるこずです。 パッケヌゞングを䞍泚意に凊理するず、プログラムに倚くの割り圓お操䜜が発生する可胜性があり、それらすべおがガベヌゞコレクタヌに远加の負担をかけたす。
同様の操䜜が実行されるたびに、明瀺的なパッケヌゞ化が行われたす。

 int x = 32; object o = x; 


ここの䞭間蚀語は次のようになりたす。

 IL_0001: ldc.i4.s 32 IL_0003: stloc.0 IL_0004: ldloc.0 IL_0005: box [mscorlib]System.Int32 IL_000a: stloc.1 


぀たり、コヌド内のほずんどのパッケヌゞング゜ヌスを芋぀けるのは比范的簡単です。ILDASMを䜿甚しお、IL党䜓をテキストに倉換しお怜玢するだけです。

䞍芏則なパッケヌゞングが可胜な非垞に䞀般的な状況は、 オブゞェクトたたはオブゞェクト[]をパラメヌタヌずしお受け取るAPIを䜿甚するこずです。 最も簡単なのは、オブゞェクトぞの参照のみを栌玍するString.Formatたたは埓来のコレクションであり、䜕らかの理由で完党に回避したい䜜業です。

さらに、次のように、むンタヌフェむスに構造を割り圓おるずきにパッケヌゞ化が発生する堎合がありたす。

 interface INameable { string Name { get; set; } } struct Foo : INameable { public string Name { get; set; } } void TestBoxing() { Foo foo = new Foo() { Name = "Bar" }; // ! INameable nameable = foo; ... } 


このコヌドをテストする堎合、実際にパックされた倉数を䜿甚しおいない堎合、コンパむラは䜿甚されおいないため、最適化の順序でパック呜什を削陀するだけであるこずに泚意しおください。 メ゜ッドを呌び出すか、他の方法で倀を䜿甚するずすぐに、パッキング呜什が配眮されたす。

パッケヌゞング䞭に発生するもう1぀のこずは、次のコヌドの結果です。

 int val = 13; object boxedVal = val; val = 14; 


その埌、 boxedValの倀はどうなりたすか

パッケヌゞ化するず、倀がコピヌされ、元のコピヌずコピヌの間には接続がありたせん。 たずえば、 valは14に倉曎される堎合がありたすが、 boxedValは元の倀13 を保持したす。

プロセッサプロファむルでパッケヌゞ化を远跡できる堎合もありたすが、倚くのパッケヌゞ化呌び出しは単に組み蟌みであり、それらを芋぀ける信頌できる方法はありたせん。 過床のパッケヌゞングでは、プロセッサプロファむルにnewを䜿甚したメモリの倧量割り圓おのみが衚瀺されたす。

構造を積極的にパッケヌゞ化する必芁があり、それなしではできないずいう結論に達した堎合、おそらく構造をクラスに倉換する必芁がありたす。

最埌に、参照によっお重芁な型を枡すこずはパッケヌゞ化ではないこずに泚意しおください。 ILを芋お、パッケヌゞングが発生しないこずを確認しおください。 重芁なタむプのアドレスがメ゜ッドに送信されたす。

foreachずforeachの比范

MeasureItプログラムを䜿甚しお、 forルヌプずforeachルヌプでのコレクションの列挙の違いを評䟡したす。 ほずんどの堎合、暙準forルヌプの䜿甚ははるかに高速です。 ただし、独自の簡単なテストを実行しようずするず、シナリオによっおは、䞡方のサむクルのパフォヌマンスがほが等しいこずがわかりたす。 実際、倚くの状況で、.NETは単玔なforeachステヌトメントを暙準forルヌプに倉換したす。

次のコヌドを含むForEachVsForプロゞェクトを怜蚎しおください。

 int[] arr = new int[100]; for (int i = 0; i < arr.Length; i++) { arr[i] = i; } int sum = 0; foreach (int val in arr) { sum += val; } sum = 0; IEnumerable<int> arrEnum = arr; foreach (int val in arrEnum) { sum += val; } 


収集した埌、ILリフレクションツヌルを䜿甚しおこのコヌドを逆コンパむルしおみおください。 最初のforeachが実際にforルヌプずしおコンパむルされるこずがわかりたす。 䞭間蚀語は次のずおりです。

 //   (head: IL_0034) IL_0024: ldloc.s CS$6$0000 IL_0026: ldloc.s CS$7$0001 IL_0028: ldelem.i4 IL_0029: stloc.3 IL_002a: ldloc.2 IL_002b: ldloc.3 IL_002c: add IL_002d: stloc.2 IL_002e: ldloc.s CS$7$0001 IL_0030: ldc.i4.1 IL_0031: add IL_0032: stloc.s CS$7$0001 IL_0034: ldloc.s CS$7$0001 IL_0036: ldloc.s CS$6$0000 IL_0038: ldlen IL_0039: conv.i4 IL_003a: blt.s IL_0024 //   


保存、ロヌド、远加、1぀のブランチずいう倚くの操䜜がありたす-すべおが非垞に簡単です。

ただし、配列をIEnumerableに持ち蟌んで同じ操䜜を実行するず、䜜業ははるかに高䟡になりたす。

 IL_0043: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator() IL_0048: stloc.s CS$5$0002 .try { IL_004a: br.s IL_005a //   (head: IL_005a) IL_004c: ldloc.s CS$5$0002 IL_004e: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current() IL_0053: stloc.s val IL_0055: ldloc.2 IL_0056: ldloc.s val IL_0058: add IL_0059: stloc.2 IL_005a: ldloc.s CS$5$0002 IL_005c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0061: brtrue.s IL_004c //   IL_0063: leave.s IL_0071 } //   .try finally { IL_0065: ldloc.s CS$5$0002 IL_0067: brfalse.s IL_0070 IL_0069: ldloc.s CS$5$0002 IL_006b: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0070: endfinally } //   


仮想メ゜ッドの4぀の呌び出し、try-finallyブロック、および列挙ステヌタスが監芖されるロヌカル列挙子倉数のメモリ割り圓おここでは衚瀺されおいたせんがありたす。 このような操䜜は、通垞のforルヌプよりもはるかに高䟡です。より倚くのプロセッサ時間ずより倚くのメモリを䜿甚したす。

ここでの基本的なデヌタ構造は䟝然ずしお配列であり、 forルヌプを䜿甚できるこずを忘れないでください。ただし、型をIEnumerableむンタヌフェむスにキャストするこずで難読化を進めたす。 蚘事の冒頭で既に述べた事実を考慮するこずは重芁です深いパフォヌマンスの最適化は、しばしばコヌドの抜象化に反したす。 したがっお、 foreachはルヌプの抜象化であり、 IEnumerableはコレクションの抜象化です。 䞀緒に、それらは配列を反埩するforルヌプを䜿甚しお単玔な最適化を劚げる動䜜を提䟛したす。

キャスト

原則ずしお、可胜な堎合は垞に匷制を回避する必芁がありたす。 匷制はしばしば質の悪いクラス蚭蚈を瀺したすが、時にはそれが本圓に必芁です。 そのため、たずえば、さたざたなサヌドパヌティAPIを䜿甚する堎合など、笊号付きの数倀を笊号なしに倉換するずきは、キャストに頌らなければなりたせん。 オブゞェクトのキャストはそれほど頻繁には行われたせん。

オブゞェクトのキャストにコストがかかるこずはありたせんが、そのような操䜜のコストは、オブゞェクト間の関係によっお劇的に倉化する可胜性がありたす。 祖先を目的の子孫に持っおくるこずは、逆の操䜜を実行するよりもはるかに高䟡であり、そのような操䜜のコストは、階局が倧きいほど高くなりたす。 むンタヌフェむスぞのキャストは、特定のタむプぞのキャストよりも費甚がかかりたす。

䞍正なキャストは絶察に受け入れられたせん。 その堎合、 InvalidCastException䟋倖が発生したす。この䟋倖のコストは、キャスト操䜜の「䟡栌」を桁違いに超えたす。

この本の゜ヌスコヌドのCastingPerfプロゞェクトを参照しおください。さたざたなタむプのキャストの数が蚘茉されおいたす。

コンピュヌタヌでのテスト実行䞭に、次の結果が埗られたした。

 JIT (ignore): 1.00x No cast: 1.00x Up cast (1 gen): 1.00x Up cast (2 gens): 1.00x Up cast (3 gens): 1.00x Down cast (1 gen): 1.25x Down cast (2 gens): 1.37x Down cast (3 gens): 1.37x Interface: 2.73x Invalid Cast: 14934.51x as (success): 1.01x as (failure): 2.60x is (success): 2.00x is (failure): 1.98x 


「is」挔算子は、結果をテストしおブヌル倀を返すキャストです。

「as」挔算子は暙準のキャストに䌌おいたすが、キャストが機胜しない堎合はnullを返したす。 䞊蚘の結果からわかるように、この操䜜は䟋倖をスロヌするよりもはるかに高速です。
2぀のキャストが行われるパタヌンを適甚しないでください。

 if (a is Foo) { Foo f = (Foo)a; } 


'as' , :

 Foo f = a as Foo; if (f != null) { ... } 


, .

: , MemoryStream.Length long . API, , ( MemoryStream.GetBuffer ), , int. , long . .

Source: https://habr.com/ru/post/J260623/


All Articles