Java >> Java tutorial >  >> Java

Brug af Java med Nvidia GPU'er (CUDA)

Først og fremmest skal du være opmærksom på, at CUDA ikke automatisk vil lave beregninger hurtigere. På den ene side, fordi GPU-programmering er en kunst, og det kan være meget, meget udfordrende at få det rigtigt . På den anden side, fordi GPU'er kun er velegnede til visse typer af beregninger.

Dette kan lyde forvirrende, fordi du dybest set kan beregne hvad som helst på GPU'en. Det centrale er selvfølgelig, om du opnår en god speedup eller ej. Den vigtigste klassifikation her er, om et problem er opgaveparallel eller data parallel . Den første refererer groft sagt til problemer, hvor flere tråde arbejder med deres egne opgaver, mere eller mindre uafhængigt. Den anden henviser til problemer, hvor mange tråde gør alle det samme - men på forskellige dele af dataene.

Sidstnævnte er den slags problem, som GPU'er er gode til:De har mange kerner, og alle kernerne gør det samme, men opererer på forskellige dele af inputdataene.

Du nævnte, at du har "simpel matematik, men med enorme mængder data". Selvom dette kan lyde som et perfekt dataparallelt problem og dermed som om det var velegnet til en GPU, er der et andet aspekt at overveje:GPU'er er latterligt hurtige med hensyn til teoretisk beregningskraft (FLOPS, Floating Point Operations Per Second). Men de er ofte droslet ned af hukommelsesbåndbredden.

Dette fører til en anden klassificering af problemer. Nemlig om problemer er hukommelsesbundne eller beregningsbundet .

Den første henviser til problemer, hvor antallet af instruktioner, der udføres for hvert dataelement, er lavt. Overvej f.eks. en parallelvektoraddition:Du bliver nødt til at læse to dataelementer, udfør derefter en enkelt tilføjelse og skriv summen ind i resultatvektoren. Du vil ikke se en speedup, når du gør dette på GPU'en, fordi den enkelte tilføjelse ikke kompenserer for indsatsen med at læse/skrive hukommelsen.

Det andet udtryk, "compute bound", refererer til problemer, hvor antallet af instruktioner er højt sammenlignet med antallet af hukommelseslæsninger/skrivninger. Overvej for eksempel en matrixmultiplikation:Antallet af instruktioner vil være O(n^3), når n er størrelsen af ​​matricen. I dette tilfælde kan man forvente, at GPU'en vil overgå en CPU ved en vis matrixstørrelse. Et andet eksempel kunne være, når mange komplekse trigonometriske beregninger (sinus/cosinus osv.) udføres på "få" dataelementer.

Som en tommelfingerregel:Du kan antage, at læsning/skrivning af et dataelement fra "hoved" GPU-hukommelsen har en latenstid på omkring 500 instruktioner...

Derfor er et andet nøglepunkt for GPU'ers ydeevne datalokalitet :Hvis du skal læse eller skrive data (og i de fleste tilfælde bliver du nødt til det;-)), så skal du sørge for at holde dataene så tæt som muligt på GPU-kernerne. GPU'er har således visse hukommelsesområder (benævnt "lokal hukommelse" eller "delt hukommelse"), som normalt kun er nogle få KB store, men som er særligt effektive til data, der er ved at blive involveret i en beregning.

Så for at understrege dette igen:GPU-programmering er en kunst, der kun er fjernt relateret til parallel programmering på CPU'en. Ting som Threads in Java, med al den samtidige infrastruktur som ThreadPoolExecutors , ForkJoinPools osv. kan give indtryk af, at du bare skal opdele dit arbejde på en eller anden måde og fordele det på flere processorer. På GPU'en kan du støde på udfordringer på et meget lavere niveau:Belægning, registertryk, delt hukommelsestryk, hukommelsessammensmeltning ... bare for at nævne nogle få.

Men når du har et dataparallelt, computerbundet problem at løse, er GPU'en vejen at gå.

En generel bemærkning:Du bad specifikt om CUDA. Men jeg vil stærkt anbefale dig også at tage et kig på OpenCL. Det har flere fordele. Først og fremmest er det en leverandøruafhængig, åben industristandard, og der er implementeringer af OpenCL af AMD, Apple, Intel og NVIDIA. Derudover er der en meget bredere understøttelse af OpenCL i Java-verdenen. Det eneste tilfælde, hvor jeg hellere vil nøjes med CUDA, er, når du vil bruge CUDA runtime-bibliotekerne, som CUFFT for FFT eller CUBLAS for BLAS (Matrix/Vector operations). Selvom der er metoder til at levere lignende biblioteker til OpenCL, kan de ikke direkte bruges fra Java-siden, medmindre du opretter dine egne JNI-bindinger til disse biblioteker.

Du vil måske også finde det interessant at høre, at OpenJDK HotSpot-gruppen i oktober 2012 startede projektet "Sumatra":http://openjdk.java.net/projects/sumatra/ . Målet med dette projekt er at give GPU-support direkte i JVM, med støtte fra JIT. Den aktuelle status og de første resultater kan ses på deres mailingliste på http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev

Men for et stykke tid siden samlede jeg nogle ressourcer relateret til "Java på GPU" generelt. Jeg vil opsummere disse igen her, uden nogen særlig rækkefølge.

(Ansvarsfraskrivelse :Jeg er forfatter til http://jcuda.org/ og http://jocl.org/ )

(Byte)kodeoversættelse og OpenCL-kodegenerering:

https://github.com/aparapi/aparapi :Et open source-bibliotek, der er oprettet og aktivt vedligeholdt af AMD. I en speciel "Kernel"-klasse kan man tilsidesætte en specifik metode, som skal udføres parallelt. Bytekoden for denne metode indlæses under kørsel ved hjælp af en egen bytekodelæser. Koden oversættes til OpenCL-kode, som derefter kompileres ved hjælp af OpenCL-kompileren. Resultatet kan derefter udføres på OpenCL-enheden, som kan være en GPU eller en CPU. Hvis kompileringen til OpenCL ikke er mulig (eller ingen OpenCL er tilgængelig), vil koden stadig blive eksekveret parallelt ved hjælp af en trådpulje.

https://github.com/pcpratts/rootbeer1 :Et open source-bibliotek til at konvertere dele af Java til CUDA-programmer. Det tilbyder dedikerede grænseflader, der kan implementeres for at indikere, at en bestemt klasse skal udføres på GPU'en. I modsætning til Aparapi forsøger den automatisk at serialisere de "relevante" data (det vil sige den fuldstændige relevante del af objektgrafen!) til en repræsentation, der er egnet til GPU'en.

https://code.google.com/archive/p/java-gpu/ :Et bibliotek til oversættelse af kommenteret Java-kode (med nogle begrænsninger) til CUDA-kode, som derefter kompileres til et bibliotek, der udfører koden på GPU'en. Biblioteket blev udviklet i forbindelse med en ph.d.-afhandling, som indeholder dyb baggrundsinformation om oversættelsesprocessen.

https://github.com/ochafik/ScalaCL :Scala-bindinger til OpenCL. Tillader, at specielle Scala-samlinger kan behandles parallelt med OpenCL. De funktioner, der kaldes på elementerne i samlingerne, kan være sædvanlige Scala-funktioner (med nogle begrænsninger), som derefter oversættes til OpenCL-kerner.

Sprogudvidelser

http://www.ateji.com/px/index.html :En sprogudvidelse til Java, der tillader parallelle konstruktioner (f.eks. parallel for loops, OpenMP-stil), som derefter udføres på GPU'en med OpenCL. Desværre er dette meget lovende projekt ikke længere vedligeholdt.

http://www.habanero.rice.edu/Publications.html (JCUDA) :Et bibliotek, der kan oversætte speciel Java-kode (kaldet JCUDA-kode) til Java- og CUDA-C-kode, som derefter kan kompileres og udføres på GPU. Biblioteket ser dog ikke ud til at være offentligt tilgængeligt.

https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html :Java-sprogudvidelse til OpenMP-konstruktioner med en CUDA-backend

Java OpenCL/CUDA-bindingsbiblioteker

https://github.com/ochafik/JavaCL :Java-bindinger til OpenCL:Et objektorienteret OpenCL-bibliotek, baseret på automatisk genererede lavniveaubindinger

http://jogamp.org/jocl/www/ :Java-bindinger til OpenCL:Et objektorienteret OpenCL-bibliotek, baseret på autogenererede lavniveaubindinger

http://www.lwjgl.org/ :Java-bindinger til OpenCL:Autogenererede lavniveaubindinger og objektorienterede bekvemmelighedsklasser

http://jocl.org/ :Java-bindinger til OpenCL:Lav-niveau-bindinger, der er en 1:1-mapping af den originale OpenCL API

http://jcuda.org/ :Java-bindinger til CUDA:Lav-niveau-bindinger, der er en 1:1-mapping af den originale CUDA API

Diverse

http://sourceforge.net/projects/jopencl/ :Java-bindinger til OpenCL. Ser ud til ikke længere at være vedligeholdt siden 2010

http://www.hoopoe-cloud.com/ :Java-bindinger til CUDA. Ser ud til ikke længere at blive vedligeholdt


Jeg ville starte med at bruge et af projekterne derude til Java og CUDA:http://www.jcuda.org/


Fra forskningen Jeg har gjort det, hvis du målretter mod Nvidia GPU'er og har besluttet at bruge CUDA over OpenCL, fandt jeg tre måder at bruge CUDA API i java.

  1. JCuda (eller alternativ)- http://www.jcuda.org/. Dette virker som den bedste løsning på de problemer, jeg arbejder på. Mange af biblioteker såsom CUBLAS er tilgængelige i JCuda. Kerner er dog stadig skrevet i C.
  2. JNI - JNI-grænseflader er ikke min favorit at skrive, men er meget kraftfulde og vil give dig mulighed for at gøre alt, hvad CUDA kan gøre.
  3. JavaCPP - Dette lader dig grundlæggende lave en JNI-grænseflade i Java uden at skrive C-kode direkte. Der er et eksempel her:Hvad er den nemmeste måde at køre fungerende CUDA-kode på i Java? hvordan man bruger dette med CUDA thrust. For mig ser det ud til, at du lige så godt bare kan skrive en JNI-grænseflade.

Alle disse svar er dybest set bare måder at bruge C/C++ kode på i Java. Du bør spørge dig selv, hvorfor du skal bruge Java, og om du ikke kan gøre det i C/C++ i stedet.

Hvis du kan lide Java og ved, hvordan man bruger det og ikke vil arbejde med al pointerstyring og hvad der ikke følger med C/C++, så er JCuda nok svaret. På den anden side kan CUDA Thrust-biblioteket og andre biblioteker som det bruges til at lave meget af pointer-styringen i C/C++, og måske skulle du se på det.

Hvis du kan lide C/C++ og ikke har noget imod pointerstyring, men der er andre begrænsninger, der tvinger dig til at bruge Java, så er JNI måske den bedste tilgang. Selvom dine JNI-metoder bare skal være indpakninger til kernekommandoer, kan du lige så godt bare bruge JCuda.

Der er et par alternativer til JCuda såsom Cuda4J og Root Beer, men de ser ikke ud til at blive vedligeholdt. Mens denne JCuda i skrivende stund understøtter CUDA 10.1. som er den mest opdaterede CUDA SDK.

Derudover er der et par java-biblioteker, der bruger CUDA, såsom deeplearning4j og Hadoop, som muligvis kan gøre det, du leder efter, uden at du behøver at skrive kernekode direkte. Jeg har dog ikke undersøgt dem for meget.


Java tag