Usando Java com GPU da Nvidia (cuda)

Estou trabalhando em um projeto de negócios que é feito em java e precisa de grande poder de computação para computar os mercados de negócios. Matemática simples, mas com enorme quantidade de dados.

Nós pedimos alguns cuda gpu para experimentá-lo com e desde que o Java não é suportado pelo cuda, estou querendo saber por onde começar. Devo criar uma interface JNI? Devo usar o JCUDA ou existem outras maneiras?

Eu não tenho experiência neste campo e gostaria que alguém me orientasse para algo para que eu pudesse começar a pesquisar e aprender.

Primeiro de tudo, você deve estar ciente do fato de que o CUDA não fará cálculos automáticos de maneira mais rápida. Por um lado, porque a programação da GPU é uma arte, e pode ser muito, muito desafiador para acertar . Por outro lado, porque as GPUs são adequadas apenas para certos tipos de cálculos.

Isso pode parecer confuso, porque basicamente você pode computar qualquer coisa na GPU. O ponto-chave é, claro, se você conseguirá uma boa aceleração ou não. A sorting mais importante aqui é se um problema é tarefa paralelo ou dados paralelos . O primeiro refere-se, grosso modo, a problemas em que vários segmentos estão trabalhando em suas próprias tarefas, mais ou menos independentemente. O segundo refere-se a problemas em que muitos segmentos estão todos fazendo o mesmo – mas em partes diferentes dos dados.

O último é o tipo de problema em que as GPUs são boas: elas têm muitos núcleos e todos os núcleos fazem o mesmo, mas operam em partes diferentes dos dados de input.

Você mencionou que tem “matemática simples, mas com grande quantidade de dados”. Embora isso possa soar como um problema perfeitamente paralelo de dados e, assim, como era adequado para uma GPU, há outro aspecto a considerar: GPUs são ridiculamente rápidas em termos de poder computacional teórico (FLOPS, operações de ponto flutuante por segundo). Mas eles são freqüentemente limitados pela largura de banda da memory.

Isso leva a outra sorting de problemas. Ou seja, se os problemas são ligados à memory ou ligados à computação .

O primeiro refere-se a problemas em que o número de instruções que são feitas para cada elemento de dados é baixo. Por exemplo, considere uma adição de vetor paralelo: você terá que ler dois elementos de dados, realizar uma única adição e, em seguida, gravar a sum no vetor de resultado. Você não verá uma aceleração ao fazer isso na GPU, porque a adição única não compensa os esforços de leitura / gravação da memory.

O segundo termo, “computar vinculado”, refere-se a problemas em que o número de instruções é alto em comparação com o número de leituras / gravações de memory. Por exemplo, considere uma multiplicação de matriz: O número de instruções será O (n ^ 3) quando n é o tamanho da matriz. Nesse caso, pode-se esperar que a GPU supere a CPU em um determinado tamanho de matriz. Outro exemplo poderia ser quando muitos cálculos trigonométricos complexos (seno / coseno etc) são executados em “poucos” elementos de dados.

Como regra geral: Você pode assumir que a leitura / gravação de um elemento de dados da memory “principal” da GPU tem uma latência de aproximadamente 500 instruções ….

Portanto, outro ponto-chave para o desempenho das GPUs é a localização dos dados : Se você tiver que ler ou gravar dados (e, na maioria dos casos, você terá que ;-)), certifique-se de que os dados sejam mantidos o mais próximo possível. possível para os núcleos da GPU. Portanto, as GPUs têm determinadas áreas de memory (chamadas de “memory local” ou “memory compartilhada”) que geralmente têm apenas alguns KB de tamanho, mas são particularmente eficientes para dados que estão prestes a ser envolvidos em uma computação.

Então, para enfatizar isso novamente: a programação da GPU é uma arte, que está apenas remotamente relacionada à programação paralela na CPU. Coisas como Threads em Java, com toda a infra-estrutura de concorrência como ThreadPoolExecutors , ForkJoinPools etc., podem dar a impressão de que você só precisa dividir seu trabalho de alguma forma e distribuí-lo entre vários processadores. Na GPU, você pode encontrar desafios em um nível muito mais baixo: ocupação, pressão de registro, pressão de memory compartilhada, aglutinação de memory … só para citar alguns.

No entanto, quando você tem um problema de paralelismo de dados, ligado a computação, a GPU é o caminho a percorrer.


