Usando Java con GPU de Nvidia (cuda)

Estoy trabajando en un proyecto empresarial que se realiza en Java y que necesita un gran poder de cómputo para calcular los mercados comerciales. Simple matemática pero con una gran cantidad de datos.

Pedimos algunos cpu para probarlo y dado que Java no es compatible con Cuda, me pregunto por dónde empezar. ¿Debo construir una interfaz JNI? ¿Debería usar JCUDA o hay otras formas?

No tengo experiencia en este campo y me gustaría que alguien me dirija a algo para que pueda comenzar a investigar y aprender.

En primer lugar, debe tener en cuenta el hecho de que CUDA no automaticamente hará que los cálculos sean más rápidos. Por un lado, porque la progtwigción de GPU es un arte, y puede ser muy, muy desafiante para hacerlo bien . Por otro lado, porque las GPU son adecuadas solo para ciertos tipos de cálculos.

Esto puede sonar confuso, porque básicamente puede calcular cualquier cosa en la GPU. El punto clave es, por supuesto, si logrará una buena aceleración o no. La clasificación más importante aquí es si un problema es tarea paralela o datos paralelos . El primero se refiere, en términos generales, a problemas en los que varios hilos trabajan en sus propias tareas, más o menos independientemente. El segundo se refiere a problemas donde muchos hilos están haciendo lo mismo , pero en diferentes partes de los datos.

Este último es el tipo de problema en el que las GPU son buenas: tienen muchos núcleos, y todos los núcleos hacen lo mismo, pero operan en diferentes partes de los datos de entrada.

Mencionaste que tienes “matemáticas simples pero con gran cantidad de datos”. Aunque esto puede parecer un problema perfectamente paralelo a los datos y, por lo tanto, como era adecuado para una GPU, hay otro aspecto a considerar: las GPU son ridículamente rápidas en términos de potencia de cálculo teórica (FLOPS, operaciones de coma flotante por segundo). Pero a menudo son acelerados por el ancho de banda de la memoria.

Esto lleva a otra clasificación de problemas. A saber, si los problemas están vinculados a la memoria o computados .

El primero se refiere a los problemas donde el número de instrucciones que se realizan para cada elemento de datos es bajo. Por ejemplo, considere una adición de vector paralelo: tendrá que leer dos elementos de datos, luego realizar una sola adición y luego escribir la sum en el vector de resultados. No verá una aceleración al hacer esto en la GPU, porque la sola sum no compensa los esfuerzos de lectura / escritura de la memoria.

El segundo término, “cálculo vinculado”, se refiere a problemas donde el número de instrucciones es alto en comparación con el número de lecturas / escrituras de memoria. Por ejemplo, considere una multiplicación de matrices: el número de instrucciones será O (n ^ 3) cuando n es el tamaño de la matriz. En este caso, se puede esperar que la GPU supere a una CPU en un determinado tamaño de matriz. Otro ejemplo podría ser cuando se realizan muchos cálculos trigonométricos complejos (seno / coseno, etc.) en “pocos” elementos de datos.

Como regla general: puede suponer que leer / escribir un elemento de datos de la memoria GPU “principal” tiene una latencia de aproximadamente 500 instrucciones ….

Por lo tanto, otro punto clave para el rendimiento de las GPU es la ubicación de los datos : si tiene que leer o escribir datos (y en la mayoría de los casos, tendrá que ;-)), entonces debe asegurarse de que los datos estén lo más cerca posible. posible para los núcleos de la GPU. Por lo tanto, las GPU tienen ciertas áreas de memoria (denominadas “memoria local” o “memoria compartida”) que, por lo general, tienen solo unos pocos KB de tamaño, pero son particularmente eficientes para los datos que están a punto de participar en un cálculo.

Por lo tanto, para enfatizar esto de nuevo: la progtwigción de GPU es un arte, que está solo remotamente relacionado con la progtwigción paralela en la CPU. Cosas como Threads en Java, con toda la infraestructura de concurrencia como ThreadPoolExecutors , ForkJoinPools etc., pueden dar la impresión de que solo tiene que dividir su trabajo de alguna manera y distribuirlo entre varios procesadores. En la GPU, puede enfrentar desafíos en un nivel mucho más bajo: ocupación, presión de registro, presión de memoria compartida, fusión de memoria … solo por nombrar algunos.

Sin embargo, cuando tiene que resolver un problema de cómputo paralelo en datos, la GPU es el camino a seguir.


Un comentario general: Su pedido específico de CUDA. Pero te recomiendo encarecidamente que también eches un vistazo a OpenCL. Tiene varias ventajas. En primer lugar, es un estándar abierto de la industria, independiente del proveedor, y hay implementaciones de OpenCL por AMD, Apple, Intel y NVIDIA. Además, hay un soporte mucho más amplio para OpenCL en el mundo de Java. El único caso en el que preferiría conformarme con CUDA es cuando desee utilizar las bibliotecas de tiempo de ejecución de CUDA, como CUFFT para FFT o CUBLAS para BLAS (operaciones de Matrix / Vector). Aunque existen métodos para proporcionar bibliotecas similares para OpenCL, no se pueden usar directamente desde Java, a menos que cree sus propios enlaces JNI para estas bibliotecas.


