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