TGViewer
C# Heppard C# Heppard @csharp_gepard · 1.65K subscribers
Post #137 2.6K
ByReferenceStringComparer #скорость

Недавно появилась задача быстро искать цифры по словарю. Ключ - строки, которые приходят извне, но которые всегда из определённого списка. Значение - число. В исходном коде на каждый запрос происходил поход в БД, создавался словарик, после этого по нему десятки (если не сотни) раз осуществлялся поиск по ключу.

Оптимизация первая. Ходить в БД за этими цифрами, очевидно, долго, да и данные обновляются редко, поэтому первая оптимизация - кэш через словарик на микросервисе. Это дало 50% прироста производительности. Неплохо. В принципе, заказчик уже был доволен, но мне хотелось большего.

Оптимизация вторая. Как я уже заметил, словарик, по ходу запроса, используется очень-очень часто. Это было отлично заметно в профайлере. Причём основное время тратилось на получение хэша и сравнение строк.

Я прогнал все входящие строки и все ключи из БД через штуку а-ля string.Intern. Таким образом я получил строки, идентичные по ссылке, что, кажется, должно облегчить сравнение строк. Я надеялся, что FrozenDictionary это заметит и применит какие-то оптимизации. Увы, нет. Тем не менее, я получил небольшой прирост производительности, когда перешёл на Frozen.

Однако, сравнение через ссылку не давало мне покоя. Я вспомнил, что некто Евгений рассказывал, что Serilog не эффективно использует кэш темплейтов для получения подготовленного сообщения. Мол, в качестве IEqualityComparer<string> для ключа словаря можно было бы сравнивать строки по ссылке, а хэш получать из заголовка инстанса строки. В принципе, для этого сценария у меня уже всё было готово, и я начал создавать Frozen с указанием вот этого компарера:

class ByRefStringComparer : IEqualityComparer<string>
{
public bool Equals(string? x, string? y)
{
return ReferenceEquals(x, y);
}

public int GetHashCode(string obj)
{
return RuntimeHelpers.GetHashCode(obj);
}
}


Не скажу, что последняя оптимизация внесла весомый вклад, но, тем не менее, она была достаточно полезная. Во всяком случае ещё 3-5% скорости могут помочь в ситуации высокой нагрузки.

Бенчмарк в комментариях.

Замечу, что подобный подход может помочь только (!) в случаях, когда набор строк ограничен (например, список названий областей страны) и по ним часто и много ищут. То есть не нужно использовать подход, если у вас разные строки, либо строка в алгоритме используется всего один раз. Пропуская все строки через аналог string.Intern вы просто переложите поиск строки из одного места в другое.
  • 👍 30
  • 🔥 3
More from @csharp_gepard
  1. Mar 13, 2026Ozon Tech Community .NET Meetup 24 марта, лофт Casa Picassa, 19:00 Ищем способы бороться с…
  2. Jan 20, 2026Ещё одна история про память #отдых Судя по комментариям специалистов и ценам на RAM в мага…
  3. Nov 25, 2025FastEnum #скорость Иногда нам нужно не просто быстро, а очень быстро. В этот момент в дело…
  4. Nov 20, 2025UnsafeAccessor #скорость Иногда возникает странное желание изменять приватные поля в класс…
  5. Nov 17, 2025Latency numbers every programmer should know Я всё время теряю эти циферки, особенно когда…
  6. Oct 29, 2025Dictionary.TryGetValue #скорость Уж сколько лет прошло и, вроде, все это знают. Но, тем не…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →