Optimizando la Utilización de GPU para Redes Neuronales
Un nuevo marco mejora el rendimiento de la GPU para tareas de aprendizaje profundo.
― 9 minilectura
Tabla de contenidos
- Entendiendo Por Qué Algunas Aplicaciones No Aprovechan las GPU
- Motores de Simulación de Aprendizaje Profundo por Refuerzo
- Redes Neuronales Dinámicas
- Desafíos para la Ejecución Concurrente de Kernels
- Desafío 1: Gráficos Computacionales Dependientes de la Entrada
- Desafío 2: Dependencias Irregulares Entre Kernels
- Nuestra Solución: Un Nuevo Marco
- Enfoque Solo de Software (-SW)
- Enfoque Cooperativo de Hardware y Software (-HW)
- Evaluando el Marco
- 1. Simulaciones de Aprendizaje Profundo por Refuerzo
- 2. Redes Neuronales Dinámicas
- 3. Redes Neuronales Estáticas
- Conclusión
- Fuente original
Las Unidades de Procesamiento Gráfico (GPU) modernas son clave para acelerar muchas tareas importantes hoy en día. Sin embargo, algunas aplicaciones nuevas, como los motores de simulación para aprendizaje profundo por refuerzo y redes neuronales dinámicas, no utilizan las GPU a su máximo potencial. Esto es porque estas aplicaciones suelen tener tareas pequeñas (o kernels) que no pueden mantener la GPU ocupada lo suficiente. Cuando estas pequeñas tareas se ejecutan una tras otra, en lugar de al mismo tiempo, puede desperdiciar mucha potencia de computación. Una buena solución a este problema es ejecutar tareas independientes al mismo tiempo. Pero esto es complicado porque las dependencias entre tareas cambian con diferentes entradas, y estas dependencias son complicadas.
En nuestro trabajo, presentamos un marco que puede revisar fácilmente estas dependencias entre tareas en tiempo de ejecución y programarlas sin mucho retraso. La idea principal es mirar un pequeño número de tareas a la vez y chequear sus dependencias, similar a cómo los procesadores modernos manejan las instrucciones. Esto permite la ejecución simultánea de tareas en aplicaciones donde el orden en que se ejecutan no está fijado y requiere una Programación cuidadosa. Creamos dos versiones de nuestro marco: una que funciona solo a través de software y otra que combina software con apoyo de hardware para reducir aún más los retrasos causados por la comunicación entre la CPU y la GPU.
Probamos nuestro marco en varias aplicaciones, sobre todo en aprendizaje profundo por refuerzo y diferentes tipos de redes neuronales. Los resultados fueron prometedores, mostrando que podíamos acelerar el procesamiento de manera significativa mientras aprovechábamos mejor la GPU.
Entendiendo Por Qué Algunas Aplicaciones No Aprovechan las GPU
Las GPU se usan regularmente para acelerar tareas como el procesamiento de redes neuronales profundas, cálculos científicos, renderizado de gráficos y criptografía. Las amplias capacidades paralelas de las GPU les permiten manejar grandes cantidades de datos rápidamente. Sin embargo, hay ciertas aplicaciones, particularmente en áreas como el aprendizaje profundo por refuerzo y las redes neuronales dinámicas, que no aprovechan completamente las capacidades de la GPU.
Una razón para esto es que estas aplicaciones dependen de muchas tareas pequeñas que no pueden activar completamente la potencia de procesamiento de la GPU. Exploramos dos clases de aplicaciones para identificar los problemas que causan esta subutilización.
Motores de Simulación de Aprendizaje Profundo por Refuerzo
En el aprendizaje profundo por refuerzo, un agente, como un robot, aprende a completar tareas a través de prueba y error interactuando con su entorno. Entrenar un agente implica usar una red neuronal profunda (DNN) para desarrollar estrategias que maximizan recompensas basadas en datos recopilados de entornos simulados.
Aunque las DNN pueden beneficiarse del poder de las GPU, una parte significativa del tiempo de ejecución del aprendizaje profundo por refuerzo se gasta en la recolección de datos, que a veces puede ocupar hasta el 90% del tiempo total. Durante esta fase, las simulaciones físicas generan datos de entrenamiento. Sin embargo, descubrimos que estas simulaciones no utilizan la GPU de manera eficiente, logrando solo alrededor del 30% de su capacidad total en promedio. Esta ineficiencia proviene de que los pequeños kernels en las simulaciones no pueden involucrar completamente los recursos de la GPU, lo que hace impráctico crear kernels más grandes ya que cada tarea simula diferentes escenarios.
Redes Neuronales Dinámicas
Los desarrollos recientes en redes neuronales dinámicas muestran promesas para reducir el tiempo que tardan estas redes en hacer predicciones en dispositivos con energía limitada. Estas redes pueden ajustar su estructura según la entrada que reciben. Por ejemplo, algunos modelos pueden cambiar su arquitectura en tiempo real según la imagen de entrada.
Descubrimos que, aunque estas redes dinámicas utilizan menos operaciones de punto flotante y tienen tiempos de predicción más cortos, aún no utilizan los recursos de la GPU de manera efectiva, logrando una tasa de ocupación promedio de solo el 39%. Al igual que los motores de simulación, esta ineficiencia se atribuye a pequeños kernels que no comprometen completamente los Núcleos de la GPU.
Los kernels de GPU de ambos tipos de aplicaciones suelen ejecutarse en secuencia, lo que significa que la capacidad de la GPU está limitada por el tamaño y número de hilos en cada kernel. Sin embargo, muchos de estos kernels son independientes, lo que permite que se ejecuten de manera concurrente. Al ejecutar estos kernels independientes al mismo tiempo, podemos mejorar la utilización de la GPU y aumentar el rendimiento.
Desafíos para la Ejecución Concurrente de Kernels
A pesar de que ejecutar kernels independientes concurrentemente es una buena solución para mejorar el rendimiento, no es fácil de implementar por varias razones.
Desafío 1: Gráficos Computacionales Dependientes de la Entrada
En muchas aplicaciones, las dependencias entre kernels se determinan solo durante la ejecución, según los datos de entrada. Esto significa que cada entrada o conjunto de entradas puede llevar a un arreglo diferente de kernels que deben ejecutarse. Cuando este es el caso, chequear las dependencias entre los kernels se vuelve necesario pero también lleva tiempo, especialmente dado que muchas de estas tareas tienen tiempos de ejecución cortos.
Las herramientas existentes pueden ayudar a definir dependencias y crear un gráfico acíclico dirigido de kernels, lo que permitiría la ejecución concurrente. Sin embargo, estas herramientas a menudo requieren una cantidad significativa de tiempo para preparar el gráfico de dependencias para cada ejecución de la aplicación, lo que puede ralentizar el procesamiento general.
Desafío 2: Dependencias Irregulares Entre Kernels
Otro problema es que los gráficos computacionales pueden ser impredecibles. Debido a esta impredecibilidad, puede ser difícil dividirlos en grupos independientes para ejecución paralela. La programación de alta precisión es esencial para descubrir el paralelismo entre kernels. Esta necesidad de sincronización frecuente puede introducir una sobrecarga considerable debido a la comunicación necesaria con la CPU.
Nuestra Solución: Un Nuevo Marco
Para superar estos desafíos, proponemos un nuevo marco para automatizar la programación concurrente de kernels que requiere una sobrecarga mínima para rastrear dependencias. Este marco tiene dos implementaciones: una que opera puramente en software y otra que aprovecha capacidades de hardware para reducir aún más las sobrecargas.
Características Clave de Nuestro Marco
- El marco revisa las dependencias entre kernels dentro de un grupo limitado en tiempo de ejecución, utilizando un método similar a la programación de instrucciones fuera de orden.
- Cuando un kernel entra en la ventana de programación, identifica automáticamente qué kernels dependen de él. Una vez que un kernel termina de ejecutarse, los kernels dependientes pueden lanzarse de forma concurrente si no tienen dependencias restantes.
- Las anotaciones proporcionadas por la aplicación ayudan al marco a identificar los rangos de direcciones de memoria que cada kernel lee o escribe, que luego se utilizan para verificar dependencias en tiempo de ejecución.
Enfoque Solo de Software (-SW)
La implementación -SW opera como un sistema de tiempo de ejecución que utiliza flujos CUDA existentes para programar kernels independientes de manera concurrente. Aunque es eficiente, aún incurre en cierta sobrecarga de sincronización al comunicarse con la CPU.
Enfoque Cooperativo de Hardware y Software (-HW)
La implementación -HW incorpora elementos tanto de software como de hardware. Utiliza hardware para gestionar la ventana de programación, lo que ayuda a reducir significativamente la comunicación con la CPU. Este modelo de hardware mantiene un seguimiento de las dependencias de manera eficiente para agilizar la programación y ejecución de kernels.
Evaluando el Marco
Probamos nuestro marco con varias aplicaciones para medir mejoras en la utilización de la GPU y el rendimiento general.
1. Simulaciones de Aprendizaje Profundo por Refuerzo
Examinamos varios entornos de simulación física para aprendizaje profundo por refuerzo, como Ant, Grasp, Humanoid, Cheetah y Walker2d. Estas simulaciones requieren datos de miles de ejecuciones, y observamos cómo variaban los modelos bajo nuestro marco.
Los resultados mostraron que nuestro marco podría mejorar el rendimiento aumentando la utilización de la GPU, logrando aceleraciones de hasta 4.5 veces comparado con métodos de referencia.
2. Redes Neuronales Dinámicas
Luego, evaluamos nuestro marco en redes neuronales dinámicas usando tres cargas de trabajo específicas: InstaNAS, Dynamic Routing y Conditional Convolution. Estos modelos están diseñados para ajustarse según la entrada que reciben, lo que los convierte en candidatos ideales para las pruebas.
Nuevamente, nuestro marco proporcionó resultados impresionantes, con aceleraciones que oscilan entre 2 y 3 veces sobre modelos de referencia en varias iteraciones.
3. Redes Neuronales Estáticas
Para redes neuronales estáticas, probamos varias arquitecturas populares que tienden a tener estructuras fijas. Nuestro marco aún así logró mejoras en el rendimiento, alcanzando aceleraciones de alrededor de 1.5 a 2 veces sobre métodos tradicionales.
Conclusión
En conclusión, nuestro marco proporciona una solución práctica al desafío de ejecutar kernels independientes concurrentemente en aplicaciones con gráficos computacionales irregulares. Al permitir una programación eficiente y verificaciones de dependencias en tiempo de ejecución, podemos mejorar significativamente la utilización de la GPU y el rendimiento general en aplicaciones de aprendizaje profundo por refuerzo y redes neuronales dinámicas.
Este trabajo destaca la necesidad de mejores enfoques para gestionar tareas que dependen de condiciones de entrada dinámicas, abriendo el camino a una computación más eficiente en el futuro. Los resultados logrados a través de nuestras implementaciones solo de software y cooperativas de hardware y software demuestran el potencial de aceleraciones sustanciales en aplicaciones del mundo real. Este avance marca un paso hacia la optimización de los recursos de la GPU para una gama más amplia de aplicaciones, abordando la subutilización que actualmente se experimenta en muchas cargas de trabajo emergentes.
Título: ACS: Concurrent Kernel Execution on Irregular, Input-Dependent Computational Graphs
Resumen: GPUs are widely used to accelerate many important classes of workloads today. However, we observe that several important emerging classes of workloads, including simulation engines for deep reinforcement learning and dynamic neural networks, are unable to fully utilize the massive parallelism that GPUs offer. These applications tend to have kernels that are small in size, i.e., have few thread blocks that do not saturate compute resources. Executing independent kernels concurrently is a promising approach to improve parallelism and utilization. However, this inter-kernel concurrency is difficult to leverage in such workloads with existing approaches: First, the inter-kernel dependencies and computational graph are input-dependent and vary each time the application is executed. Second, the computational graphs tend to be irregular, requiring fine-grain scheduling and synchronization; thus incurring significant synchronization overheads if kernel execution is parallelized. In this work, we propose ACS, a framework that enables lightweight detection of inter-kernel dependencies and low overhead kernel scheduling at runtime. The key idea behind ACS is to perform inter-kernel dependency checks for a small window of kernels at runtime, similar to out-of order instruction scheduling. This enables concurrent execution of kernels in applications whose computational graphs are input dependent and require fine-grained scheduling. We propose ACS-SW, a software-only open-source implementation of ACS and ACS-HW, a hardware-software cooperative implementation. ACS-HW further reduces synchronization overheads by reducing communication between the CPU and GPU. We evaluate ACS for deep RL simulation and dynamic DNNs on both real hardware and a GPU simulator. We demonstrate speedups of up to 2.19x (1.56x on average) by improving GPU utilization with concurrent kernel execution.
Autores: Sankeerth Durvasula, Adrian Zhao, Raymond Kiguru, Yushi Guan, Zhonghan Chen, Nandita Vijaykumar
Última actualización: 2024-01-22 00:00:00
Idioma: English
Fuente URL: https://arxiv.org/abs/2401.12377
Fuente PDF: https://arxiv.org/pdf/2401.12377
Licencia: https://creativecommons.org/licenses/by/4.0/
Cambios: Este resumen se ha elaborado con la ayuda de AI y puede contener imprecisiones. Para obtener información precisa, consulte los documentos originales enlazados aquí.
Gracias a arxiv por el uso de su interoperabilidad de acceso abierto.