7,8 МБ ключей, которые я выделил только для того, чтобы выбросить
Профайлер выявил скрытые издержки в моем коде.
Мой обработчик запросов считывает токены из огромной строки и суммирует их веса. Логика работала, но с памятью возникли проблемы.
Внутри «горячего» цикла я выделял 7,8 МБ строк — по одному новому ключу на каждый поиск в словаре — а затем сразу же выбрасывал этот ключ.
Виновником был Substring. Каждый срез создавал новый объект строки.
200 000 токенов
- Метод A (Substring): 23,4 мс, выделено 7 812 КБ.
- Метод B (Span Lookup): 14,9 мс, выделено 0 КБ.
Метод B работает в 1,5 раза быстрее и не создает мусора.
В .NET 9 вы можете вызвать GetAlternateLookup. Вместо новой строки вы передаете ReadOnlySpan<char>, который является лишь «окном» в исходную строку — без копирования.
Словарь вычисляет хеш span напрямую для сопоставления с существующими ключами, выдавая тот же результат без выделения памяти.
Что нужно помнить
- Работает с
StringComparer.OrdinalиStringComparer.OrdinalIgnoreCase. - Вызывает исключение во время выполнения, если вы используете пользовательский компаратор, не поддерживающий альтернативный поиск (alternate lookup).
- Идеально подходит для «горячих» путей, таких как парсеры, обработчики логов или сканеры CSV.
- Не используйте это для простых поисков с небольшим количеством ключей.
Теперь я выискиваю в своем коде сочетание Substring, за которым следует TryGetValue. Такой паттерн тратит огромное количество памяти.
Source: https://dev.to/ssukhpinder/78-mb-of-keys-i-allocated-just-to-throw-away-50mn
Optional learning community: https://github.com/ssukhpinder/dev-to-code-samples/tree/main/023-dictionary-alternate-lookup
