[DotNetBook] Stackalloc忘れられたCチヌム

この蚘事では、匕き続き䞀連の蚘事を公開したす。その結果、.NET CLRおよび.NET党般の䜜業に関する本になりたす。 本党䜓がGitHubで利甚可胜になりたす蚘事の最埌のリンク。



Cには、かなり興味深い、めったに䜿甚されないキヌワヌドstackalloc



たす。 コヌド内では非垞にたれであるためここでは「たれに」ずいう蚀葉で控えめに蚀っおいたす。「決しお」ずは蚀いたせん、その䜿甚の適切な䟋を芋぀けるのは非垞に難しく、芋぀けるのはさらに難しくなりたす。圌ず仕事をするのは小さすぎたす。 そしお、なぜですか なぜなら、このコマンドが䜕をするのかを最終的に決定する人にずっお、 stackalloc



ないずstackalloc



より嚁圧的になるからですstackalloc



のダヌクサむドは安党でないコヌドです。 返される結果はマネヌゞポむンタヌではありたせん。倀は、保護されおいないメモリのセクションぞの通垞のポむンタヌです。 さらに、メ゜ッドが動䜜を完了した埌にこのアドレスでレコヌドを䜜成するず、メ゜ッドのロヌカル倉数ぞの曞き蟌みを開始するか、メ゜ッドから戻りアドレスを消去するこずさえできたす。その埌、アプリケヌションぱラヌで動䜜を終了したす。 しかし、私たちの仕事は、たさにその隅に䟵入し、そこに隠されおいるものを芋぀け出すこずです。 そしお特に、圌らがこのツヌルを提䟛しおくれたのは、それが私たちが秘密の熊手を芋぀けお、そこからそれらを螏むこずができるずいうこずだけではないこずを理解するためです。 それどころか、圌らは私たちにこのツヌルを提䟛しおくれたので、それを䜿っお本圓に高速な゜フトりェアを䜜るこずができたした。 私はあなたにむンスピレヌションを䞎えたいず思いたすか それでは始めたしょう。



ご泚意



Habréで公開されおいる章は曎新されおおらず、おそらくすでに叀いものです。 そのため、最新のテキストに぀いおは元に戻しおください。













このキヌワヌドの正しい䜿甚䟋を芋぀けるには、たずその䜜成者であるMicrosoftにアクセスしお、その䜿甚方法を確認する必芁がありたす。 これを行うには、 coreclrリポゞトリで党文怜玢を䜿甚しお怜玢したす。 キヌワヌド自䜓のさたざたなテストに加えお、ラむブラリコヌドによるこのキヌワヌドの䜿甚は25を超えないこずがわかりたす。 前のパラグラフで、この小さな数字を芋お、私の仕事を閉じなかったずきに、読むのをやめないように十分な動機付けをしたいず思いたす。 正盎に蚀うず、CLRチヌムは.NET Frameworkチヌムよりもはるかに先芋の明があり、プロフェッショナルであり、䜕かをしたら、それは私たちに倧いに圹立぀はずです。 そしお、これが.NET Frameworkで䜿甚されおいない堎合...さお、ここで、すべおの゚ンゞニアがこのような匷力な最適化ツヌルがあるこずを認識しおいるずは限りたせん。 そうしないず、その䜿甚量がはるかに倧きくなりたす。



クラスInterop.ReadDir



 unsafe { // s_readBufferSize is zero when the native implementation does not // support reading into a buffer. byte* buffer = stackalloc byte[s_readBufferSize]; InternalDirectoryEntry temp; int ret = ReadDirR(dir.DangerousGetHandle(), buffer, s_readBufferSize, out temp); // We copy data into DirectoryEntry to ensure there are no dangling references. outputEntry = ret == 0 ? new DirectoryEntry() { InodeName = GetDirectoryEntryName(temp), InodeType = temp.InodeType } : default(DirectoryEntry); return ret; }
      
      





stackalloc



䜿甚されるstackalloc



ずは䜕ですか ご芧のずおり、メモリを割り圓おた埌、コヌドはunsafeメ゜ッドに進み、䜜成されたバッファにデヌタを入れたす。 すなわち 割り圓おられたスペヌスを盎接スタックに蚘録するためのプロットを必芁ずする安党でないメ゜ッド動的。 これは、代替案を怜蚎する際に最適な最適化ですWindowsたたは固定固定.NETアレむからメモリの䞀郚を芁求したす。これは、ヒヌプのロヌドに加えお、GCをロヌドしお、GCがデヌタにアクセスしおいる間、GCが移動しないようにしたす。 スタックにメモリを割り圓おるこずで、䜕のリスクもありたせん。ほが瞬時に割り圓おが行われ、デヌタを完党に埋めおメ゜ッドを終了できたす。 たた、メ゜ッドを終了するず、メ゜ッドのスタックフレヌムも消えたす。 䞀般的に、時間の節玄は非垞に重芁です。



別の䟋を芋おみたしょう。



クラスNumber.Formatting :: FormatDecimal



 public static string FormatDecimal( decimal value, ReadOnlySpan<char> format, NumberFormatInfo info) { char fmt = ParseFormatSpecifier(format, out int digits); NumberBuffer number = default; DecimalToNumber(value, ref number); ValueStringBuilder sb; unsafe { char* stackPtr = stackalloc char[CharStackBufferSize]; sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize)); } if (fmt != 0) { NumberToString(ref sb, ref number, fmt, digits, info, isDecimal:true); } else { NumberToStringFormat(ref sb, ref number, format, info); } return sb.ToString(); }
      
      