Uma observação geral: Você pediu especificamente por CUDA. Mas eu recomendo fortemente que você também dê uma olhada no OpenCL. Tem várias vantagens. Primeiro de tudo, é um padrão aberto da indústria e independente do fornecedor, e há implementações do OpenCL da AMD, Apple, Intel e NVIDIA. Além disso, há um suporte muito mais amplo para o OpenCL no mundo Java. O único caso em que eu prefiro resolver para CUDA é quando você deseja usar as bibliotecas de tempo de execução CUDA, como CUFFT para FFT ou CUBLAS para BLAS (operações Matrix / Vector). Embora existam abordagens para fornecer bibliotecas semelhantes para o OpenCL, elas não podem ser usadas diretamente do lado do Java, a menos que você crie suas próprias ligações de JNI para essas bibliotecas.


Você também pode achar interessante saber que em outubro de 2012, o grupo OpenJDK HotSpot iniciou o projeto “Sumatra”: http://openjdk.java.net/projects/sumatra/ . O objective deste projeto é fornecer suporte à GPU diretamente na JVM, com suporte do JIT. O status atual e os primeiros resultados podem ser vistos em sua lista de discussão em http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev


No entanto, há algum tempo, coletei alguns resources relacionados a “Java na GPU” em geral. Vou resumir isso de novo aqui, em nenhuma ordem particular.

( Isenção de responsabilidade : Eu sou o autor de http://jcuda.org/ e http://jocl.org/ )

Conversão de código (Byte) e geração de código OpenCL:

https://github.com/aparapi/aparapi : Uma biblioteca de código aberto que é criada e ativamente mantida pela AMD. Em uma class especial “Kernel”, pode-se replace um método específico que deve ser executado em paralelo. O código de byte deste método é carregado em tempo de execução usando um próprio leitor de bytecode. O código é traduzido em código OpenCL, que é então compilado usando o compilador OpenCL. O resultado pode então ser executado no dispositivo OpenCL, que pode ser uma GPU ou uma CPU. Se a compilation em OpenCL não for possível (ou se não houver OpenCL disponível), o código ainda será executado em paralelo, usando um pool de threads.

https://github.com/pcpratts/rootbeer1 : Uma biblioteca de código aberto para converter partes de Java em programas CUDA. Ele oferece interfaces dedicadas que podem ser implementadas para indicar que uma determinada class deve ser executada na GPU. Em contraste com Aparapi, ele tenta serializar automaticamente os dados “relevantes” (ou seja, a parte relevante completa do gráfico de objects!) Em uma representação adequada para a GPU.

https://code.google.com/archive/p/java-gpu/ : Uma biblioteca para traduzir o código Java anotado (com algumas limitações) no código CUDA, que é então compilado em uma biblioteca que executa o código na GPU. A Biblioteca foi desenvolvida no contexto de uma tese de doutorado, que contém informações profundas sobre o processo de tradução.

https://github.com/ochafik/ScalaCL : Ligações do Scala para o OpenCL. Permite que collections especiais da Scala sejam processadas em paralelo com o OpenCL. As funções que são chamadas nos elementos das collections podem ser funções Scala usuais (com algumas limitações) que são então traduzidas em kernels OpenCL.

Extensões de linguagem

http://www.ateji.com/px/index.html : Uma extensão de linguagem para Java que permite construções paralelas (por exemplo, loops paralelos, estilo OpenMP) que são executados na GPU com OpenCL. Infelizmente, este projeto muito promissor não é mais mantido.

http://www.habanero.rice.edu/Publications.html (JCUDA): Uma biblioteca que pode traduzir código Java especial (chamado código JCUDA) em código Java e CUDA-C, que pode ser compilado e executado no GPU. No entanto, a biblioteca não parece estar disponível publicamente.

https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html : Extensão de linguagem Java para construções OpenMP, com um backend CUDA

Bibliotecas de binding Java OpenCL / CUDA

https://github.com/ochafik/JavaCL : Ligações Java para OpenCL: Uma biblioteca OpenCL orientada a object, baseada em ligações de baixo nível geradas automaticamente

http://jogamp.org/jocl/www/ : Ligações Java para OpenCL: Uma biblioteca OpenCL orientada a object, baseada em ligações de baixo nível geradas automaticamente

http://www.lwjgl.org/ : Ligações Java para OpenCL: Ligações de baixo nível geradas automaticamente e classs de conveniência orientadas a objects

http://jocl.org/ : Ligações Java para OpenCL: Ligações de baixo nível que são um mapeamento 1: 1 da API OpenCL original

http://jcuda.org/ : Ligações Java para CUDA: Ligações de baixo nível que são um mapeamento 1: 1 da API CUDA original

Diversos

http://sourceforge.net/projects/jopencl/ : Ligações Java para OpenCL. Parece não ser mais mantido desde 2010

http://www.hoopoe-cloud.com/ : Ligações Java para CUDA. Parece não ser mais mantido


Eu começaria usando um dos projetos para Java e CUDA: http://www.jcuda.org/