.Net / Cでの文字列の展開に぀いお

文字列に぀いお、たたは.Net / Cで文字列を反転するこずに぀いお話したしょう。 たたたた暙準ラむブラリでは、察応する関数が芳察されおいたせん。 そしお、圌らが私に蚀ったように、行反転関数を曞くこずは、就職の面接でかなり䞀般的な質問です。 このプラットフォヌムを䜿甚しお、効果的にラむンを反転できる方法を芋おみたしょう。



カットの䞋には、ラむン反転のさたざたな方法のパフォヌマンスの比范分析がありたす。







Khrr-rr ...-チェヌン゜ヌは蚀った。

Tyuyuyu ...-男性は蚀った。

©叀いゞョヌク。



さあ、始めたしょう。 すべおの実装は、サむズが256メガバむト128×1024×1024文字の単䞀行ず256バむト128文字の1024×1024行で速床がテストされたした。 各枬定の前に、ガベヌゞコレクションが加速されこのようなサむズのテストデヌタで重芁です、枬定が50回実行され、20の極端なものが砎棄され、残りの倀が平均化されたした。 条件付きオりムは、ストップりォッチクラスのオブゞェクトによっお発行されたティックの数を遞択したした。



このテストは、Athlon64 x2 4200 +、2GBデュアルチャンネルDDR2 RAMおよびPentium4 HT 3GHz、3GB DDR RAMの2台のコンピュヌタヌで実行されたした。 このテストの構成の䞻な違いは、メモリキャッシュバンドルのパフォヌマンスです。2番目のシステムは、この点で著しく䜎速です。



技術的な手続きは終わりたした。実際の実装に移りたしょう。 明確にするために、ここではナニコヌドのサロゲヌトコヌドポむントは考慮されおいたせん。 呚囲の「゚コシステム」のコンテキストで、ロゞックず実装の「可愛さ」の順にアプロヌチを怜蚎したす。



このノヌトの最埌の郚分に、比范枬定倀がありたす。 䞀般に、 ReverseUnsafeCopy関数は、安党なコヌド-ReverseArrayManualが制限されおいる堎合にのみ最適であるこずが刀明したした。 安党なコヌドず巚倧な文字列が必芁な堎合、 ReverseStringBuilderで苊しむ必芁がありたす。



パヌト1「通垞の」メ゜ッド。



1. ReverseStringBuilder


掚奚事項に埓い、「倧きな」文字列を䜜成するために、特別なツヌルStringBuilderクラスを䜿甚したす。 アむデアは恐ろしいほど単玔です。適切なサむズのビルダヌを䜜成し、逆の順序で行に沿っお移動し、新しい行に文字を远加したす。



Copy Source | Copy HTML static string ReverseStringBuilder( string str) { StringBuilder sb = new StringBuilder (str.Length); for ( int i = str.Length; i-- != 0 ; ) sb.Append(str[i]); return sb.ToString(); }



  1. Copy Source | Copy HTML static string ReverseStringBuilder( string str) { StringBuilder sb = new StringBuilder (str.Length); for ( int i = str.Length; i-- != 0 ; ) sb.Append(str[i]); return sb.ToString(); }



  2. Copy Source | Copy HTML static string ReverseStringBuilder( string str) { StringBuilder sb = new StringBuilder (str.Length); for ( int i = str.Length; i-- != 0 ; ) sb.Append(str[i]); return sb.ToString(); }



  3. Copy Source | Copy HTML static string ReverseStringBuilder( string str) { StringBuilder sb = new StringBuilder (str.Length); for ( int i = str.Length; i-- != 0 ; ) sb.Append(str[i]); return sb.ToString(); }



  4. Copy Source | Copy HTML static string ReverseStringBuilder( string str) { StringBuilder sb = new StringBuilder (str.Length); for ( int i = str.Length; i-- != 0 ; ) sb.Append(str[i]); return sb.ToString(); }



  5. Copy Source | Copy HTML static string ReverseStringBuilder( string str) { StringBuilder sb = new StringBuilder (str.Length); for ( int i = str.Length; i-- != 0 ; ) sb.Append(str[i]); return sb.ToString(); }



  6. Copy Source | Copy HTML static string ReverseStringBuilder( string str) { StringBuilder sb = new StringBuilder (str.Length); for ( int i = str.Length; i-- != 0 ; ) sb.Append(str[i]); return sb.ToString(); }



  7. Copy Source | Copy HTML static string ReverseStringBuilder( string str) { StringBuilder sb = new StringBuilder (str.Length); for ( int i = str.Length; i-- != 0 ; ) sb.Append(str[i]); return sb.ToString(); }





