Saltar al contenido

Uso de Java con GPU de Nvidia (CUDA)

Solución:

En primer lugar, debe tener en cuenta el hecho de que CUDA no hará los cálculos automágicamente más rápido. Por un lado, porque la programación de GPU es un arte y puede ser muy, muy difícil conseguirla. Derecha. Por otro lado, debido a que las GPU son adecuadas solo para ciertos tipos de cálculos.

Esto puede parecer 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, a grandes rasgos, a problemas en los que varios subprocesos están trabajando en sus propias tareas, de forma más o menos independiente. El segundo se refiere a problemas donde muchos los hilos son todos 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 una gran cantidad de datos”. Aunque esto puede parecer un problema perfectamente paralelo de datos y, por lo tanto, como si fuera adecuado para una GPU, hay otro aspecto a considerar: las GPU son ridículamente rápidas en términos de potencia computacional teórica (FLOPS, operaciones de punto flotante por segundo). Pero a menudo se ven limitados por el ancho de banda de la memoria.

Esto conduce a otra clasificación de problemas. Es decir, si los problemas son límite de memoria o calcular límite.

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

El segundo término, “límite de cálculo”, se refiere a problemas en los que 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 cierto 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 asumir que leer / escribir un elemento de datos de la memoria “principal” de la GPU tiene una latencia de aproximadamente 500 instrucciones …

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

Entonces, para enfatizar esto nuevamente: la programación de GPU es un arte, que solo está relacionado de forma remota con la programación en paralelo en la CPU. Cosas como Threads en Java, con toda la infraestructura de concurrencia como ThreadPoolExecutors, ForkJoinPools etc. puede dar la impresión de que solo tiene que dividir su trabajo de alguna manera y distribuirlo entre varios procesadores. En la GPU, puede encontrar 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 procesamiento paralelo de datos, la GPU es el camino a seguir.


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


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


Sin embargo, hace un tiempo, recopilé algunos recursos relacionados con “Java en la GPU” en general. Los resumiré nuevamente aquí, sin ningún orden en particular.

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

Traducción de código (Byte) 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 códigos de bytes propio. El código se traduce en código OpenCL, que luego se compila utilizando el compilador OpenCL. El resultado se puede ejecutar en el dispositivo OpenCL, que puede ser una GPU o una CPU. Si la compilación en OpenCL no es posible (o no hay OpenCL disponible), el código aún se ejecutará en paralelo, utilizando un Thread Pool.

https://github.com/pcpratts/rootbeer1: una biblioteca de código abierto para convertir partes de Java en programas CUDA. Ofrece interfaces dedicadas que pueden implementarse para indicar que se debe ejecutar una determinada clase 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 adecuada para la GPU.

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

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

Extensiones de idioma

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

http://www.habanero.rice.edu/Publications.html (JCUDA): una biblioteca que puede traducir código Java especial (llamado código JCUDA) a código Java y CUDA-C, que luego se puede compilar y ejecutar 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 del lenguaje Java para construcciones OpenMP, con un backend CUDA

Bibliotecas de enlace Java OpenCL / CUDA

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

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

http://www.lwjgl.org/: enlaces 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 OpenCL original

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

Diverso

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

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


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

Desde el investigar Ya lo hice, si está apuntando a las GPU de Nvidia y ha decidido usar CUDA sobre OpenCL, encontré tres formas de usar la API de CUDA en java.

  1. JCuda (o alternativa) – http://www.jcuda.org/. Esta parece la mejor solución para los problemas en los que estoy trabajando. Muchas de las bibliotecas, como CUBLAS, están disponibles en JCuda. Sin embargo, los núcleos todavía están escritos en C.
  2. JNI – Las interfaces JNI no son mis favoritas para escribir, pero son muy poderosas y le permitirían hacer cualquier cosa que pueda hacer CUDA.
  3. JavaCPP: esto básicamente le permite crear una interfaz JNI en Java sin escribir código C directamente. Aquí hay un ejemplo: ¿Cuál es la forma más sencilla de ejecutar código CUDA funcional en Java? de cómo usar esto con CUDA thrust. Para mí, esto parece que también podría escribir una interfaz JNI.

Todas estas respuestas básicamente son solo formas de usar código C / C ++ en Java. Debería preguntarse por qué necesita usar Java y si no puede hacerlo en C / C ++.

Si le gusta Java y sabe cómo usarlo y no quiere trabajar con toda la administración de punteros y todo lo que viene con C / C ++, entonces JCuda es probablemente la respuesta. Por otro lado, la biblioteca CUDA Thrust y otras bibliotecas como esta se pueden usar para hacer gran parte de la administración de punteros en C / C ++ y tal vez debería mirar eso.

Si le gusta C / C ++ y no le importa la administración de punteros, pero existen otras restricciones que lo obligan a usar Java, entonces JNI podría ser el mejor enfoque. Sin embargo, si sus métodos JNI solo van a ser envoltorios para los comandos del kernel, también puede usar JCuda.

Hay algunas alternativas a JCuda como Cuda4J y Root Beer, pero parece que no se mantienen. Mientras que en el momento de redactar este documento, JCuda es compatible con CUDA 10.1. que es el SDK de CUDA más actualizado.

Además, hay algunas bibliotecas java que usan CUDA, como deeplearning4j y Hadoop, que pueden hacer lo que está buscando sin necesidad de escribir código del kernel directamente. Sin embargo, no los he investigado demasiado.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *