SearchValues
Почему появился SearchValues
.NET 8 представил SearchValues
Важные бенчмарки
В тестах, вызвавших дискуссию, использовался 32-мегабайтный буфер, содержащий пять символов-разделителей. На .NET 10 были замерены три подхода:
- IndexOfAny(char[]) – 10,2 мс
- Ручной цикл foreach по span – 19,4 мс
- SearchValues
– 9,7 мс
SearchValues обошел встроенный IndexOfAny всего на полмиллисекунды. Результат не является тем самым драматичным «пятикратным ускорением», о котором пишут на форумах; современный .NET уже оптимизирует IndexOfAny для небольших наборов, сокращая разрыв.
Когда разделители встречаются редко — например, один раз на каждые 40 КБ вместо каждых 60 байт — картина меняется:
- IndexOfAny(char[]) – 5,6 мс
- SearchValues
– 3,3 мс
Теперь SearchValues в 1,7 раза быстрее, потому что движок стремительно проходит через длинные последовательности несовпадающих данных с помощью векторизованных шагов, в то время как IndexOfAny переходит на менее агрессивный путь.
Скрытая цена
SearchValues не бесплатен. Создание объекта выделяет память и строит внутренние таблицы поиска. Если вы создаете его при каждом вызове метода, накладные расходы перекроют любую выгоду в производительности. Бенчмарк, вызывавший процедуру сканирования миллион раз на коротких строках, показал:
- Статический (повторно используемый) SearchValues – всего 25,1 мс
- SearchValues при каждом вызове – всего 70,2 мс
Создание объекта при каждом вызове делает всю операцию в три раза медленнее, чем если бы вы вообще не использовали SearchValues. Сохраняйте экземпляр в поле static readonly или используйте его повторно при вызовах.
Когда стоит использовать SearchValues
- Большие входные данные, мало совпадений – Векторизованный путь раскрывается во всей красе, когда сканер может пропускать длинные участки, не встречая разделитель.
- Повторные сканирования с тем же набором – Если одни и те же символы ищутся многократно, единоразовая настройка окупается.
Когда лучше остаться на IndexOfAny
- Короткие строки или много совпадений – Дополнительные затраты на настройку перевешивают незначительный прирост скорости.
- Код, который уже использует IndexOfAny – Реализация в современном .NET уже отлично оптимизирована для небольших наборов символов, поэтому переход на SearchValues может не дать заметного эффекта.
Распространенные ошибки
- Создание объекта внутри горячих циклов – Это приводит к трехкратному замедлению, показанному выше.
- Попытки «перехитрить» среду выполнения с помощью ручных циклов – Версия с ручным foreach оказалась в два раза медленнее обоих встроенных методов, что подтверждает: библиотеки .NET трудно превзойти без глубоких знаний инструкций SIMD.
- Предположение об универсальном ускорении – Выигрыш в полмиллисекунды на плотных данных доказывает, что SearchValues — это не универсальный чит-код; его преимущества зависят от характера данных.
Итог
SearchValues
Исходный код и полный набор тестов: https://dev.to/ssukhpinder/when-searchvalues-actually-pays-off-310l
