Практическое использование ключевого слова `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);