使用 stackalloc 初始化分配的内存

Posted

技术标签:

【中文标题】使用 stackalloc 初始化分配的内存【英文标题】:Initialization of memory allocated with stackalloc 【发布时间】:2012-01-30 12:46:13 【问题描述】:

如果我在C# 中使用stackalloc 分配内存,该内存是否已初始化(使用0 文档没有提到这一点,只是告诉我们保留了正确的金额。

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

【问题讨论】:

【参考方案1】:

是的,规范说它是未定义的,但编译器会为stackalloc 发出localloc CIL 指令。这就是 ECMA 规范对 localloc 的评价:

localloc 指令分配大小(类型为 native unsigned int) 本地动态内存池中的字节并返回地址(a 第一个分配字节的托管指针,类型 &)。块 仅当在初始化标志上返回的内存被初始化为 0 方法为真(见第一部分)。内存区域是新的 分配。当前方法返回时本地内存池为 可重复使用。

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

请查看 coreclr 上的 this issue 要求停止在 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 "";
        
    

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

【讨论】:

【参考方案2】:

来自规范:

18.8 堆栈分配

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

【讨论】:

现在 stackalloc 可以在 Span 的“安全”上下文中使用,这仍然成立吗?

以上是关于使用 stackalloc 初始化分配的内存的主要内容,如果未能解决你的问题,请参考以下文章

分配初始化的、对齐的内存

C#struct最佳实践,可能存在矛盾吗?

控制内存分配

第十二章 类和动态内存分配

数组的引用变量和内存分配

数组及其在内存中的分配