Мы часто используем
Span для ускорения сборки строк - создаём буфер на стеке, складываем туда кусочки будущей строки (чтобы не плодить промежуточные), а потом превращаем всё это в строку. Пример с ValueStringBuilder будет выглядеть следующим образом:public string MyMethod() {
Span<char> buffer = stackallock char[1024];
var vsb = new ValueStringBuilder(buffer);
...
}Это работает быстро, но можно сделать быстрее. В данном случае, местом ускорения будет создание буфера. В платформе есть правило - каждая переменная перед использованием должна быть инициализирована. В данном случае, платформа осуществляет инициализацию
Span , то есть заполняет его элементы значениями по умолчанию. Это достаточно ресурсоёмкая операция (см. Span против SpanSkipInit на бенчмарке), которую мы можем не делать, воспользовавшись атрибутом SkipLocalsInit. Атрибут
SkipLocalsInit, применённый к методу, заставляет компилятор не использовать инициализацию локальных переменных этого метода. Важно понимать, что это не безопасная штука, так как в созданной переменной могут содержаться значения из памяти, которые были в памяти до этого. Именно поэтому, применение атрибута SkipLocalsInit требует объявить всю сборку как unsafe.Я бы не стал использовать этот атрибут в обычном коде, но для библиотечного кода (который, надеюсь, обложен тестами) этот подход может весьма существенно ускорить работу с промежуточными буферами.
Также, немаловажно то, что чем больше буфер, тем более выгодно брать его из
ArrayPool. Это отчётливо видно на бенчмарке: буфер размером более 4096 char'ов уже стоит брать оттуда. Это понятнее для разработчиков и не требует применения unsafe.Больше подробностей о том, как работает инициализация в C# с
SkipLocalsInitAttribute, есть тут и тут.Код бенчмарка в комментариях.
P.S.: Если же мы хотим пропускать инициализацию модно и молодёжно, не помечая сборку как unsafe, мы должны использовать
Unsafe.SkipInit.P.P.S.: Ещё много информации про это у некого Сергея.
