使用stackalloc分配的内存初始化

fru*_*ela 9 c# unsafe stackalloc

如果我stackallocC#中分配内存,那个内存是否已初始化(带0)
文档没有提到这一点,只说明保留了正确的金额.

在我的测试中,这样的内存默认为0,但这并不意味着它得到了保证.

use*_*016 14

从规格:

18.8堆栈分配

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

  • 现在 stackalloc 可以在 Span<T> 的“安全”上下文中使用,这仍然成立吗? (3认同)

Jes*_*pez 9

是的,规范说它是未定义的,但编译器locallocstackalloc. 这就是 ECMA 规范的规定localloc

localloc 指令从本地动态内存池中分配 size (native unsigned int 类型)字节,并返回第一个分配字节的地址(托管指针,类型 &)。仅当方法上的初始化标志为 true 时,返回的内存块才会初始化为 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 "";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

未初始化的方法比初始化的方法快五倍。