C#stackalloc
でメモリを割り当てている場合、そのメモリは(で)初期化されていますか?
ドキュメントはそれについては述べておらず、正しい金額が予約されていることを示しているだけです。0
私のテストでは、そのようなメモリはデフォルトでに設定されていまし0
たが、それが保証されているわけではありません。
C#stackalloc
でメモリを割り当てている場合、そのメモリは(で)初期化されていますか?
ドキュメントはそれについては述べておらず、正しい金額が予約されていることを示しているだけです。0
私のテストでは、そのようなメモリはデフォルトでに設定されていまし0
たが、それが保証されているわけではありません。
仕様から:
18.8スタック割り当て
新しく割り当てられたメモリの内容は未定義です。
はい、仕様には未定義と記載されていますが、コンパイラーはのlocalloc
CIL命令を発行しstackalloc
ます。そしてこれはECMAスペックが言っていることlocalloc
です:
localloc命令は、ローカル動的メモリプールからサイズ(タイプnative unsigned int)バイトを割り当て、最初に割り当てられたバイトのアドレス(マネージポインター、タイプ&)を返します。返されるメモリのブロックは、メソッドの初期化フラグがtrueの場合にのみ0に初期化されます(パーティションIを参照)。メモリの領域が新しく割り当てられます。現在のメソッドが戻ると、ローカルメモリプールを再利用できます。
初期化フラグ(フラグとも呼ばれlocalsinit
ます)は、検証可能なコードに必要なため、コンパイラーによってすべてのメソッドに対して発行されます。
stackallocのメモリのゼロ化を停止するように要求するcoreclrのこの問題を確認してください。問題の終わりに、jkotasは次のように述べています。
現在の計画は次のとおりです。
C#は、デフォルトでゼロの初期化を維持します。デフォルトを変更するのは大変です。JITによって行われるゼロ初期化をより効率的にするか、その必要性を減らすために、一連の問題が開かれています(#13827、#13823、#13825)ゼロ初期化を回避することによってパフォーマンスの最後のビットを本当に取得したい人は、彼らが何をしているのかを知っているときのカスタムILLinkerステップ(mono / linker#159)。今日はCoreLibに対してこれを行い(VMハックを介して、ただしILLinkerに切り替える必要があります)、CoreFX(dotnet / corefx#25956)でこれを試す予定です。これらの実験の結果に基づいて、将来的にこれを行うためのより合理化された方法を導入することを検討する可能性があります。@ahsonkhan役立つと思われる場合は、CoreFXLabでも実験することを検討してください。
そして、このcsharplangの提案を参照してください
したがって、結論は次のとおりです。メモリは実際にはゼロに初期化されます
ただし、コンパイラにはフラグの発行を妨げるバグ/機能がありlocalsinit
ます。他の変数を宣言せず、スタックに割り当てられた変数を他のメソッドに渡すためだけに使用する安全でないメソッドは、localsinit
フラグでマークされません。
このようなバグ/機能の例を次に示します。
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace InformalTests
{
class Program
{
const int n = 100_000_000;
static unsafe void Main(string[] args)
{
var watch = Stopwatch.StartNew();
for (int i =0; i < n; i++)
{
ThisMethodDoes_NOT_InitializeStackAllocatedMemory();
}
watch.Stop();
Console.WriteLine($"NOT INITIALIZED elapsed time {watch.Elapsed}");
watch.Restart();
for (int i = 0; i < n; i++)
{
ThisMethodInitializeStackAllocatedMemory();
}
watch.Stop();
Console.WriteLine($"INITIALIZED Elapsed time {watch.Elapsed}");
}
private static unsafe string ThisMethodDoes_NOT_InitializeStackAllocatedMemory()
{
// avoid declaring other local vars, or doing work with stackalloc
// to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279
char* pointer = stackalloc char[256];
return CreateString(pointer, 256);
}
private static unsafe string ThisMethodInitializeStackAllocatedMemory()
{
//Declaring a variable other than the stackallocated, causes
//compiler to emit .locals int cil flag, so it's slower
int i = 256;
char* pointer = stackalloc char[256];
return CreateString(pointer, i);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe string CreateString(char* pointer, int length)
{
return "";
}
}
}
初期化されていないメソッドは、初期化されたメソッドより5倍高速です。