Cómo funcionan las Swiss Tables en el map nativo de Go
La versión 1.24 de Go, el lenguaje de programación desarrollado por Google, sustituyó la implementación interna de sus mapas (maps) por un diseño basado en Swiss Tables, una técnica de tablas hash que optimiza la velocidad de acceso a datos. El cambio, anunciado por el equipo oficial de Go en su blog bajo el título "Faster Go maps with Swiss Tables" y explicado en profundidad por el equipo de VictoriaMetrics en su publicación "How Swiss Tables Work in Go's Built-in Map", supone una mejora significativa de rendimiento respecto al sistema anterior.
Qué es un map en Go
Un map es la estructura de datos nativa de Go que almacena pares clave-valor. Se puede crear con un literal o mediante la función make, a la que opcionalmente se le puede pasar un tamaño estimado. Por ejemplo, make(map[string]int, 100) indica al runtime que reserve espacio para alrededor de 100 entradas. Según VictoriaMetrics, con esa pista Go crea inicialmente 128 slots, capaces de contener 112 entradas antes de necesitar crecer, una operación costosa.
El valor pasado a make es solo una sugerencia: Go no expone la capacidad del map. len(m) devuelve el número de entradas y es O(1) gracias a que el runtime lleva la cuenta en el campo used de la estructura interna Map. En cambio, cap no funciona con maps.
Las operaciones básicas —asignación, búsqueda y borrado— no cambian entre versiones. La búsqueda de una clave inexistente devuelve el valor cero del tipo, lo que puede generar ambigüedad con claves almacenadas cuyo valor sea cero. Para resolverla existe la forma de doble valor (value, ok), donde ok indica si la clave existe.
Comportamiento de un map nulo
Un map sin inicializar vale nil. Sobre él, las operaciones de lectura, borrado, len y range son seguras. Sin embargo, escribir en un map nil provoca un panic. Esta regla, vigente en todas las versiones, se mantiene con Swiss Tables.
Reglas de tipos de clave
Las claves deben ser comparables porque internamente Go calcula un hash para localizar slots candidatos y luego compara con ==. Tipos como arrays de strings o interfaces son válidos, pero un valor concreto no comparable, como un slice, provoca panic al intentar calcular su hash.
La estructura interna: Map, grupo y bytes de control
Internamente, un map es un puntero a la estructura runtime/maps.Map. Esta contiene, entre otros campos, used (número de entradas), seed (semilla aleatoria única por map) y un puntero al directorio de grupos. Copiar el map copia el puntero, por lo que dos variables apuntan a la misma estructura subyacente.
La unidad mínima de almacenamiento es el grupo (group), capaz de almacenar hasta 8 pares clave-valor. Cada grupo tiene un byte de control por slot y los 8 bytes forman la palabra de control (control word). Cada byte describe el slot directamente debajo.
Para localizar una clave, Go calcula su hash y lo divide en dos partes en sistemas de 64 bits: H1 (57 bits superiores), que elige el grupo inicial, y H2 (7 bits inferiores), que se almacena en el byte de control cuando el slot está ocupado. El bit restante del byte de control puede usarse para marcadores como estado vacío o borrado.
La semilla aleatoria del map hace que dos maps con las mismas claves distribuyan los datos en posiciones distintas, lo que dificulta ataques de colisión y mejora la localidad de caché.
Implicaciones del cambio
La adopción de Swiss Tables es una evolución técnica relevante: los mapas son una de las estructuras más usadas en programas Go, y las mejoras en su rendimiento afectan a compilaciones, bases de datos, servidores web y prácticamente todo el ecosistema. Aunque el cambio es interno y no altera la API pública, los desarrolladores obtienen automáticamente búsquedas, inserciones y eliminaciones más rápidas sin modificar su código.
Qué esperar
Go 1.24 marca el inicio de esta nueva era para los maps. El equipo de Go también experimenta con un diseño de grupo dividido (split group layout) que separa los arrays de claves y valores para mejorar la localidad de caché durante las búsquedas. Aún está en pruebas, pero podría llegar en versiones futuras.
Para los desarrolladores, el mensaje es claro: el lenguaje evoluciona por dentro sin romper por fuera. Basta con actualizar a Go 1.24 para beneficiarse de las Swiss Tables, mientras la documentación y los análisis de la comunidad, como los de VictoriaMetrics y el blog oficial de Go, facilitan entender qué ocurre bajo el capó.
