De lento a SIMD: una historia de optimización en Go
La optimización manual con SIMD en Go para operaciones como productos punto pone de relieve tanto el potencial de rendimiento como las limitaciones actuales del lenguaje para la computación numérica de alto rendimiento. Los comentaristas contrastan la falta de autovectorización de Go y de abstracciones SIMD ergonómicas con ecosistemas como C++, Rust, C#, y Java, y debaten si los intrínsecos explícitos, el ensamblador o el FFI hacia bibliotecas estilo C/BLAS son la vía más práctica. El hilo también plantea compensaciones más amplias entre el escalado vertical mediante optimización de bajo nivel, el escalado horizontal entre más máquinas y los costes de mantenimiento y portabilidad de depender de ensamblador o cgo.
Compilador de Go, SIMD y modelo de optimización
- Varios comentaristas señalan que Go todavía carece de autovectorización y, en general, tiene optimizaciones de bucles débiles (por ejemplo, desenrollado limitado), lo que lo convierte en una mala opción para trabajo numérico de alto nivel en comparación con C/C++/Rust/C#/Java.
- Algunos sostienen que esto podría ser una “bendición oculta” porque los vectorizadores son difíciles de hacer bien y pueden ser frágiles o contener errores.
- Varias personas prefieren abstracciones SIMD explícitas (tipo ISPC, intrínsecos, tipos SIMD portables) en lugar de una autovectorización “mágica”.
Comparaciones con otros lenguajes y bibliotecas
- C/C++: Los compiladores modernos con
-O3y-marcha menudo pueden autovectorizar productos punto, especialmente con banderas de fast-math. El SIMD escrito a mano aún puede superar la autovectorización al aprovechar detalles microarquitectónicos. - Rust: Los iteradores pueden optimizar bien; los bucles con enteros se vectorizan, pero las reducciones en coma flotante se bloquean por reglas de asociatividad salvo que se usen intrínsecos especiales o funciones en nightly. SIMD portátil es prometedor, pero todavía no es completamente estable.
- C#: Tiene intrínsecos estables y SIMD portátil, además de bibliotecas auxiliares. Los vectores de Panama de Java están en vista previa, pero se critican por ser lentos o carecer de operaciones.
- BLAS/Gonum: Un producto punto
float32de BLAS en Go supera las versiones en float del blog, pero la falta de un dotint8lo hace inutilizable para vectores cuantizados. - Otras herramientas mencionadas: Halide (planificación desacoplada), Google Highway, SimSIMD para otros lenguajes y avo para generación de ensamblador en Go.
Fast-math y semántica numérica
- Debate animado sobre las banderas estilo
-ffast-math: necesarias para vectorizar reducciones en coma flotante, pero pueden cambiar sutilmente los resultados y, históricamente, tenían efectos “contagiosos” entre unidades de traducción. - Alternativas sugeridas: banderas más específicas (
-fassociative-math,-fno-signed-zeros), atributos por función, tipos dedicados de “fast float”, o intrínsecos estables con semántica explícita.
Slices de Go, capacidad y microdetalles
- Se discute la sintaxis completa de slices de Go
a[i:j:k]: limita la capacidad, puede evitar algunos cálculos extra de cap, pero no elimina de forma fiable las comprobaciones de límites. - La reutilización de la capacidad del slice puede llevar a un aliasing sorprendente al hacer append, lo que algunos consideran poco intuitivo o inseguro; otros ven los slices como una encapsulación limpia del idiom de C de puntero+longitud(+capacidad).
FFI, ensamblador y consideraciones de escalado
- Algunos prefieren usar C vía cgo (o BLAS/Halide) en lugar de ensamblador escrito a mano; otros evitan cgo por complicaciones de toolchain, binarios estáticos, portabilidad y depuración.
- Se sugiere la paralelización entre núcleos como otra vía directa de ускорación, además de SIMD.
- Punto más amplio: la optimización vertical (velocidad en un solo nodo) puede reducir de forma material los costes de infraestructura, especialmente para SaaS con economías unitarias problemáticas.