私たちは詊しお、起動したす、はい...それはすべお䜕らかの圢で䜕らかの圢でゆっくり動䜜したす。



2. ReverseArrayFramework


ハ したがっお、このビルダヌには、すべおの偎面からスレッドの安党性を確保するためのチェックが装備されおいたす。いいえ、これは必芁ありたせん。 しかし、文字列は文字の配列です。 裏返しお、結果を文字列に倉換したす。



Copy Source | Copy HTML



  1. 静的文字列 ReverseArrayFramework 文字列 str
  2. {
  3. char [] arr = str.ToCharArray;
  4. 配列 .Reversearr;
  5. 新しい 文字列 arrを返したす。
  6. }


たったく別の問題で、3.5倍高速でした。 うヌん、倚分もっずいい



3. ReverseArrayManual


だから、私たちは思う。 最初に、デヌタが2回コピヌされたす。最初は文字列から配列に、次に配列内にコピヌされたす。 次に、Array.Reverseはラむブラリメ゜ッドです。぀たり、入力チェックがありたす。 さらに、アトミックタむプの堎合、ネむティブメ゜ッドずしお明瀺的に実装され、これは実行コンテキストの远加の切り替えです。 文字列を手動で配列に倉換しおみたしょう。



Copy Source | Copy HTML



  1. 静的文字列 ReverseArrayManual string originalString
  2. {
  3. char [] reverseCharArray = new char [originalString.Length];
  4. for  int i = originalString.Length- 1 ; i> -1 ; i--
  5. ReverseCharArray [originalString.Length-i- 1 ] = originalString [i];
  6. 新しい文字列を返したす reversedCharArray;
  7. }


4. ReverseManualHalf


どうぞ この堎合、アクションは行の䞭倮に察しお察称です。぀たり、2぀のむンデックスを反察方向に配眮し、反埩回数を半分にするこずができたす。



Copy Source | Copy HTML



  1. 静的文字列 ReverseManualHalf string originalString
  2. {
  3. char [] reverseCharArray = new char [originalString.Length];
  4. int i = 0 ;
  5. int j = originalString.Length- 1 ;
  6. while i <= j
  7. {
  8. reverseCharArray [i] = originalString [j];
  9. reverseCharArray [j] = originalString [i];
  10. i ++; j--;
  11. }
  12. 新しい文字列を返したす reversedCharArray;
  13. }


うヌん...䜕かがうたくいかなかった、遅いメモリのシステムで、速床が1.5倍萜ちた。 構成ず実装の詳现を考えるず、メモリ速床ずプロセッサキャッシュが原因であるず考えるこずができたす。以前は2぀のリモヌトメモリ領域を同時に操䜜しおいたしたが、珟圚はそれぞれ4぀あり、デヌタキャプチャはより頻繁に実行されたす。



LINQず逆の方法


LINQにはただ比范的矎しく短い方法がありたすが、パフォヌマンスに関する批刀に耐えるこずはできたせん。StringBuilderに基づく方法よりも3〜3.5倍遅く動䜜したす。 この理由は、IEnumerableを介したデヌタのポンピングず各反埩の仮想呌び出しです。 ご垌望の方には、以䞋が実装です。



Copy Source | Copy HTML



  1. 静的文字列 ReverseStringLinq string originalString
  2. {
  3. 新しい文字列を返したす originalString.Reverse。ToArray;
  4. }


メモリ䜿甚量


ほずんどの堎合、問題はそれほど重倧ではありたせんが、考慮されるメ゜ッドのすべおの「高速」は、文字の配列の圢匏で文字列の䞭間コピヌを䜜成したす。 合成テストでは、これは最初のメ゜ッドのみが512MBの文字列をラップでき、残りはSystem.OutOfMemoryExceptionになったずいう事実に珟れおいたす。 たた、䞍必芁な䞀時オブゞェクトがGCの頻床を増やすこずを忘れおはなりたせん。それは恐怖のポむントに最適化されおいたすが、ずにかく時間を浪費したす。 次のパヌトでは、速床の最適化に加えお、この問題の解決策も探したす。



パヌト2より速く、より効率的に、たたは安党でないコヌドが必芁な堎合。



安党でないコヌドを䜿甚するず、1぀の興味深い利点が埗られたす。以前は䞍倉であった行を倉曎できるようになりたしたが、非垞に泚意しお行のコピヌのみを倉曎する必芁がありたす-ラむブラリは1行のコピヌの数を最小限に抑えたす。アプリケヌション。



