初始化用stackalloc分配的内存

本文关键字:内存 分配 stackalloc 初始化 | 更新日期: 2023-09-27 18:19:09

如果我在 c# 中分配stackalloc的内存,是内存初始化(与0)?
文档中没有提到这一点,只告诉我们保留了正确的数量。

在我的测试中,这样的内存默认为0,但这并不意味着它是有保证的。

初始化用stackalloc分配的内存

From the spec:

18.8堆栈分配

新分配的内存内容未定义。

是的,规范说它是未定义的,但是编译器为stackalloc发出localloc CIL指令。以下是ECMA Specs对localloc的描述:

本地指令分配大小(类型为本机unsigned int)从本地动态内存池中取出字节,并返回地址(a托管指针,第一个分配字节的类型&)。块的初始化标志才会将返回的内存初始化为0方法为true(参见分区I)。该内存区域是新创建的分配。当当前方法返回时,本地内存池为可重复使用

初始化标志,也称为localsinit标志,由编译器为每个方法发出,因为它是可验证代码所必需的。

请查看coreclr请求停止在stackalloc上置零内存的问题。在问题的最后,jkotas说:

当前的计划是:

c#默认将初始化为零。更改默认值将太崩溃了。我们有一系列问题要解决由JIT完成的初始化更有效或减少了对它的需求(#13827, #13823, #13825)真的想要得到最后一点的人通过避免零初始化可以使用自定义ILLinker步骤(mono/link# 159),当他们知道他们在做什么。我们这样做今天的CoreLib(通过VM hack,但我们应该切换到ILLinker),我们计划在CoreFX (dotnet/CoreFX #25956)中进行实验。基于这些实验的结果,我们可以考虑引入一个更精简的方式来做到这一点在未来。@ahsonkhan你应该如果您相信,也可以考虑在CoreFXLab中进行试验那会有帮助的。

看这个夏普朗提案

所以结论是:内存在实际中被初始化为0

然而,在编译器中有一个bug/特性阻止发出localsinit标志。不声明其他变量并且使用堆栈分配的变量只是为了将其传递给其他方法的不安全方法不会被标记为localsinit标志。

下面是一个这样的bug/特性的例子:

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倍。