El arte de la computación de alto rendimiento
(theartofhpc.com)- The Art of HPC es una serie de libros de texto sobre computación de alto rendimiento creada por Victor Eijkhout, de TACC, que integra en un solo recorrido desde los fundamentos de la computación científica hasta la programación paralela y las herramientas de desarrollo
- El primer volumen es un libro de contexto sobre computación científica que aborda cómo la arquitectura de computadoras, la aritmética, el álgebra lineal y las ODE/PDE se articulan en los cálculos a gran escala
- El segundo volumen explica la programación paralela con foco en MPI y OpenMP, e incluye también secciones breves sobre PETSc, Kokkos, Sycl y Co-array Fortran
- El tercer volumen trata C++17 y Fortran2008, usados en programación científica y de ingeniería, y puede leerse tanto por principiantes como por programadores en C
- El cuarto volumen presenta herramientas de flujo de trabajo de desarrollo necesarias para el trabajo real en HPC, como compiladores, sistemas de build y gestión de código fuente
Estructura de los materiales de The Art of HPC
- The Art of HPC es una serie de libros de texto sobre computación de alto rendimiento creada por Victor Eijkhout, de TACC
- La serie divide por volúmenes el contexto de la computación científica, la programación paralela, los lenguajes de programación científica y el ecosistema de desarrollo HPC
Alcance por volumen
-
Volume 1: The Science of Computing
- Aborda los conocimientos generales de contexto necesarios para entender la computación científica
- Incluye arquitectura de computadoras, arquitectura de computadoras paralelas, aritmética computacional, álgebra lineal y ODE/PDE
- Explica cómo se combinan estos elementos en el cómputo a gran escala y, junto con el Volume 2, conforma el “qué/por qué” y el “cómo” de HPC
-
Volume 2: Parallel Programming for Science and Engineering
- Es el volumen dedicado a la programación paralela, importante en la computación científica
- Presenta principalmente las versiones modernas de MPI y OpenMP
- También incluye secciones breves sobre PETSc, Kokkos, Sycl y Co-array Fortran
- MPI y OpenMP se tratan en C, Fortran y C++; MPI también incluye Python
-
Volume 3: Introduction to Scientific Programming
- Enseña C++17 y Fortran2008 modernos, con base en C/C++ y Fortran, muy usados en programación científica y de ingeniería
- Adopta un enfoque que prefiere C++17 por sobre C
- Puede leerse tanto como una introducción a la programación científica desde cero, como un libro para que programadores de C aprendan C++
- Incluye varios proyectos largos de programación
-
Volume 4: HPC Carpentry
- Se centra en que el ecosistema de computación científica no está compuesto solo por lenguajes de programación y sistemas de programación paralela
- Presenta elementos necesarios para los flujos de trabajo científicos, como compiladores, sistemas de build y gestión de código fuente
- Más que una obra de referencia exhaustiva, se acerca a una colección introductoria orientada a flujos de trabajo científicos
1 comentarios
Opiniones de Hacker News
El lado de hardware/centros de datos de este tema es igual de interesante.
Hace tiempo trabajé en AWS del lado de software/servicios, y a veces me escapaba a escuchar presentaciones del equipo de centros de datos.
La mayor revelación fue que aumentar la capacidad de cómputo en un centro de datos se parece más a un problema de termodinámica que a la computación en sí. La densidad de nodos se vuelve tan alta que meter energía y sacar calor, además de sumar todo tipo de redundancias, es extremadamente difícil. Incluso si se detecta una ineficiencia, no es algo que puedas arreglar como con una actualización de software.
Esto fue hace unos 10 años, así que quizá algunas cosas hayan cambiado, pero sorprende que Amazon, que empezó como una librería en línea, esté en la primera línea de la resolución de problemas de termodinámica.
En la Cray-2 adoptaron un enfoque más extremo: usaron una estructura de enfriamiento que sumergía pilas densas de placas de circuito en un líquido especial no conductor llamado Fluorinert™: “The Cray-2's unusual cooling scheme immersed dense stacks of circuit boards in a special non-conductive liquid called Fluorinert™”
El agua tiene una capacidad térmica muy alta y puede enfriar grandes volúmenes rápidamente hasta la temperatura óptima. Seguirían haciendo falta ventiladores y aire acondicionado para extraer el calor de los componentes que no pueden enfriarse con líquido, pero en componentes de alto consumo como CPU, GPU o motores de cómputo, permitiría extraer una enorme cantidad de calor de forma rápida y directa.
La complejidad y el riesgo de fugas sí serían un problema, pero en centros de datos a la escala de Amazon no me parece que sea una preocupación tan grande.
Me da curiosidad saber cómo se ven las tecnologías de enfriamiento de punta.
Es interesante cuando la HPC parece bastante abstraída del hardware
Los libros parecen cubrir mucho programación SPMD, algoritmos y estructuras de datos, paralelismo de tareas, sincronización, etc., pero parecen tener pocos detalles de arquitectura de computadoras, como los subsistemas de memoria de supercomputadoras, interconexiones de alto ancho de banda como CXL o la estructura de las GPU
Me pregunto si las abstracciones y herramientas ya son lo suficientemente buenas como para no tener que preocuparse por esos detalles, o si los profesionales de HPC también tocan muchas perillas de caja negra para extraer rendimiento
Como principio general, para lograr una escalabilidad óptima, la topología del software debe coincidir lo más posible con la topología del hardware. El software HPC eficiente está fuertemente influido por las características del hardware
Cuando escribía código para hardware HPC nuevo, la gente siempre se sorprendía cuando pedía documentación del hardware y de la arquitectura del sistema, no documentación de programación. Entender el diseño del hardware dejaba claro, desde primeros principios, cómo debía diseñarse el software encima de él. La documentación de programación tenía bastantes medias verdades que hacían que todo pareciera más fácil para los desarrolladores de lo que realmente era
Algunas plataformas HPC, para parecer “fáciles de usar”, comunicaban de forma persistentemente errónea qué debía hacer el desarrollador para obtener el máximo rendimiento, y si se escribía software de la forma que insinuaba el marketing, se fracasaba estrepitosamente porque no se alcanzaba el rendimiento que el silicio podía dar
Se puede escribir código HPC sobre abstracciones, y de hecho mucha gente lo hace, pero la pérdida de rendimiento y escalabilidad suele ser de múltiplos enteros inevitables. Como con otro software, a menudo esas pérdidas se aceptaban si permitían que desarrolladores menos experimentados diseñaran el código
En HPC, igual que en otro software, hay mucha gente que, aunque nominalmente sea desarrolladora profesional, tiene dificultades para producir buenos resultados de forma consistente. Buena parte del hardware caro que se usa en HPC existe para mitigar las pérdidas de rendimiento causadas por malos diseños de software
Si se quiere el máximo rendimiento, no hay atajos que sustituyan entender realmente cómo funciona el hardware. No es distinto del software común; en HPC, el sistema de hardware simplemente es más grande y complejo
Pero no fue como esperaba. Pensé que haría más trabajo orientado a rendimiento, analizando números y exprimiendo hasta el último rendimiento del clúster. Sinceramente, al principio ni siquiera había monitoreo. Lo construí yo, pero casi no se usa. De vez en cuando la gerencia pregunta “qué tan ocupado está el clúster”, por cosas como justificar el presupuesto
La mayor parte de la “optimización” consiste en verificar que la gente no pida 384 CPUs cuando su script solo usa 16, o en probar hasta cuántas CPUs puede funcionar cierto software sin degradación de rendimiento. Abrí el profiler de Intel exactamente dos veces
La mayor parte del trabajo se parece más a ayudar a investigadores con sus trabajos. Normalmente ejecutamos programas comerciales o de código abierto y resolvemos problemas, o tomamos código que otro equipo escribió en otro clúster y hacemos que compile y corra en el nuestro. Me toca revisar código Python terrible y pelear para compilar proyectos C++ de un clúster más moderno en un entorno CentOS 7
A su manera es divertido. Como he trabajado con varios lenguajes, disfruto hacer que las cosas funcionen y meterme en crashes y stack traces. Cuando uno trabaja con equipos grandes, se le distorsiona el criterio de lo normal al ver un servidor con “solo” 128 GB de RAM o 20 TB de disco
Lo aterrador es que estos resultados se usan en el mundo real, y a veces quienes corren las simulaciones no lo hacen correctamente. He visto código incorrecto, código fuente mezclado, uso de datos distintos de los que creían estar usando, e incluso un bug enorme que llevaba 3 años existiendo. Entonces uno se pregunta si eso no invalida todo el trabajo hecho sobre ese tema
La desventaja es que muchos empleos de HPC exigen maestría aunque solo sean para operar clústeres. No lo entiendo bien. No escribo el software que ejecuto ni opero un clúster TOP500 de última generación. Solo conecto varias máquinas en red y corro código
Por mi experiencia trabajando con desarrolladores CUDA, se tocan muchas perillas para extraer rendimiento. El Shmoo Plot(https://en.wikipedia.org/wiki/Shmoo_plot, también llamado “wedge” en algunos sectores) es una de las herramientas centrales de la optimización cotidiana
Dicho eso, no sé si lo llamaría caja negra. En la práctica quizá sea parecido. Aunque sepas qué hacen las perillas y cómo funcionan, y hagas conjeturas fundamentadas, al medir es común encontrarse con grandes sorpresas. La primera regla de la optimización es medir
Siempre me viene a la mente el primer capítulo de “Black Book” de Michael Abrash, “The Best Optimizer is Between Your Ears” http://twimgs.com/ddj/abrashblackbook/gpbb1.pdf. Aunque se centra en juegos de PC y no en HPC moderna, es un texto excelente que muestra bien la filosofía del alto rendimiento
En cuanto a las abstracciones, lo mejor es dejar el ajuste más pesado de perillas para el final del proceso de optimización. Si refactorizas o cambias algo, tienes que volver a ajustar las perillas. Un pequeño cambio en register spilling o en los patrones de acceso a caché puede reiniciar por completo los ajustes finos de configuración de hilos, caché y tamaño de memoria compartida
Aun así, hace falta una cantidad razonable de ajuste de perillas de vez en cuando, para verificar y equilibrar, y para ganar intuición sobre el espacio de rendimiento alrededor del código
En el hardware x86_64 actual no existe algo como un subsistema de memoria de supercomputadora. Es simplemente un sistema NUMA sofisticado, y el mayor problema es mantener la memoria cerca de los cores; es decir, mantener los datos locales dentro de ese nodo NUMA para reducir la latencia
El mapeo de recursos lo maneja el scheduler. El scheduler conoce el hardware, así que crea un cgroup que satisface los requisitos y está optimizado en la medida de lo posible, y ejecuta la aplicación dentro de ese cgroup
Actualmente, el rey de las interconexiones de alto rendimiento es InfiniBand, que acelera MPI a nivel de fabric. Permite que el envío de mensajes, los broadcasts y las reducciones de resultados sean muy rápidos. Cuando llega un mensaje, ya viene reducido, y al hacer un broadcast basta con enviar un solo mensaje para que se difunda en la capa de fabric. Las tarjetas IB con múltiples contextos tienen muchas colas, por lo que desde un solo nodo/tarjeta se pueden ejecutar varias tareas MPI con aislamiento de colas/contextos
Si usas un framework para trabajos en GPU, la estructura y las optimizaciones normalmente se manejan automáticamente a ese nivel. El trabajo pesado suele recaer en los desarrolladores del framework. Los drivers de NVIDIA también son pura magia negra y se encargan de parte de la optimización. La conexión entre GPU queda a cargo del fabric físico, y la gestionan el driver y sus propios daemons
Si el cuello de botella está en la CPU, por lo general las bibliotecas ya vienen ajustadas a mano por el proveedor. Es el caso de Intel MKL, BLAS, Eigen, etc.; en Eigen, que he usado personalmente, venían incluidos hints y optimizaciones específicos por procesador
Lo que hay que cuidar es compilar el código para la arquitectura correcta y asegurarse de que el hardware donde se ejecuta pueda cumplir los requisitos. Por ejemplo, no hacer demasiados accesos aleatorios a memoria, ajustar bien el prefetcher y el predictor de ramas si se quiere ir “lo más rápido posible” en el nodo, y no abusar del acceso a disco
En cálculo numérico, la clave es mantener las tareas independientes para permitir paralelismo a nivel de instrucciones/vectorización, no hacer cálculos innecesarios y no abusar de MPI; es decir, reducir la comunicación entre nodos a lo estrictamente necesario
Es más fácil decirlo que hacerlo, pero una vez que te acostumbras, pensar en estas cosas se vuelve casi una segunda naturaleza. Si este tipo de trabajo va con tus gustos, claro
Sí y no
MPI y OpenMP son los principales medios para abstraer el hardware en HPC. MPI es una abstracción para cómputo paralelo con memoria distribuida, y OpenMP es una abstracción para cómputo paralelo con memoria compartida. Muchos investigadores escriben código solo con esas dos herramientas, y también es común usar ambas en el mismo código. Al usarlas, la mayoría de las veces no hace falta preocuparse por los detalles de la arquitectura
Aun así, los investigadores a los que les gusta optimizar más tocan muchos pequeños detalles arquitectónicos para exprimir más rendimiento. Por ejemplo, el desenrollado de bucles es bastante común y, personalmente, creo que puede volverse bastante confuso. Recuerdo vagamente haber oído que, por ciertas arquitecturas de CPU, se prefería la suma a la multiplicación para intentar vectorizar operaciones, pero nunca lo he visto en la práctica
Evitar cache misses también es un tema importante. Hay código escrito para mantener la información más necesaria en la caché de la CPU, no en la memoria. La mayoría del código solo llega a garantizar recorrido por columnas en operaciones con arreglos en Fortran y recorrido por filas en C, pero el concepto puede llevarse más lejos. Si conoces el tamaño de la caché del procesador, también podrías optimizar ciertas operaciones para mantener toda la información necesaria dentro de la caché y minimizar los cache misses. Nunca lo he visto en la práctica, pero en una clase de computación científica que tomé en 2013 era un tema que se trataba mucho
Usar o no una GPU en particular depende mucho del problema que se quiera resolver. Algunos problemas van de maravilla en GPU y otros son demasiado difíciles. Lamentablemente, no conozco bien esa parte
Me impresiona que Victor haya reunido material tan excelente.
No lo conozco personalmente, pero en la década de 1990, mientras hacía el doctorado en UT Austin, terminé mi investigación usando recursos administrados por TACC (Cray Y-MP, IBM SP/2 Winterhawk y Lonestar, que en ese entonces era el hostname que apuntaba al Cray T3E). Uno de los miembros de mi comité doctoral sigue allí. Si no me falla la memoria, en esa época TACC se llamaba HPCC o CHPC.
En aquel entonces, el programador tenía que paralelizar el código directamente y, en mi caso, usé MPI en un Cray T3E bajo UNICOS. Como el campo todavía estaba en una etapa temprana, también hacía falta entender algo de hardware. Resolví los problemas leyendo las carpetas grises de anillos de Cray y el libro de Gropp y otros que tenía a mano; y, por supuesto, el contacto con mucho conocimiento que mencioné antes también ayudó bastante.
El tiempo nunca se detiene.
Está un poco fuera de mi campo, pero me pareció muy interesante. Pienso revisar el resto también, y recomiendo que cualquiera interesado le eche un vistazo.
Me interesa el lado de la administración de hardware en HPC.
Me da curiosidad cómo se detectan y diagnostican los problemas, cómo se mapean a acciones como reinicios/reinstalaciones/reparaciones, y cómo se programan y optimizan esas tareas para ofrecer el mejor nivel de servicio.
También me interesa cómo se maneja cuando hay varios objetivos que optimizar al mismo tiempo, como la disponibilidad de nodos y el throughput total; cómo distintas topologías afectan todo lo anterior; qué efecto tienen otras restricciones; y, en general, cómo se abordan estos problemas desde una perspectiva de dinámica de sistemas.
No he encontrado mucho material que cubra bien esta información. Si alguien conoce recursos, agradecería que los compartiera.
La teoría de colas parece trivial y fácil cuando uno la aprende por primera vez, pero tiene muchos problemas abiertos.
Por ejemplo, las métricas de rendimiento de un sistema con tiempos de llegada aleatorios, tiempos de servicio independientes y k servidores (M/G/k) siguen siendo un problema abierto.
https://www.sciencedirect.com/science/article/pii/S0895717704905341
Contra lo que uno esperaría, en la teoría de colas hay muchísimos problemas abiertos.
Meta tenía varios videos buenos en YouTube que explicaban el problema de manejar GPU a esa escala.
Meta también publica muchos papers, blogs y proyectos open source en su sitio de ingeniería [2].
James Hamilton, de AWS, también da presentaciones sobre infraestructura casi todos los años. Vale la pena ver las de varios años [3].
[1] https://youtu.be/69PrhWQorEM?si=u7vh_Um6SQNoyeFH
[2] https://engineering.fb.com/category/data-center-engineering/
[3] https://youtu.be/AyOAjFNPAbA?si=nFRJVcQI4EiamC-O
Básicamente optimiza a nivel de carga de trabajo, en este caso trabajos de deep learning, para permitir ajustar el tamaño de los trabajos y hacer preemptions.
[1] https://arxiv.org/pdf/2202.07848.pdf
En 2013 tomé una clase de cómputo científico.
Era una materia ofrecida de forma cruzada entre ciencias de la computación y matemática aplicada. El problema es que el campo en general era demasiado amplio, así que cubría muchos temas, incluidos HPC y programación paralela, de manera muy superficial. No me arrepiento de haberla tomado, pero era demasiado amplia para las aplicaciones que yo buscaba.
No he revisado en años qué materias se ofrecen, pero cuando era estudiante de posgrado me habría sido muy útil una materia dedicada a cálculo paralelo durante todo un semestre. En particular, hacía falta una clase que profundizara en algoritmos y estructuras de datos específicos de la computación paralela y distribuida.
En la clase de cómputo científico que tomé, estos temas se trataron demasiado por encima, como si uno pudiera saber exactamente cómo paralelizar algo bien desde el primer intento. Después, como mucha gente de HPC, aprendí bastante por mi cuenta y de colegas a lo largo de los años, pero estos libros habrían sido muy valiosos como parte de una materia dedicada de un semestre.
Me sorprende que el autor haya creado libros tan completos, que incluso incluyen formación en C++ y herramientas Unix, y los haya compartido gratis.
Aunque no sean específicos de HPC, cualquier programador puede aprender algo de ellos.
Como material relacionado también están el libro “Matters Computational” de Jorg Arndt y la biblioteca FXT: https://www.jjj.de/fxt/
Me da curiosidad saber qué opinan del enfoque de enseñanza de C++ que se usa aquí. ¿Tiene alguna desventaja particular?
He usado Python durante muchísimo tiempo, también manejo algo de C, C++ y CUDA, y hago investigación a nivel de aplicaciones (ML/DL) en entornos HPC. Quiero mejorar mis habilidades en C++, y al hojear los tres volúmenes parecen estar justo a mi nivel. No avanzan demasiado lento y, más que apuntar a ser exhaustivos, enseñan las buenas prácticas que propone el autor.
Busqué bucles for basados en rango, std::array y std::span, y me gustó ver que todos están incluidos.
Como este libro está relacionado con HPC, me gustaría agregar algunas cosas. Sería bueno que hubiera explicaciones sobre la optimización del valor de retorno, la semántica de movimiento y, en la sección de funciones recursivas, la optimización de llamadas de cola.
Como material para principiantes, lo puedo recomendar ampliamente.
Como referencia, MPI es solo una forma de hacer HPC con Python.
Si no recuerdo mal, ipyparallel puede ejecutar trabajos MPI sobre túneles creados por uno mismo.
Sería más actual si tuviera capítulos sobre dask-scheduler, CuDF, CuGraph (NetworkX), DaskML, CuPy y dask-labextension.
Dask no se encarga por ti del almacenamiento de datos, así que es responsabilidad del usuario asegurarse de que el almacenamiento de datos antes de cada barrera no se convierta en un cuello de botella de rendimiento.
High Performance Computers en la documentación de Dask: https://docs.dask.org/en/stable/deploying-hpc.html
La fuente de números aleatorios también podría ser un cuello de botella. No lo sabrás hasta perfilar el trabajo en todo el clúster.
Sobre herramientas de trazado basadas en eBPF: https://news.ycombinator.com/item?id=31688180
Después de eso, también estaría bien incluir temas como GitOps y ChatOps, revisiones de código y versiones, y cuotas de recursos de proyecto.
Hace 10 años me propusieron compartir el rol de ayudante de cátedra en un curso de posgrado de HPC, pero lo rechacé.
Por lo que pude hojear, puedo decir honestamente que, si este libro hubiera existido en ese momento, habría aceptado esa oportunidad.
La combinación del encuadre como arte, que parece al estilo de Knuth, la analogía con la carpintería y la necesidad de convertirte en alguien mejor en DevOps que tu propio encargado de DevOps resulta convincente.
Aplaudo el logro del autor. UT Austin parece haber hecho en ciencias de la computación algo similar a lo que North Texas State hizo en música.
UT Austin es una institución realmente excelente en HPC y metodologías computacionales.
Cuando me uní a una empresa pequeña que daba soporte a ingenieros de HPC de un gran fabricante automotriz, me sorprendió la cantidad de scripts desarrollados internamente alrededor del planificador LSF.
Mucho más tarde, al jugar con SLURM en un miniclúster personal, descubrí que las versiones del software de planificación en general no son compatibles entre sí. Es decir, no era posible usar una versión dentro del clúster y otra distinta en una máquina cliente externa.
Por eso se necesitaba software de pegamento para enviar trabajos al planificador desde afuera y luego recuperar los resultados. Personalmente, creo que eso le resta valor al planificador.
Pensé que, después de unos 30 años de cómputo distribuido de alto rendimiento, los requisitos ya serían bien conocidos y que al menos los protocolos de intercambio de comandos y datos estarían fijados. Pero parece que no.
En el pasado hubo intentos de estandarizar una API básica de gestión de trabajos, y DRMAA es un ejemplo notable. Sin embargo, DRMAA v2 solo fue implementado por Grid Engine y, en la práctica, era una versión ligeramente abstraída de su API interna, por lo que nunca obtuvo soporte de primera clase en Slurm/PBS/LSF.
En Slurm, la API REST se considera el camino a seguir. El problema de autenticación se delega mediante un proxy Apache/NGINX a cualquier mecanismo con el que el administrador quiera integrarlo. Las APIs básicas de envío y estado de trabajos ya se han estabilizado lo suficiente como para que casi cualquier versión futura de una aplicación cliente pueda consumirlas.