Pull to refresh

Comments 16

В Ignite.NET использование stackalloc для передачи JNI аргументов (varargs / va_list) вместо params JavaValue[] подняло перформанс реальных кэшовых операций на 15% и избавило от лишних аллокаций / GC.


Код на гитхабе: UnmanagedUtils.cs


Ещё пример, перетасовка байт: WriteGuidSlow

Спасибо, сделаю отсылку на вариант использования

Думаю, что stackalloc используется так редко, потому что либо размер буфера предопределён и данные структурированы, тогда просто заполняется struct и берётся указатель, либо размер буфера непредсказуем.

необходимо тщательно проверять работу тех методов, в которые уходит ссылка
(void *) т.к.
О, за решётку в буханке протянули двуствольный ногострел!

                int* buff = stackalloc int[Random.Next(100500)];
                if (buff == null) RainbowFire();


А сколько памяти так можно выделить?

Сколько в стеке свободного места есть. Размер стека по умолчанию на винде 1 или 4 метра (32/64 бит), на Линухе по-разному, частый вариант — 8 метров.


Ну и часть этого места наверняка занята самим стеком вызовов.

Хочу отдельно отметить, что стиль изложения в этой части лучше. Текст гораздо легче читается.

Для C/C++ это массивы переменной длинны и alloca. (Но в основном их все ругают.)
Я бы сказал, что это C#-овская замена прежде всего для обычных (фиксированной длины) массивов C/C++ (которые чаще располагаются на стеке). Массивы переменной длины и alloca — это уже следующий уровень.
Может кто-то подскажет, есть ли подобные книги с качественным материалом на английском? Помимо Рихтера.
Under the hood of .NET memory management. Однако все основное — размазано по блогам. У Matt Warren, кстати, недавно вышел пост — собрная солянка mattwarren.org/2018/01/22/Resources-for-Learning-about-.NET-Internals как раз по теме вашего вопроса. Но (!) нужно аккуратно смотреть на год каждой статьи. Все-таки все переписывается и меняется. Многое уже не актуально.

Плюс есть еще отдельные личности, которые ведут видеоблоги. Как, например, Immo Landwerth из команды ядра

Иногда (например в этом докладе) stackalloc не рекомендуют использовать, говоря, что он заполняет выделенную память нулями, и из-за этого работает медленно. На гитхабе coreclr эта тема тоже обсуждалась, и там есть пример, когда stackalloc не заполняет память нулями (я немного модифицировал этот пример):


const int Size = 16384;

static unsafe void Main() {
    Foo();
    byte* p = stackalloc byte[Size];
    Console.WriteLine(p[0]);
}

static unsafe void Foo() {
    byte* p = stackalloc byte[Size];
    for (int i = 0; i < Size; i++)
        p[i] = 42;
}

Если в этом коде менять Size на разные значения — можно получить разный результат. На моей машине с .NET Framework 4.7.1 результаты такие:
RyuJit x64:


  • Size: 1-48, значение: 0
  • Size: 49-64 — «случайное» число,
  • Size >=65 — 42

LegacyJit x86:


  • Size 1-24 — 0
  • Size >=25 — 42

При этом в машинном коде Foo заполнение нулями присутствует.


Отсюда появляются вопросы — в каких случаях при использовании stackalloc память обнуляется, как это влияет на производительность, и зачем вообще нужно обнуление, если на него нельзя полагаться?

У меня было предположение по этому вопросу что должны быть оптимизации на этот счет. Например, есть JIT видит что сначала идет запись, а потом — чтение, то смысла обнулять нет вообще. Но пока не успел заняться этим вопросом
Sign up to leave a comment.