.NET 8: Optimización de búsquedas con SearchValues y FrozenCollections en rutas calientes
.NET 8 introduce SearchValues<T> y colecciones como FrozenSet<T>/FrozenDictionary<TKey, TValue>, que reducen drásticamente la sobrecarga en rutas calientes. Estas estructuras hacen el trabajo pesado durante la creación, ofreciendo búsquedas en tiempo constante sin análisis repetidos. Son ideales para aplicaciones de alto rendimiento que manejan millones de operaciones.
Acelerando la búsqueda de caracteres con SearchValues
El método estándar string.IndexOfAny reanaliza el conjunto de caracteres en cada llamada, acumulando sobrecarga en bucles. SearchValues<char> se crea una sola vez y almacena en caché la estrategia de búsqueda óptima.
Durante la inicialización, .NET realiza:
- Análisis del conjunto de entrada (carácter único, rango o varios caracteres).
- Selección de algoritmo con soporte SIMD (AVX2, SSE2).
- Construcción de estructuras de datos (máscaras de bits, tablas hash).
SearchValues<char> separadores = SearchValues.Create(".,;!?");
ReadOnlySpan<char> entrada = "¡Hola, mundo!";
int indice = entrada.IndexOfAny(separadores);
Las estrategias de búsqueda varían según los datos:
- Carácter único — verificación vectorizada.
- Rango (0-9A-Z) — máscara de bits O(1).
- Conjunto pequeño (≤4 caracteres) — SIMD.
- Conjunto grande — tabla hash optimizada.
Los benchmarks muestran ganancias masivas:
| Escenario | IndexOfAny | SearchValues | Mejora |
|-----------|------------|--------------|--------|
| Cadena corta | ~5 ns | ~5 ns | 1x |
| Cadena larga (2000 caracteres) | ~52 ns | ~4 ns | ~13x |
Las mayores mejoras se dan con cadenas de más de 100 caracteres, evitando análisis repetidos.
FrozenCollections: Tablas hash inmutables
HashSet<T> y Dictionary<TKey, TValue> tienen sobrecarga por mutaciones como rehashing y verificaciones de límites. FrozenSet<T> y FrozenDictionary<TKey, TValue> están optimizados para casos de solo lectura.
Durante la creación:
- Análisis de claves para una función hash perfecta sin colisiones.
- Distribución de datos amigable con la caché.
- Eliminación de verificaciones de inmutabilidad en tiempo de ejecución.
Las colecciones pequeñas usan máscaras de bits en lugar de hash.
var diccionario = Enumerable.Range(0, 100000)
.ToFrozenDictionary(x => x, x => $"Valor {{x}}");
if (diccionario.TryGetValue(42, out string? valor))
{
Console.WriteLine(valor);
}
Benchmarks (100.000 elementos):
| Operación | Dictionary | HashSet | FrozenDictionary | FrozenSet |
|-----------|------------|---------|------------------|-----------|
| TryGetValue/Contains | 3.78 ns | 4.47 ns | 1.87 ns | 3.37 ns |
FrozenDictionary es dos veces más rápido que un Dictionary estándar.
Casos de uso reales
SearchValues:
- Millones de búsquedas contra conjuntos fijos.
- Análisis de logs, tokenización, validación.
- Cadenas largas (>100 caracteres).
FrozenCollections:
- Configuraciones estáticas cargadas al inicio.
- Tablas de búsqueda (códigos de error, estados).
- Cargas de trabajo con miles de lecturas.
Evítalas en mutaciones frecuentes o llamadas raras: el costo de creación supera los beneficios.
Implementación práctica
Almacena en caché a nivel de aplicación:
public static class CacheApp
{
public static readonly SearchValues<char> CharsUrlSeguros =
SearchValues.Create("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~");
public static readonly FrozenSet<string> MonedasValidas =
new[] { "USD", "EUR", "GBP", "JPY", "CNY" }.ToFrozenSet();
}
Ejemplos de validación:
- Email:
SearchValues<char> charsEmailInvalidos = SearchValues.Create("()<>[]:;@\\,?\""); - TLD:
FrozenSet<string> tldsValidos = CargarTldsDeConfig().ToFrozenSet();
Siempre perfila con BenchmarkDotNet: el beneficio depende del volumen de operaciones.
Conclusiones clave
SearchValuesacelera búsquedas 13x en cadenas largas con optimización única.FrozenDictionaryduplica el rendimiento deTryGetValuefrente aDictionary.- Ambas brillan con conjuntos inmutables en rutas calientes.
- Perfila primero: inútiles para llamadas infrecuentes.
- Aprovecha SIMD y hash perfecto para victorias de devs intermedios/seniors.
— Editorial Team
Aún no hay comentarios.