Przeglądając angielską wiki na temat CUDA, zauważyłem, że są dostępne 4 biblioteki dla Javy, współpracujące z technologią Nvidi: jCUDA, JCuda, JCublas, JCufft. Jako że moja karta graficzna (GeForce 9600M GT) w laptopie obsługuje CUDA w wersji 1.1 (aktualne dane można sprawdzić na stronie CUDA GPUs), postanowiłem trochę się pobawić tymi klockami i sprawić aby się choć trochę spociła.
Przyglądając się bliżej tym czterem bibliotekom Javowym, okazało się, że tak właściwie są to 2 projekty: jCUDA i JCuda. Pozostałe: JCublas i JCufft są de facto podprojektami JCuda. JCublas zajmuje się operacjami wektorowymi i macierzowymi. Natomiast JCufft zajmuje się szybką transformacją Fouriera. Ten kto kończył studia na Wydziale Elektroniki PWr, lub coś co jest związane z elektroniką, to wie o co chodzi ;) Druciarze rządzą!
Ściągnąłem więc na początek jCUDA wersję na Windowsa 64 bit.
Następnie utworzyłem projekt w IntelliJ Idea, skopiowałem zawartość katalogu examples (ze ściągniętego ZIP’a ) do src, a jcuda.jar, jcuda.dll i jcudafft.dll do lib i dodałem do projektu ze scope’m: "Compile"
Pierwsze uruchomienie klasy EnumDevices (widzę że jest tam jakiś main()) i dostajemy stacktrace’a:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no jcuda in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860) at java.lang.Runtime.loadLibrary0(Runtime.java:845) at java.lang.System.loadLibrary(System.java:1084) at jcuda.driver.CUDADriver.(CUDADriver.java:909) at jcuda.CUDA.init(CUDA.java:62) at jcuda.CUDA.(CUDA.java:42) at examples.EnumDevices.main(EnumDevices.java:20) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Myślałem początkowo, że to podczas uruchomienia, aplikacja nie widzi dołączanych JAR’ów. Zmiana scope’a na Runtime, powoduje jednak, że IDEA nie widzi biblioteki podczas kompilacji:
Wróciłem wiec do zasięgu compile. Nie chcąc grzebać sobie zbytnio w classpath’ie, ustawiłem właściwość java.library.path w konfiguracji uruchomieniowej wspomnianej klasy na katalog z wymaganymi dll’kami:
Po ponownym uruchomieniu otrzymałem ten oto stos wywołań:
Exception in thread "main" java.lang.UnsatisfiedLinkError: D:\Java_programy\IdeaWorkspace\jCUDAFirstExample\lib\jcuda.dll: Can't load AMD 64-bit .dll on a IA 32-bit platform at java.lang.ClassLoader$NativeLibrary.load(Native Method) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1803) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1728) at java.lang.Runtime.loadLibrary0(Runtime.java:823) at java.lang.System.loadLibrary(System.java:1028) at jcuda.driver.CUDADriver.(CUDADriver.java:909) at jcuda.CUDA.init(CUDA.java:62) at jcuda.CUDA.(CUDA.java:42) at examples.EnumDevices.main(EnumDevices.java:20) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Hmmm, wersje systemu mam 64 bitową, ale nie na procku AMD tylko Intel Core 2 Duo T6400 2.00 GHz.
Dobra ściągamy wersję 32 bitową na Winde. Podmiana plików, przebudowa projektu i Voila:
Total number of devices: 1
Name: GeForce 9600M GT
Version: 1.1
Clock rate: 1250000 MHz
Threads per block: 512
W przykładowym kodzie znajduje się jeszcze klasa LoadModule, uruchamiam więc:
Exception in thread "main" CUDA Driver error: 301 at jcuda.CUDA.setError(CUDA.java:1874) at jcuda.CUDA.loadModule(CUDA.java:375) at examples.LoadModule.main(LoadModule.java:36) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
I idę do linii gdzie poleciał wyjątek:
File cubinFile = new File("resources", "test_module.cubin"); cuda.loadModule(cubinFile.getAbsolutePath());
W archiwum z biblioteką jest jeszcze katalog resource, wiec kopiuję go do projektu i dołączam jako zasób:
Od razu nowe skróty klawiaturowe do zapamiętania, dostępne w oknie Project Structure - Modules - dodaj jako: Alt + S Sources, Alt + T Test Sources, Alt + E Excluded.
I otrzymujemy miły komunikat po uruchomieniu:
Test passed
Z ciekawości jeszcze zajrzałem co się znajduje w podpakiecie driver przykładowego kodu. Po uruchomieniu okazuje się, że aplikacje wypisują to samo na ekran. Porównałem kdiff3’em, co się zmienia w kodzie. Jak można przeczytać w release note w pliku readme.txt od wersji 1.1 biblioteki wprowadzono bardziej obiektowy sposób korzystania z biblioteki, aby nam Javowcom było łatwiej pracować :)
Added object oriented support for jCUDA and CUDA interface.
Niestety aby dowiedzieć się coś więcej o tej bibliotece, trzeba się zarejestrować na stronie producenta. Próbowałem z dwóch kont (gmail’owe i studenckie), ale zero odzewu. Później się doszukałem, że rejestracja jest tymczasowo nieaktywna i ze wyślą e-mail’a aktywacyjnego, gdy rejestracja będzie znów dostępna…
Jako, ze nie chcę pracować z biblioteką do której nie ma ogólnodostępnej dokumentacji, a rejestracja nic nie daje, wziąłem się za drugą dostępną bibliotekę JCuda. Nauczony powyższym doświadczeniem od razu ściągnąłem wersję 4.0 32 bitową, dokumentację (są javadoc’i) i kod źródłowy. Początkowo postanowiłem przejrzeć przykłady: JCuda Code samples.
Zacząłem od pierwszego JCublasSample. Ściągnąłem, skopiowałem do src i to co było w ZIPie z JCudą skopiowałem do lib w moim projekcie. Następnie dodałem je jako zależności z zakresem Compile.
Pierwsze uruchomienie i oczywiście stacktrace:
Creating input data... Performing Sgemm with Java... Performing Sgemm with JCublas... Error while loading native library "JCublas-windows-x86_64" with base name "JCublas" Operating system name: Windows 7 Architecture : amd64 Architecture bit size: 64 Stack trace from the attempt to load the library as a resource: java.lang.NullPointerException: No resource found with name '/lib/JCublas-windows-x86_64.dll' at jcuda.LibUtils.loadLibraryResource(LibUtils.java:144) at jcuda.LibUtils.loadLibrary(LibUtils.java:80) at jcuda.jcublas.JCublas.initialize(JCublas.java:94) at jcuda.jcublas.JCublas.(JCublas.java:82) at JCublasSample.sgemmJCublas(JCublasSample.java:64) at JCublasSample.testSgemm(JCublasSample.java:49) at JCublasSample.main(JCublasSample.java:25) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) Stack trace from the attempt to load the library as a file: java.lang.UnsatisfiedLinkError: no JCublas-windows-x86_64 in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860) at java.lang.Runtime.loadLibrary0(Runtime.java:845) at java.lang.System.loadLibrary(System.java:1084) at jcuda.LibUtils.loadLibrary(LibUtils.java:90) at jcuda.jcublas.JCublas.initialize(JCublas.java:94) at jcuda.jcublas.JCublas.(JCublas.java:82) at JCublasSample.sgemmJCublas(JCublasSample.java:64) at JCublasSample.testSgemm(JCublasSample.java:49) at JCublasSample.main(JCublasSample.java:25) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) Exception in thread "main" java.lang.UnsatisfiedLinkError: Could not load the native library at jcuda.LibUtils.loadLibrary(LibUtils.java:122) at jcuda.jcublas.JCublas.initialize(JCublas.java:94) at jcuda.jcublas.JCublas.(JCublas.java:82) at JCublasSample.sgemmJCublas(JCublasSample.java:64) at JCublasSample.testSgemm(JCublasSample.java:49) at JCublasSample.main(JCublasSample.java:25) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) Process finished with exit code 1
Z czego interesujące jest to:
no JCublas-windows-x86_64 in java.library.path
problem rozwiązuję jak w przypadku pierwszej biblioteki, czyli ustawiam właściwość java.library.path dla wirtualnej maszyny Javy w konfiguracji uruchomieniowej przykładu. Uruchamiam i ten sam błąd. Przyglądam się bliżej nazwie brakującego pliku: JCublas-windows-x86_64. Okazuje się, że w wskazanym katalogu mam taki plik ale bez przyrostka _64, czyli pewnie framework, na podstawie wyświetlanej wartości „Architecture: amd64” poszukuje automatycznie bibliotek 64bitowych. No dobra, ściągnąłem, podmieniłem, uruchamiam i znów myślałem, że dostałem to samo.
Jednak gdy się bliżej przyjrzałem, to się okazało ze jest trochę odmienny:
java.lang.UnsatisfiedLinkError: D:\Java_programy\IdeaWorkspace\JCudaExample01\lib\JCublas-windows-x86_64.dll: Can't find dependent libraries
Zajrzałem więc do JCuda Tutorial’a i tamta strona zaprowadziła mnie do JCuda FAQ na jakimś formu:
Type 2: UnsatisfiedLinkError: Can't find dependent libraries
Czyli jednak czegoś nie doinstalowałem związanego bezpośrednio z CUDĄ. Ściągnąłem więc CUDA Toolkit (64 bit) zainstalowałem i uruchomiłem ponownie komputer. Po odpaleniu aplikacji dostałem:
Creating input data... Performing Sgemm with Java... Performing Sgemm with JCublas... testSgemm FAILED
Już jest lepiej, ale chyba coś dalej jest nie tak. Zajrzałem w kod i się okazało, że przykładowa aplikacja korzysta z biblioteki JCublas, a ta wykorzystuje cuBLAS (ang. CUDA Basic Linear Algebra Subroutines), czyli bibliotekę wspomagającą podstawowe obliczenia algebry liniowej. Nie chciało mi się zgłębiać co jest nie tak i uruchomiłem inny przykład.
Klasa JCufftSample wymaga do uruchomienia dodatkowo javovej biblioteki JTransforms, która to liczy m.in. transformatę Fouriera. Uruchomienie tego przykładu wykrywało w moim przypadku architekturę 32 bitową:
Operating system name: Windows 7 Architecture : x86 Architecture bit size: 32
No i oczywiście sypało wyjątkami. Postanowiłem więc w katalogu lib trzymać zarówno 64 bitowe jaki 32 bitowe dll’ki. Prawdopodobnie dzięki temu udało mi się wywalić wirtualną maszynę:
# # A fatal error has been detected by the Java Runtime Environment: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0ad2de60, pid=4376, tid=6112 # # JRE version: 6.0_22-b04 # Java VM: Java HotSpot(TM) Client VM (17.1-b03 mixed mode, sharing windows-x86 )
Trochę zniechęcająca jest praca z tymi bibliotekami. Ale tak to zazwyczaj jest, jak się bawimy ze sprzętem, że na początku bardzo ciężko się z nim dogadać, a później to już jakoś lepiej idzie.
Jako że początkowo korzystałem z bibliotek w wersji 0.4.0-beta1 dla CUDA 4.0 postanowiłem zrobić downgrade do JCuda 0.3.2a. dla CUDA 3.2. Taka konfiguracja sprawia mi najmniej problemów. JCufftSample zaczął działać, i najprostszy, podstawowy test z tutorial’a też.
Proponuję wszystkim zaczynać od tego przykładu, gdyż ten test alokuje pewną pamięć w przestrzeni GPU i ją zwalnia – nic więcej.
Teraz to by było na tyle odnośnie cudów. Kolejne porady, co jeszcze należy doinstalować i jak przygotować środowisko do pracy z tym wynalazkiem w kolejnej części.
No i przepraszam za rozjechane listingi kodu pod Chrome'm, ale coś SyntaxHighlighter się pod tą przeglądarką rozjeżdża.
OpenCL to z tego co pamiętam alternatywa dla CUDA nVidii. W OpenCLu można (wieloplatformowo?) pisać także dla kart ATi. Polecam czystą CUDĘ dla C/C++ i VisualStudio.
OdpowiedzUsuńRzeczywiście. Musiałem niedokładnie doczytać / zrozumieć, gdy o tym czytałem. Tekst poprawiony.
UsuńZakład Teorii Obwodów - przy tych panach, Katyń wydaje się być weekendowym wypadem do kolegów z Rosji.
OdpowiedzUsuńWidzę, że inni również mają niemiłe wspomnienia z ekipą ZTO.
Usuń