`stackalloc`キーワードの活用


134

stackallocC#でのプログラミング中に実際に使用した人はいますか?何が行われているのかはわかっていますが、コードに表示されるのは偶然staticです。たとえば、を入力し始めるとIntellisenseが示唆するためです。

これはの使用シナリオとは関係ありませんがstackalloc、実際にはアプリでかなりの量のレガシー相互運用を行っているため、時々unsafeコードを使用することに頼ることができました。しかし、それでも私は通常、unsafe完全に回避する方法を見つけます。

また、.Netの単一スレッドのスタックサイズは〜1Mb(間違っている場合は修正してください)であるため、を使用することはできませんstackalloc

「これは私が危険にさらして使用するためのデータと処理の正確な量です」と言うことができるいくつかの実際的なケースはありますstackallocか?


5
ちょうどそれに気づいたSystem.Numbersことは、多くの用途をreferencesource.microsoft.com/#mscorlib/system/...
Slai

回答:


150

使用する唯一の理由stackallocは、パフォーマンス(計算または相互運用のいずれか)です。stackallocヒープに割り当てられた配列の代わりに使用することで、GCのプレッシャーが少なくなり(GCの実行が少なくなります)、配列を固定する必要がなく、ヒープの配列よりも割り当てが高速で、メソッドで自動的に解放されます出口(ヒープに割り当てられた配列は、GCの実行時にのみ割り当て解除されます)。またstackalloc、ネイティブのアロケーター(mallocや.Netの同等物など)の代わりに使用すると、スコープの終了時に速度と自動割り当て解除も得られます。

パフォーマンスstackallocに関しては、使用すると、データの局所性により、CPUでキャッシュヒットの可能性が大幅に増加します。


26
データの局所性、良い点!これは、複数の構造体または配列を割り当てるときに、マネージメモリがめったに達成しないことです。ありがとう!
Groo、

22
トラバースする空きリストがないため、ヒープ割り当ては通常、管理対象オブジェクトの方が非管理対象オブジェクトよりも高速です。CLRはヒープポインタをインクリメントするだけです。局所性については、ヒープの圧縮により、順次割り当ては、長時間実行される管理対象プロセスに対して同じ場所に配置される可能性が高くなります。
シェ

1
「ヒープ配列よりも割り当てる方が速い」なぜですか?地方だけ?どちらにしても、それは単なるポインタバンプです。
マックスBarraclough

2
@MaxBarracloughアプリケーションの存続期間にわたってヒープ割り当てにGCコストを追加するため。総割り当てコスト=割り当て+割り当て解除、この場合は、ポインターバンプ+ GCヒープ、vsポインターバンプ+ポインターデクリメントスタック
Pop Catalin

35

私は、stackallocを使用して、[ほぼ]リアルタイムのDSP作業用にバッファーを割り当てました。これは、パフォーマンスをできる限り一貫させる必要がある非常に特殊なケースでした。一貫性と全体的なスループットには違いがあることに注意してください。この場合、プログラムのその時点でのガベージコレクションの非決定性だけで、ヒープ割り当てが遅すぎることは気になりませんでした。99%のケースでは使用しません。


25

stackalloc安全でないコードにのみ関連します。マネージコードの場合、データを割り当てる場所を決定することはできません。値の型は、デフォルトでスタックに割り当てられます(参照型の一部でない場合は、ヒープに割り当てられます)。参照型はヒープに割り当てられます。

単純なバニラ.NETアプリケーションのデフォルトのスタックサイズは1 MBですが、これはPEヘッダーで変更できます。スレッドを明示的に開始する場合は、コンストラクターオーバーロードを使用して別のサイズを設定することもできます。ASP.NETアプリケーションの場合、デフォルトのスタックサイズは256Kのみです。これは、2つの環境を切り替える場合に注意する必要があることです。


Visual Studioからデフォルトのスタックサイズを変更することはできますか?
コンフィギュレータ

@configurator:私が知る限りでは。
ブライアンラスムッセン

17

スパンのStackalloc初期化。以前のバージョンのC#では、stackallocの結果はポインターローカル変数にのみ格納できました。C#7.2以降、stackallocは式の一部として使用でき、スパンを対象とすることができます。これは、unsafeキーワードを使用せずに実行できます。したがって、書く代わりに

Span<byte> bytes;
unsafe
{
  byte* tmp = stackalloc byte[length];
  bytes = new Span<byte>(tmp, length);
}

あなたは簡単に書くことができます:

Span<byte> bytes = stackalloc byte[length];

これは、操作を実行するためにスクラッチ領域が必要だが、比較的小さなサイズにヒープメモリを割り当てたくない場合にも非常に役立ちます。

Span<byte> bytes = length <= 128 ? stackalloc byte[length] : new byte[length];
... // Code that operates on the Span<byte>

出典: C#-All About Span:新しい.NETメインステイの探索


4
先端をありがとう。C#の新しいバージョンはそれぞれC ++に少し近づいているようですが、これは実際には良いことです。
Groo

1
ここここでわかるSpanように、.NETフレームワーク4.7.2では使用できません。4.8でも使用できません。そのため、今のところ、新しい言語機能の使用は制限されています。
フレデリック

2

この質問にはいくつかの素晴らしい答えがありますが、私はそれを指摘したいだけです

StackallocはネイティブAPIの呼び出しにも使用できます

多くのネイティブ関数は、呼び出し側が戻り結果を取得するためにバッファを割り当てる必要があります。たとえば、CfGetPlaceholderInfo関数にcfapi.hは次のシグネチャがあります。

HRESULT CfGetPlaceholderInfo(
HANDLE                    FileHandle,
CF_PLACEHOLDER_INFO_CLASS InfoClass,
PVOID                     InfoBuffer,
DWORD                     InfoBufferLength,
PDWORD                    ReturnedLength);

相互運用機能を介してC#で呼び出すには、

[DllImport("Cfapi.dll")]
public static unsafe extern HResult CfGetPlaceholderInfo(IntPtr fileHandle, uint infoClass, void* infoBuffer, uint infoBufferLength, out uint returnedLength);

あなたはstackallocを利用することができます。

byte* buffer = stackalloc byte[1024];
CfGetPlaceholderInfo(fileHandle, 0, buffer, 1024, out var returnedLength);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.