El desafío de mil millones de filas en Go: de 1m45s a 4s en nueve soluciones

Optimizar código en Go para el “1 Billion Row Challenge” — analizar un archivo de texto de 13GB y 1.000 millones de líneas con lecturas de temperatura — muestra hasta dónde pueden llevar a un programa sencillo el perfilado cuidadoso, las estructuras de datos personalizadas y la paralelización, pasando de casi dos minutos a unos cuatro segundos. Los comentaristas comparan estos resultados en Go con enfoques muy afinados en Java, .NET, C++, Rust e incluso con bases de datos y GPUs, señalando que los toolchains JIT/AOT de JVM y .NET y el soporte SIMD pueden lograr tiempos aún menores a costa de código extremo y poco idiomático. Muchos subrayan que, aunque estos microbenchmarks son una forma divertida de explorar los límites del rendimiento, las soluciones del mundo real deben equilibrar velocidad bruta con simplicidad, robustez, portabilidad y las capacidades de bibliotecas y bases de datos de más alto nivel.

Comparaciones de rendimiento entre lenguajes

  • El hilo gira en torno a por qué las soluciones más rápidas en Java y C#/.NET superan a las más rápidas en Go, a veces por factores de 2 a 4 veces.
  • Algunos sostienen que Java y .NET se benefician de un JIT/AOT muy agresivo, intrínsecos SIMD, PGO y décadas de ajuste del runtime.
  • Otros replican que Go, en principio, puede usar trucos algorítmicos similares; la brecha se debe más al esfuerzo de implementación que a una imposibilidad inherente.
  • Hay debate sobre las afirmaciones de que el JVM puede “a menudo superar a C”; los escépticos sostienen que eso solo es cierto en casos seleccionados.
  • Se mencionan Rust, C++, Swift, Dart, Node.js e incluso R a través de otros leaderboards o ejemplos, pero las comparaciones se complican por el distinto hardware.

Técnicas de optimización y estructuras de datos

  • Trucos clave: tablas hash personalizadas, IO con memory-mapped, análisis de temperaturas solo con enteros, desenrollado de bucles y uso cuidadoso de acceso inseguro a memoria.
  • Se discuten posibles mejoras adicionales: arrays asignados en la pila, reducir copy(), tries tempranos para nombres de estaciones, tablas de búsqueda para temperaturas y hash perfecto.
  • Varios participantes dudan de que grandes LUTs vayan a vencer a la aritmética simple debido a los costes de caché y latencia de memoria.

IO, caché y advertencias sobre benchmarking

  • Muchos señalan que las ejecuciones repetidas mantienen el archivo de 13GB en la caché del sistema operativo o en un disco RAM, así que el ancho de banda del disco no es el cuello de botella.
  • Las preguntas sobre soluciones en Java que aparentemente superan el rendimiento de un SSD se responden con caché y sistemas de archivos en RAM.
  • Algunos comentan que la paralelización ofrece grandes ganancias, pero no es directamente comparable con herramientas monohilo como cat.

Librerías, bases de datos, GPUs y herramientas de alto nivel

  • La gente prueba o propone Polars, DuckDB, BigQuery, bases de datos SQL e incluso GPUs; estas pueden llegar a decenas de segundos o unos pocos minutos con código de nivel mucho más alto.
  • Algunos argumentan que, para sistemas reales, hacer más trabajo dentro de bases de datos puede ser competitivo y operativamente más simple que escribir código a mano.

Relevancia en el mundo real y notas específicas de Go

  • Varios enfatizan que el ejercicio es un “juego”: el código real necesitaría análisis robusto y manejo de errores, sacrificando velocidad por corrección.
  • Otros se centran en las limitaciones del compilador de Go (historia débil con SIMD, PGO modesto, inlining menos agresivo) frente a toolchains de Java/.NET más maduras, aunque señalan las fortalezas de Go en simplicidad y arranque rápido.
  • Se destaca como útil, aunque poco idiomático, perfilar con pprof de Go y técnicas como desactivar el GC, fijar hilos del sistema operativo y usar punteros unsafe.