7,8 MB de claves que asigné solo para desechar

Un profiler reveló un coste oculto en mi código.

Mi manejador de peticiones lee tokens de una cadena enorme y suma sus pesos. La lógica funcionaba, pero la memoria no.

Dentro de un bucle crítico, asigné 7,8 MB de cadenas —una clave nueva para cada búsqueda en el diccionario— y luego deseché la clave inmediatamente.

El culpable era Substring. Cada fragmento creaba un nuevo objeto de cadena.

200.000 tokens

  • Método A (Substring): 23,4 ms, 7.812 KB asignados.
  • Método B (Span Lookup): 14,9 ms, 0 KB asignados.

El método B es 1,5 veces más rápido y no genera basura.

En .NET 9 puedes llamar a GetAlternateLookup. En lugar de una nueva cadena, pasas un ReadOnlySpan<char>, que es simplemente una ventana a la cadena original, sin copias.

El diccionario calcula el hash del span directamente contra las claves existentes, ofreciendo el mismo resultado sin la asignación de memoria.

Cosas a tener en cuenta

  • Funciona con StringComparer.Ordinal y StringComparer.OrdinalIgnoreCase.
  • Lanza una excepción en tiempo de ejecución si utilizas un comparador personalizado que carezca de soporte para búsquedas alternativas.
  • Ideal para rutas críticas (hot paths) como analizadores (parsers), procesadores de logs o escáneres de CSV.
  • No lo uses para búsquedas sencillas con solo unas pocas claves.

Ahora busco en mi código el patrón Substring seguido de TryGetValue. Ese patrón desperdicia cantidades masivas de memoria.

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