Практическое использование ключевого слова `stackalloc`

Кто-нибудь когда-либо использовал stackalloc во время программирования на С#? Я знаю, что происходит, но единственный раз, когда он появляется в моем коде, случайно, потому что Intellisense предлагает это, когда я начинаю набирать static, например.

Несмотря на то, что он не связан с сценариями использования stackalloc, я на самом деле выполняю значительное количество старых взаимодействий в своих приложениях, поэтому время от времени я могу использовать код unsafe. Но, тем не менее, я обычно нахожу способы полностью избежать unsafe.

И так как размер стека для одного потока в .Net составляет ~ 1 Мб (исправьте меня, если я ошибаюсь), я еще более зарезервирован от использования stackalloc.

Существуют ли какие-то практические случаи, когда можно было бы сказать: "Это именно то, что нужно для обработки данных и обработки для меня небезопасным и использовать stackalloc"?

Ответ 1

Единственной причиной использования stackalloc является производительность (либо для вычислений, либо для взаимодействия). Используя stackalloc вместо массива, выделенного кучей, вы создаете меньшее давление GC (GC должен работать меньше), вам не нужно связывать массивы, быстрее распределять, чем массив кучи, это автоматически освобождается при выходе метода (массивы, выделенные кучей, освобождаются только при запуске GC). Кроме того, используя stackalloc вместо собственного распределителя (например, malloc или эквивалент .Net), вы также получаете скорость и автоматическое освобождение при выводе области.

Производительность, если вы используете stackalloc, вы значительно увеличиваете вероятность попадания кеша на процессор из-за локальности данных.

Ответ 2

Я использовал stackalloc для выделения буферов для работы [DSP] в реальном времени. Это был очень конкретный случай, когда производительность должна была быть как можно более последовательной. Обратите внимание, что существует разница между консистенцией и общей пропускной способностью - в этом случае я не занимался слишком большим распределением кучи, просто с неопределенностью сбора мусора в этой точке программы. Я бы не использовал его в 99% случаев.

Ответ 3

stackalloc относится только к небезопасному коду. Для управляемого кода вы не можете решить, где распределить данные. Типы значений распределяются по стеку по умолчанию (если только они не являются частью ссылочного типа, и в этом случае они выделяются в куче). В куче выделяются ссылочные типы.

Размер стека по умолчанию для простого приложения .NET с .NET-платформой составляет 1 МБ, но вы можете изменить его в заголовке PE. Если вы прямо запускаете потоки, вы также можете установить другой размер через перегрузку конструктора. Для приложений ASP.NET размер стека по умолчанию составляет всего 256 тыс., Что следует помнить, если вы переключаетесь между двумя средами.

Ответ 4

Stackalloc инициализация пролетов. В предыдущих версиях С# результат stackalloc мог быть сохранен только в локальной переменной указателя. Начиная с С# 7.2, stackalloc теперь может использоваться как часть выражения и может предназначаться для диапазона, и это может быть сделано без использования ключевого слова unsafe. Таким образом, вместо записи

Span<byte> bytes;
unsafe
{
  byte* tmp = stackalloc byte[length];
  bytes = new Span<byte>(tmp, length);
}

Вы можете написать просто:

Span<byte> bytes = stackalloc byte[length];

Это также чрезвычайно полезно в ситуациях, когда вам нужно некоторое пространство для выполнения операции, но вы хотите избежать выделения памяти кучи для относительно небольших размеров

Span<byte> bytes = length <= 128 ? stackalloc byte[length] : new byte[length];
... // Code that operates on the Span<byte>

Источник: С# - All About Span: Изучение нового .NET Mainstay

Ответ 5

На этот вопрос есть несколько отличных ответов, но я просто хочу отметить, что

Stackalloc также можно использовать для вызова собственных API

Многие нативные функции требуют, чтобы вызывающая сторона выделяла буфер для получения возвращаемого результата. Например, функция CfGetPlaceholderInfo в cfapi.h имеет следующую подпись.

HRESULT CfGetPlaceholderInfo(
HANDLE                    FileHandle,
CF_PLACEHOLDER_INFO_CLASS InfoClass,
PVOID                     InfoBuffer,
DWORD                     InfoBufferLength,
PDWORD                    ReturnedLength);

Для вызова в С# через взаимодействие,

[DllImport("Cfapi.dll")]
public static unsafe extern HResult CfGetPlaceholderInfo(IntPtr fileHandle, uint infoClass, void* infoBuffer, uint infoBufferLength, out uint returnedLength);

Вы можете использовать stackalloc.

byte* buffer = stackalloc byte[1024];
CfGetPlaceholderInfo(fileHandle, 0, buffer, 1024, out var returnedLength);