Инициализация памяти, выделенной с помощью stackalloc - PullRequest
9 голосов
/ 30 декабря 2011

Если я выделяю память с stackalloc в C # , , это память инициализирована 0) ?
В документации об этом не говорится, а только говорится, что зарезервировано правильное количество.

В моих тестах такая память по умолчанию была 0, но это не значит, что она гарантирована.

Ответы [ 2 ]

14 голосов
/ 30 декабря 2011

Из спецификации:

18.8 Распределение стека

Содержимое вновь выделенной памяти не определено.

4 голосов
/ 06 ноября 2018

Да, спецификация говорит, что она не определена, но компилятор испускает localloc CIL intruction для stackalloc.И это то, что ECMA Specs говорит о localloc:

Команда localloc выделяет размер (тип native unsigned int) байтов из пула локальной динамической памяти и возвращает адрес (управляемый указатель, тип &) первого выделенного байта.Возвращенный блок памяти инициализируется равным 0, только если флаг инициализации метода имеет значение true (см. Раздел I).Область памяти вновь выделена.Когда текущий метод возвращает локальный пул памяти, доступен для повторного использования.

Флаг инициализации, также известный как флаг localsinit, генерируется компилятором для каждого метода, поскольку он требуется для проверяемого кода..

Пожалуйста, посмотрите на эту проблему на coreclr с просьбой прекратить обнуление памяти на stackalloc.В конце проблемы jkotas говорит:

Текущий план:

C # будет сохранять ноль при инициализации по умолчанию.Изменение значения по умолчанию будет слишком разрушительным.У нас есть ряд вопросов, открытых для того, чтобы JIT-инициализация, выполненная JIT, была более эффективной или уменьшить потребность в ней (# 13827, # 13823, # 13825). Люди, которые действительно хотят получить последний бит производительности, избегая нулевой инициализации, могут использоватьпользовательский шаг ILLinker (моно / линкер # 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 "";
        }
    }
}

Метод без инициализации в пять раз быстрее, чем инициализированный один.

...