También puede ser interesante escuchar que en octubre de 2012, el grupo OpenJDK HotSpot comenzó el proyecto “Sumatra”: http://openjdk.java.net/projects/sumtra/ . El objective de este proyecto es proporcionar soporte de GPU directamente en la JVM, con el apoyo de JIT. El estado actual y los primeros resultados se pueden ver en su lista de correo en http://mail.openjdk.java.net/mailman/listinfo/sumtra-dev


Sin embargo, hace un tiempo, recolecté algunos recursos relacionados con “Java en la GPU” en general. Resumiré estos de nuevo aquí, sin ningún orden en particular.

( Descargo de responsabilidad : soy el autor de http://jcuda.org/ y http://jocl.org/ )

(Byte) traducción de código y generación de código OpenCL:

https://github.com/aparapi/aparapi : una biblioteca de código abierto creada y mantenida activamente por AMD. En una clase especial “Kernel”, se puede anular un método específico que debe ejecutarse en paralelo. El código de bytes de este método se carga en tiempo de ejecución utilizando un lector de bytecode propio. El código se traduce en código OpenCL, que luego se comstack utilizando el comstackdor OpenCL. El resultado se puede ejecutar en el dispositivo OpenCL, que puede ser una GPU o una CPU. Si la comstackción en OpenCL no es posible (o no hay OpenCL disponible), el código se ejecutará en paralelo, utilizando un grupo de subprocesos.

https://github.com/pcpratts/rootbeer1 : una biblioteca de código abierto para convertir partes de Java en progtwigs CUDA. Ofrece interfaces dedicadas que pueden implementarse para indicar que una determinada clase debe ejecutarse en la GPU. A diferencia de Aparapi, intenta serializar automáticamente los datos “relevantes” (es decir, la parte relevante completa del gráfico de objetos) en una representación que sea adecuada para la GPU.

https://code.google.com/archive/p/java-gpu/ : una biblioteca para traducir el código anotado de Java (con algunas limitaciones) en código CUDA, que luego se comstack en una biblioteca que ejecuta el código en la GPU. La Biblioteca se desarrolló en el contexto de una tesis doctoral, que contiene información profunda sobre el proceso de traducción.

https://github.com/ochafik/ScalaCL : enlaces de Scala para OpenCL. Permite que las colecciones especiales de Scala se procesen en paralelo con OpenCL. Las funciones que se invocan sobre los elementos de las colecciones pueden ser funciones habituales de Scala (con algunas limitaciones) que luego se traducen en núcleos OpenCL.

Extensiones de idioma

http://www.ateji.com/px/index.html : Una extensión de lenguaje para Java que permite construcciones paralelas (por ejemplo, bucles paralelos para, estilo OpenMP) que luego se ejecutan en la GPU con OpenCL. Lamentablemente, este proyecto muy prometedor ya no se mantiene.

http://www.habanero.rice.edu/Publications.html (JCUDA): una biblioteca que puede traducir el código especial de Java (llamado código JCUDA) al código Java y CUDA-C, que luego puede comstackrse y ejecutarse en el GPU. Sin embargo, la biblioteca no parece estar disponible públicamente.

https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html : extensión de lenguaje Java para construcciones OpenMP, con un backend CUDA

Bibliotecas de enlace Java OpenCL / CUDA

https://github.com/ochafik/JavaCL : enlaces de Java para OpenCL: una biblioteca de OpenCL orientada a objetos, basada en enlaces de bajo nivel generados automáticamente

http://jogamp.org/jocl/www/ : enlaces de Java para OpenCL: una biblioteca de OpenCL orientada a objetos, basada en enlaces de bajo nivel generados automáticamente

http://www.lwjgl.org/ : enlaces de Java para OpenCL: enlaces de bajo nivel generados automáticamente y clases de conveniencia orientadas a objetos

http://jocl.org/ : enlaces Java para OpenCL: enlaces de bajo nivel que son una asignación 1: 1 de la API original de OpenCL

http://jcuda.org/ : enlaces Java para CUDA: enlaces de bajo nivel que son un mapeo 1: 1 de la API original de CUDA

Diverso

http://sourceforge.net/projects/jopencl/ : enlaces de Java para OpenCL. Parece que ya no se mantiene desde 2010

http://www.hoopoe-cloud.com/ : enlaces de Java para CUDA. Parece que ya no se mantiene


Comenzaría usando uno de los proyectos que hay para Java y CUDA: http://www.jcuda.org/