したがっお、適切なサむズの新しい行を䜜成したら、それを配列ずしお芋お、必芁なデヌタを入力できたす。 さお、むンデックスの有効性のチェックがないこずを忘れないでください。これによりコヌドの速床も䞊がりたす。 ただし、.Netの文字列の仕様により、目的の長さの文字列を䜜成するこずはできたせん。 Stringchar、intコンストラクタヌを䜿甚しお、繰り返し文字スペヌスなどからストリングを䜜成するか、String.CopyStringを䜿甚しお元のストリングをコピヌできたす。



この知識を歊噚に、次の2぀の実装を䜜成したす。



5. ReverseUnsafeFill


スペヌスの行を䜜成し、逆の順序で埋めたす。



Copy Source | Copy HTML



  1. 静的な安党でない文字列 ReverseUnsafeFill 文字列 str
  2. {
  3. if str.Length <= 1 strを返す ;
  4. String copy = new String  '' 、str.Length;
  5. 修正  char * buf_copy = copy
  6. {
  7. 修正  char * buf = str
  8. {
  9. int i = 0 ;
  10. int j = str.Length- 1 ;
  11. while i <= j
  12. {
  13. buf_copy [i] = buf [j];
  14. buf_copy [j] = buf [i];
  15. i ++; j--;
  16. }
  17. }
  18. }
  19. コピヌを返す ;
  20. }


6. ReverseUnsafeCopy


行をコピヌしお反転したす。



Copy Source | Copy HTML



  1. 静的な安党でない文字列 ReverseUnsafeCopy 文字列 str
  2. {
  3. if str.Length <= 1 strを返す ;
  4. char tmp;
  5. String copy = String .Copystr;
  6. 修正  char * buf = copy
  7. {
  8. char * p = buf;
  9. char * q = buf + str.Length- 1 ;
  10. while p <q
  11. {
  12. tmp = * p;
  13. * p = * q;
  14. * q = tmp;
  15. p ++; q--;
  16. }
  17. }
  18. コピヌを返す ;
  19. }


枬定結果が瀺すように、2番目のバヌゞョンは、䜎速メモリでははるかに速く動䜜し、高速ではわずかに遅くなりたす:)いく぀かの理由がありたす4぀ではなく2぀のリモヌトメモリ領域で動䜜し、メモリブロックのコピヌ速床ずルヌプの単玔な充填速床の違い フルパスでReverseUnsafeFillバヌゞョンを䜜成しおメモリからキャッシュぞのデヌタキャプチャの数を枛らすこずができたす、遅いメモリでテストするこずができたすが、ReverseUnsafeCopyよりも遅いず信じる理由がありたす間違っおいる可胜性がありたす。



7. ReverseUnsafeXorCopy


次は うわさによるず、XOR挔算子ずの亀換は、 3番目の倉数をコピヌするよりも高速に動䜜したすちなみに、長所では「a ^ = b ^ = a ^ = b;」、Cでは、この行は機胜したせん 。 さお、実際に確認したしょう。



Copy Source | Copy HTML



  1. 静的な安党でない文字列 ReverseUnsafeXorCopy string str
  2. {
  3. if str.Length <= 1 strを返す ;
  4. String copy = String .Copystr;
  5. 修正  char * buf = copy
  6. {
  7. char * p = buf;
  8. char * q = buf + str.Length- 1 ;
  9. while p <q
  10. {
  11. * p ^ = * q;
  12. * q ^ = * p;
  13. * p ^ = * q;
  14. p ++; q--;
  15. }
  16. }
  17. コピヌを返す ;
  18. }


その結果、コピヌよりも1.2〜1.5倍遅くなりたす。 レゞスタの倀をすばやく亀換するために機胜するトリックは、倉数に察しおは機胜したせんでした倚くのC / C ++コンパむラでは機胜したせん。



この事実の説明を求めお、アプリケヌションの内郚に登り、結果のCILコヌドを読み取りたす。



パヌト3CILおよび.Netラむブラリコヌドに進みたす。



XORによる亀換がより悪いこずが刀明した理由


この質問ぞの回答に぀いおは、2぀の亀換方法甚に生成されたCILコヌドを芋る䟡倀がありたす。 これらの呜什を理解しやすくするために、その目的を説明したす。ldloc.N-スタックにロヌカル倉数番号Nをロヌドし、 stloc .N-スタックの先頭をロヌカル倉数番号Nに読み蟌みたすxor-スタックの先頭にある2぀の倀のXOR挔算の倀を蚈算し、結果を読み蟌みたす代わりにスタックに。



コピヌ共有 XORベヌスの亀換 完党なCIL亀換
  1. int a、b;
  2. int tmp = a;
  3. a = b;
  4. b = tmp;
  1. int a、b;
  2. a ^ = b;
  3. b ^ = a;
  4. a ^ = b;
.locals init (<br/>

[0] int32 a,<br/>

[1] int32 b,<br/>

[2] int32 tmp)





.locals init (<br/>

[0] int32 a,<br/>

[1] int32 b)<br/>





.locals init (<br/>

[0] int32 a,<br/>

[1] int32 b)<br/>





L_0004: ldloc .0<br/>

L_0005: stloc .2<br/>

L_0006: ldloc .1<br/>

L_0007: stloc .0<br/>

L_0008: ldloc .2<br/>

L_0009: stloc .1<br/> <br/> <br/> <br/> <br/> <br/>





L_0004: ldloc .0<br/>

L_0005: ldloc .1<br/>

L_0006: xor <br/>

L_0007: stloc .0<br/>

L_0008: ldloc .1<br/>

L_0009: ldloc .0<br/>

L_000a: xor <br/>

L_000b: stloc .1<br/>

L_000c: ldloc .0<br/>

L_000d: ldloc .1<br/>

L_000e: xor <br/>

L_000f: stloc .0<br/>





L_0004: ldloc .0<br/>

L_0005: ldloc .1<br/>

L_0006: stloc .0<br/>

L_0007: stloc .1<br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/>







ご芧のずおり、XOR挔算子を䜿甚したバヌゞョンでは、より倚くの仮想マシン操䜜が必芁です。 さらに、その埌のネむティブコヌドぞのコンパむル時に、䜿甚されるプロセッサの胜力を考慮しお、3番目の倉数を介した䞀般的な亀換をより効率的な亀換操䜜に眮き換える最適化が行われる可胜性がありたす。 分析の副䜜甚ずしお、CILの仮想的な「理想的な」亀換が行われたす。CILの䜿甚に䜕らかの効果があるかどうかを確認するのは興味深いでしょうが、このためにilasmたたは反射/攟出を台無しにする必芁がありたす。 ただし、結果のコヌドは安党ではないcよりも適甚性が䜎いため、実甚的な関心はありたせん。



Array.Reverseはどのように機胜したすか


さお、リフレクタヌを䜿甚しおアセンブリの内郚を芋るず、最初の郚分で䜿甚されるラむブラリメ゜ッドの実装を芋るのが理にかなっおいたす。 ここではArray.Reverseが際立っおおり、ネむティブメ゜ッドずしお実装された特定の関数Array.TrySZReverseに䟝存しおいたす。 したがっお、 Shared Source Common Language Infrastructure 2.0 -source .net 2.0をダりンロヌドしお、どんな皮類の獣かを確認しおください:)短い怜玢の埌、ファむル「sscli20 \ clr \ src \ vm \ comarrayhelpers.h」にリバヌステンプレヌト関数がありたすこれにはKINDのケヌスはUINT16に察応したすが、これは驚きReverseUnsafeCopy実装ず恐ろしく䌌おいたす。



Copy Source | Copy HTML



  1. static void Reverse KIND配列[]、 UNIT32むンデックス、 UNIT32カりント{
  2. LEAF_CONTRACT;
  3. _ASSERTEarray= NULL;
  4. if count == 0{
  5. åž°ã‚‹
  6. }
  7. UNIT32 i =むンデックス。
  8. UNIT32 j =むンデックス+カりント-1 ;
  9. while i <j{
  10. KIND temp = array [i];
  11. 配列[i] =配列[j];
  12. 配列[j] = temp;
  13. i ++;
  14. j--;
  15. }
  16. }


パヌト4バむナリマラ゜ンの結果。



枬定結果の分析は最初の2぀の郚分で行いたすが、ここでは䞊蚘の機胜の性胜比を瀺す比范図のみを瀺したす。 「ティック」の正確な数倀は構成に䟝存しすぎおおり、特に関心を匕くこずはほずんどありたせん。誰もが提䟛されたコヌドを䜿甚しお自分のパフォヌマンスを枬定できたす。



最初の2぀の郚分のベストプラクティスの比范


高速メモリ䞊

TinyPicによる画像ずビデオのホスティング

遅いメモリ

TinyPicによる画像ずビデオのホスティング



高速メモリでのデバッグおよびリリヌス構成のすべおのメ゜ッドの比范


倧きな線

TinyPicによる画像ずビデオのホスティング

短い線

TinyPicによる画像ずビデオのホスティング



䜎速メモリでのデバッグおよびリリヌス構成のすべおのメ゜ッドの比范


倧きな線

TinyPicによる画像ずビデオのホスティング

短い線

TinyPicによる画像ずビデオのホスティング



All Articles