Calculé el hash de cada palabra dos veces para contarla una sola vez
La semana pasada analicé el rendimiento de un analizador de registros (log parser). Cuenta códigos de error en un registro diario. La lógica principal es una simple actualización de un diccionario.
La mayoría de los desarrolladores lo escriben así:
if (counts.TryGetValue(code, out int c))
counts[code] = c + 1;
else
counts[code] = 1;
Este código calcula el hash de la clave dos veces. TryGetValue encuentra la entrada calculando el hash y recorriendo el bucket. Luego, el indexador realiza exactamente el mismo trabajo de nuevo para actualizar el valor. Desperdicias ciclos de CPU en la misma clave y el mismo hash dos veces por cada token.
Puedes saltarte el segundo paso. Usa CollectionsMarshal.GetValueRefOrAddDefault.
ref int slot = ref CollectionsMarshal.GetValueRefOrAddDefault(counts, code, out _);
slot++;
Este método encuentra o crea el espacio en un solo paso. Te proporciona una referencia directa al almacenamiento. Realizas un hash y un recorrido de bucket. Luego, mutas el valor in situ.
Probé esto con 5 millones de tokens.
Resultados: • TryGetValue + indexer: 160,0 ms • GetValueRefOrAddDefault: 95,0 ms
La versión de una sola búsqueda es 1,7 veces más rápida.
Fundamentalmente, las asignaciones de memoria se mantuvieron idénticas. Este truco no ahorra memoria. Solo ahorra CPU. Si tu código es lento debido a la recolección de basura (garbage collection), este cambio no hará nada. Si tu código es lento debido a un conteo intensivo, esto ayuda.
Usa esto cuando tu bucle realice muchas actualizaciones de claves existentes. El beneficio aumenta a medida que crece la proporción de actualizaciones respecto a las inserciones.
Una advertencia: La referencia apunta al almacenamiento interno del diccionario. Solo permanece válida hasta el próximo cambio estructural. No mantengas la referencia si añades o eliminas claves. Obtén la referencia, cambia el valor y continúa.
Usa TryGetValue para la mayoría de las tareas. Es más fácil de leer. Usa la versión con ref solo cuando el bucle de tu diccionario sea un cuello de botella en el rendimiento.
Fuente: https://dev.to/ssukhpinder/i-hashed-every-word-twice-to-count-it-once-1mg9
