如果我stackalloc
在C#中分配内存,该内存是否已初始化(使用0
)?
文档没有提到这一点,只是告诉我们保留了正确的金额。
在我的测试中,这种内存默认为0
,但这并不意味着它是有保证的。
如果我stackalloc
在C#中分配内存,该内存是否已初始化(使用0
)?
文档没有提到这一点,只是告诉我们保留了正确的金额。
在我的测试中,这种内存默认为0
,但这并不意味着它是有保证的。
从规范:
18.8 堆栈分配
新分配的内存的内容是未定义的。
是的,规范说它是未定义的,但编译器会发出localloc
CIL 指令stackalloc
。这就是 ECMA 规范所说的localloc
:
localloc 指令从本地动态内存池分配 size(类型为 native unsigned int)字节并返回第一个分配字节的地址(托管指针,类型 &)。仅当方法上的初始化标志为真时,返回的内存块才初始化为 0(请参阅分区 I)。内存区域是新分配的。当当前方法返回时,本地内存池可供重用。
初始化标志,也称为localsinit
标志,由编译器为每个方法发出,因为它是可验证代码所必需的。
请在 coreclr 上查看此问题,要求停止在 stackalloc 上将内存归零。在问题结束时,jkotas 说:
目前的计划是:
默认情况下,C# 将保持零初始化。更改默认设置太麻烦了。我们打开了一组问题,以使 JIT 完成的零初始化更有效或减少对它的需求(#13827、#13823、#13825)真正想要通过避免零初始化来获得最后一点性能的人可以使用当他们知道自己在做什么时,自定义 ILLinker 步骤 (mono/linker#159)。我们今天为 CoreLib 执行此操作(通过 VM hack,但我们应该切换到 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 "";
}
}
}
未初始化的方法比初始化的方法快五倍。