7,8 MB kluczy, które zaalokowałem tylko po to, by je wyrzucić

Profiler ujawnił ukryty koszt w moim kodzie.

Mój handler żądań odczytuje tokeny z ogromnego ciągu znaków i sumuje ich wagi. Logika działała, ale pamięć nie.

Wewnątrz gorącej pętli zaalokowałem 7,8 MB ciągów znaków — jeden nowy klucz dla każdego wyszukiwania w słowniku — a następnie natychmiast go wyrzuciłem.

Winowajcą był Substring. Każdy wycinek tworzył nowy obiekt typu string.

200 000 tokenów

  • Metoda A (Substring): 23,4 ms, zaalokowano 7 812 KB.
  • Metoda B (Span Lookup): 14,9 ms, zaalokowano 0 KB.

Metoda B działa 1,5× szybciej i nie generuje śmieci.

W .NET 9 możesz wywołać GetAlternateLookup. Zamiast nowego ciągu znaków przekazujesz ReadOnlySpan<char>, który jest jedynie „oknem” na oryginalny ciąg — bez kopiowania.

Słownik haszuje span bezpośrednio względem istniejących kluczy, dostarczając ten sam wynik bez alokacji.

O czym warto pamiętać

  • Działa z StringComparer.Ordinal oraz StringComparer.OrdinalIgnoreCase.
  • Rzuca wyjątkiem w czasie wykonywania, jeśli użyjesz własnego porównywacza, który nie obsługuje mechanizmu alternate lookup.
  • Idealne dla ścieżek krytycznych (hot paths), takich jak parserzy, procesory logów czy skanery CSV.
  • Pomiń to w przypadku prostych wyszukiwań z zaledwie kilkoma kluczami.

Teraz tropię w swoim kodzie wzorzec Substring po którym następuje TryGetValue. Ten wzorzec marnuje ogromne ilości pamięci.

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