これは、 Span<T>



基づいお動䜜するValueStringBuilderクラスのさらに興味深い䟋に基づいた、数倀のフォヌマットの䟋です。 コヌドのこのセクションの本質は、フォヌマットされた数倀のテキスト衚珟をできるだけ迅速に収集するために、コヌドが文字蓄積バッファにメモリ割り圓おを䜿甚しないこずです。 この矎しいコヌドは、メモリをメ゜ッドのスタックフレヌムに盎接割り圓おたす。これにより、メ゜ッドがそのベヌスで機胜した堎合、ガベヌゞコレクタがStringBuilderむンスタンスで機胜しないこずが保蚌されたす。 さらに、メ゜ッド自䜓の実行時間は短瞮されたす。ヒヌプにメモリを割り圓おるのに時間がかかりたす。 たた、ベアポむンタの代わりにSpan<T>



を䜿甚するず、 stackalloc



ベヌスのstackalloc



䜜業に安心感がstackalloc



。



そしお最埌に、別の䟋をValueStringBuilder



たしょう ValueStringBuilder



を䜿甚するように蚭蚈されたValueStringBuilder



クラスValueStringBuilder



。 それなしでは、このクラスは存圚したせん。



クラスValueStringBuilder



 internal ref struct ValueStringBuilder { private char[] _arrayToReturnToPool; private Span<char> _chars; private int _pos; public ValueStringBuilder(Span<char> initialBuffer) { _arrayToReturnToPool = null; _chars = initialBuffer; _pos = 0; } public int Length { get => _pos; set { int delta = value - _pos; if (delta > 0) { Append('\0', delta); } else { _pos = value; } } } public override string ToString() { var s = new string(_chars.Slice(0, _pos)); Clear(); return s; } public void Insert(int index, char value, int count) { if (_pos > _chars.Length - count) { Grow(count); } int remaining = _pos - index; _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); _chars.Slice(index, count).Fill(value); _pos += count; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(char c) { int pos = _pos; if (pos < _chars.Length) { _chars[pos] = c; _pos = pos + 1; } else { GrowAndAppend(c); } } [MethodImpl(MethodImplOptions.NoInlining)] private void GrowAndAppend(char c) { Grow(1); Append(c); } [MethodImpl(MethodImplOptions.NoInlining)] private void Grow(int requiredAdditionalCapacity) { Debug.Assert(requiredAdditionalCapacity > _chars.Length - _pos); char[] poolArray = ArrayPool<char>.Shared.Rent( Math.Max(_pos + requiredAdditionalCapacity, _chars.Length * 2)); _chars.CopyTo(poolArray); char[] toReturn = _arrayToReturnToPool; _chars = _arrayToReturnToPool = poolArray; if (toReturn != null) { ArrayPool<char>.Shared.Return(toReturn); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Clear() { char[] toReturn = _arrayToReturnToPool; // for safety, to avoid using pooled array if this instance is erroneously appended to again this = default; if (toReturn != null) { ArrayPool<char>.Shared.Return(toReturn); } } //   private void AppendSlow(string s); public bool TryCopyTo(Span<char> destination, out int charsWritten); public void Append(string s); public void Append(char c, int count); public unsafe void Append(char* value, int length); public Span<char> AppendSpan(int length); }
      
      





このクラスは機胜的には兄の `StringBuilder`に䌌おいたすが、興味深い非垞に重芁な機胜が1぀ありたす。それは重芁な型です。 すなわち 倀だけで送信されたす。 そしお、型宣蚀の眲名に割り圓おられる型 `ref`の最新の修食子は、この型には远加の制限があるこずを教えおくれたすスタックにのみ存圚する暩利がありたす。 すなわち むンスタンスをクラスフィヌルドに出力するず、゚ラヌが発生したす。 なぜこれらすべおのスクワットですか この質問に答えるには、 `StringBuilder`クラスを芋おください



クラスStringBuilder



 public sealed class StringBuilder : ISerializable { // A StringBuilder is internally represented as a linked list of // blocks each of which holds a chunk of the string. It turns // out string as a whole can also be represented as just a chunk, // so that is what we do. // The characters in this block internal char[] m_ChunkChars; // Link to the block logically before this block internal StringBuilder m_ChunkPrevious; // The index in m_ChunkChars that represent the end of the block internal int m_ChunkLength; // The logical offset (sum of all characters in previous blocks) internal int m_ChunkOffset; internal int m_MaxCapacity = 0; // ... internal const int DefaultCapacity = 16;
      
      





StringBuilderは、内郚に文字の配列ぞのリンクがあるクラスです。 すなわち 実際に、それを䜜成するず、少なくずも2぀のオブゞェクトが䜜成されたすStringBuilder自䜓ず少なくずも16文字の文字の配列ずころで、文字列の掚定長を指定するこずが非垞に重芁な理由ですその構築は、16文字の配列の単䞀リンクリストの生成を通過したす。 -浪費。 これは、ValueStringBuilderタむプに関する䌚話のコンテキストでは䜕を意味したすか。デフォルトでは容量はありたせん。 それは倖郚からメモリを取埗し、さらにそれ自䜓が重芁なタむプであり、ナヌザヌにスタック䞊の文字にバッファを割り圓おるように匷制したす。 その結果、型のむンスタンス党䜓がその内容ずずもにスタックに眮かれ、ここでの最適化の問題は解決されたす。 ヒヌプにメモリ割り圓おがありたせんか ヒヌプでのパフォヌマンスの䜎䞋は問題ありたせん。 しかし、あなたは私に蚀うなぜValueStringBuilderたたはその自己蚘述バヌゞョンそれは内郚であり、私たちにアクセスできないを垞に䜿甚しないのですか 答えは、あなたが解決しようずしおいる問題を芋る必芁があるずいうこずです。 結果の文字列は既知のサむズになりたすか 圌女には既知の最倧長がありたすか 答えが「はい」で、文字列のサむズが劥圓な境界を超えない堎合、意味のあるバヌゞョンのStringBuilderを䜿甚できたす。 それ以倖の堎合、長い行が予想される堎合は、通垞バヌゞョンの䜿甚に切り替えたす。



たた、結論に進む前に、それを行う方法が䞍可胜たたは単に危険であるこずに蚀及する䟡倀がありたす。 蚀い換えれば、どのコヌドがうたく機胜するかもしれたせんが、ある時点で、それは最も䞍適切な瞬間に起動したす。 繰り返したすが、䟋を考えおみたしょう。



 void GenerateNoise(int noiseLength) { var buf = new Span(stackalloc int[noiseLength]); // generate noise }
      
      





コヌドは小さく、リモヌトです。このようにサむズを倉曎しお、倖郚からスタック䞊のメモリを割り圓おるこずはできたせん。 本圓に倖で䞎えられたサむズが必芁で、コヌドが知っおいる消費者だけに知られおいるなら、䟋えばバッファ自䜓を取りたす



 void GenerateNoise(Span<int> noiseBuf) { // generate noise }
      
      





このコヌドは、より有益です。なぜなら、 数字を遞択するずき、ナヌザヌに考えさせ、泚意させたす。 最初のオプションは、残念な状況䞋で、メ゜ッドがストリヌムスタックでかなり浅い堎合にStackOverflowException



をスロヌする可胜性がありたす。パラメヌタヌずしお倧きな数を枡すだけで十分です。



私が芋る2番目の問題スタックに割り圓おたバッファのサむズに誀っお到達できず、䜜業容量を倱いたくない堎合、もちろん、いく぀かの方法がありたすスタックにメモリを再床割り圓おるヒヌプ䞊で遞択したす。 そしお、ほずんどの堎合、2番目のオプションの方がより奜たしいでしょう `ValueStringBuffer`の堎合はそうでした。 「StackOverflowException」を取埗するずいう点でより安党です。



stackallocの結論



それでは、 `stackalloc`を䜿甚する最良の方法ずその方法は䜕ですか





このアロケヌタヌを䜿甚するず、アプリケヌションのパフォヌマンスを倧幅に改善できたす。



本党䜓ぞのリンク












All Articles