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.OrdinalorazStringComparer.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
