Menggunakan Java dengan GPU Nvidia (CUDA)

144

Saya sedang mengerjakan proyek bisnis yang dilakukan di Jawa, dan perlu daya komputasi yang besar untuk menghitung pasar bisnis. Matematika sederhana, tetapi dengan sejumlah besar data.

Kami memesan beberapa GPU CUDA untuk mencobanya dan karena Java tidak didukung oleh CUDA, saya bertanya-tanya harus mulai dari mana. Haruskah saya membangun antarmuka JNI? Haruskah saya menggunakan JCUDA atau adakah cara lain?

Saya tidak memiliki pengalaman dalam bidang ini dan saya ingin jika seseorang dapat mengarahkan saya ke sesuatu sehingga saya dapat mulai meneliti dan belajar.

Hans
sumber
2
GPU akan membantu Anda mempercepat jenis masalah komputasi-intensif tertentu. Namun jika Anda memiliki sejumlah besar data, Anda lebih cenderung terikat IO. Kemungkinan besar GPU bukan solusinya.
masak steve
1
"Meningkatkan Kinerja Java menggunakan GPGPU" -> arxiv.org/abs/1508.06791
BlackBear
4
Semacam pertanyaan terbuka, saya senang para mod tidak menutupnya karena jawaban dari Marco13 sangat membantu! Harus menjadi wiki IMHO
JimLohse

Jawaban:

442

Pertama-tama, Anda harus menyadari fakta bahwa CUDA tidak akan secara otomatis membuat perhitungan lebih cepat. Di satu sisi, karena pemrograman GPU adalah seni, dan bisa sangat, sangat menantang untuk memperbaikinya . Di sisi lain, karena GPU hanya cocok untuk jenis komputasi tertentu .

Ini mungkin terdengar membingungkan, karena pada dasarnya Anda dapat menghitung apa pun di GPU. Kuncinya adalah, tentu saja, apakah Anda akan mencapai kecepatan yang baik atau tidak. Klasifikasi paling penting di sini adalah apakah suatu masalah adalah tugas paralel atau data paralel . Yang pertama merujuk, secara kasar, ke masalah di mana beberapa utas sedang mengerjakan tugas mereka sendiri, kurang lebih secara mandiri. Yang kedua mengacu pada masalah di mana banyak utas melakukan hal yang sama - tetapi pada bagian data yang berbeda.

Yang terakhir adalah jenis masalah yang dimiliki GPU dengan baik: GPU memiliki banyak core, dan semua core melakukan hal yang sama, tetapi beroperasi pada bagian input data yang berbeda.

Anda menyebutkan bahwa Anda memiliki "matematika sederhana tetapi dengan sejumlah besar data". Meskipun ini mungkin terdengar seperti masalah paralel-data yang sempurna dan karenanya cocok untuk GPU, ada aspek lain yang perlu dipertimbangkan: GPU sangat cepat dalam hal kekuatan komputasi teoretis (FLOPS, Operasi Titik Apung Per Detik). Tetapi mereka sering dibatasi oleh bandwidth memori.

Ini mengarah pada klasifikasi masalah lain. Yaitu apakah masalah terikat memori atau terikat komputasi .

Yang pertama mengacu pada masalah di mana jumlah instruksi yang dilakukan untuk setiap elemen data rendah. Misalnya, pertimbangkan penambahan vektor paralel: Anda harus membaca dua elemen data, lalu melakukan penambahan tunggal, dan kemudian menulis jumlahnya ke dalam vektor hasil. Anda tidak akan melihat speedup ketika melakukan ini pada GPU, karena penambahan tunggal tidak mengimbangi upaya membaca / menulis memori.

Istilah kedua, "compute bound", mengacu pada masalah di mana jumlah instruksi tinggi dibandingkan dengan jumlah memori yang dibaca / ditulis. Sebagai contoh, pertimbangkan perkalian matriks: Jumlah instruksi akan menjadi O (n ^ 3) ketika n adalah ukuran dari matriks. Dalam hal ini, orang dapat berharap bahwa GPU akan mengungguli CPU pada ukuran matriks tertentu. Contoh lain dapat terjadi ketika banyak perhitungan trigonometri kompleks (sinus / cosinus dll) dilakukan pada "beberapa" elemen data.

Sebagai aturan praktis: Anda dapat berasumsi bahwa membaca / menulis satu elemen data dari memori GPU "utama" memiliki latensi sekitar 500 instruksi ....

Oleh karena itu, titik kunci lain untuk kinerja GPU adalah lokalitas data : Jika Anda harus membaca atau menulis data (dan dalam kebanyakan kasus, Anda harus ;-)), maka Anda harus memastikan bahwa data disimpan sedekat mungkin dengan mungkin untuk core GPU. GPU dengan demikian memiliki area memori tertentu (disebut "memori lokal" atau "memori bersama") yang biasanya hanya berukuran beberapa KB, tetapi sangat efisien untuk data yang akan terlibat dalam perhitungan.

Jadi untuk menekankan ini lagi: Pemrograman GPU adalah seni, yang hanya terkait jarak jauh dengan pemrograman paralel pada CPU. Hal-hal seperti Threads in Java, dengan semua infrastruktur konkurensi seperti ThreadPoolExecutors, ForkJoinPoolsdll. Mungkin memberi kesan bahwa Anda hanya perlu membagi pekerjaan Anda dan mendistribusikannya di antara beberapa prosesor. Pada GPU, Anda mungkin menghadapi tantangan pada level yang jauh lebih rendah: Hunian, tekanan register, tekanan memori bersama, penggabungan memori ... hanya untuk beberapa nama.

Namun, ketika Anda memiliki masalah paralel-data, komputasi-terikat untuk dipecahkan, GPU adalah cara untuk pergi.


Komentar umum: Anda secara khusus meminta CUDA. Tapi saya sangat menyarankan Anda untuk melihat-lihat OpenCL. Ini memiliki beberapa keunggulan. Pertama-tama, ini adalah standar industri terbuka yang independen terhadap vendor, dan ada implementasi OpenCL oleh AMD, Apple, Intel dan NVIDIA. Selain itu, ada dukungan yang jauh lebih luas untuk OpenCL di dunia Java. Satu-satunya kasus di mana saya lebih suka menerima CUDA adalah ketika Anda ingin menggunakan pustaka runtime CUDA, seperti CUFFT untuk FFT atau CUBLAS untuk BLAS (operasi Matriks / Vektor). Meskipun ada pendekatan untuk menyediakan pustaka yang serupa untuk OpenCL, mereka tidak dapat langsung digunakan dari sisi Java, kecuali jika Anda membuat binding JNI Anda sendiri untuk pustaka ini.


Anda juga mungkin tertarik mendengarnya pada bulan Oktober 2012, grup OpenJDK HotSpot memulai proyek "Sumatra": http://openjdk.java.net/projects/sumatra/ . Tujuan dari proyek ini adalah untuk memberikan dukungan GPU langsung di JVM, dengan dukungan dari JIT. Status saat ini dan hasil pertama dapat dilihat di milis mereka di http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev


Namun, beberapa waktu yang lalu, saya mengumpulkan beberapa sumber yang terkait dengan "Java on the GPU" secara umum. Saya akan meringkas ini lagi di sini, tanpa urutan tertentu.

( Penafian : Saya penulis http://jcuda.org/ dan http://jocl.org/ )

(Byte) terjemahan kode dan pembuatan kode OpenCL:

https://github.com/aparapi/aparapi : Perpustakaan open-source yang dibuat dan dikelola secara aktif oleh AMD. Dalam kelas "Kernel" khusus, seseorang dapat mengganti metode tertentu yang harus dijalankan secara paralel. Kode byte dari metode ini dimuat pada saat runtime menggunakan bytecode reader sendiri. Kode tersebut diterjemahkan ke dalam kode OpenCL, yang kemudian dikompilasi menggunakan kompiler OpenCL. Hasilnya kemudian dapat dieksekusi pada perangkat OpenCL, yang mungkin berupa GPU atau CPU. Jika kompilasi ke OpenCL tidak dimungkinkan (atau tidak ada OpenCL tersedia), kode masih akan dieksekusi secara paralel, menggunakan Thread Pool.

https://github.com/pcpratts/rootbeer1 : Perpustakaan open-source untuk mengubah bagian Jawa menjadi program CUDA. Ini menawarkan antarmuka khusus yang dapat diimplementasikan untuk menunjukkan bahwa kelas tertentu harus dijalankan pada GPU. Berbeda dengan Aparapi, ia mencoba untuk membuat serialisasi data "relevan" secara otomatis (yaitu, bagian relevan yang lengkap dari grafik objek!) Ke dalam representasi yang sesuai untuk GPU.

https://code.google.com/archive/p/java-gpu/ : Perpustakaan untuk menerjemahkan kode Java beranotasi (dengan beberapa batasan) menjadi kode CUDA, yang kemudian dikompilasi ke perpustakaan yang mengeksekusi kode pada GPU. Perpustakaan dikembangkan dalam konteks tesis PhD, yang berisi informasi latar belakang mendalam tentang proses penerjemahan.

https://github.com/ochafik/ScalaCL : Scala binding untuk OpenCL. Mengizinkan koleksi Scala khusus diproses secara paralel dengan OpenCL. Fungsi-fungsi yang dipanggil pada elemen-elemen koleksi dapat berupa fungsi Scala biasa (dengan beberapa batasan) yang kemudian diterjemahkan ke dalam kernel OpenCL.

Ekstensi bahasa

http://www.ateji.com/px/index.html : Ekstensi bahasa untuk Java yang memungkinkan konstruksi paralel (misalnya paralel untuk loop, gaya OpenMP) yang kemudian dieksekusi pada GPU dengan OpenCL. Sayangnya, proyek yang sangat menjanjikan ini tidak lagi dipertahankan.

http://www.habanero.rice.edu/Publications.html (JCUDA): Perpustakaan yang dapat menerjemahkan Kode Java khusus (disebut kode JCUDA) menjadi kode Java- dan CUDA-C, yang kemudian dapat dikompilasi dan dieksekusi pada GPU. Namun, perpustakaan sepertinya tidak tersedia untuk umum.

https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html : Ekstensi bahasa Java untuk konstruksi OpenMP, dengan backend CUDA

Java OpenCL / CUDA binding libraries

https://github.com/ochafik/JavaCL : Java bindings untuk OpenCL: Pustaka OpenCL yang berorientasi objek, berdasarkan pada binding tingkat rendah yang dihasilkan secara otomatis

http://jogamp.org/jocl/www/ : Java binding untuk OpenCL: Pustaka OpenCL yang berorientasi objek, berdasarkan binding level rendah yang dihasilkan secara otomatis

http://www.lwjgl.org/ : Java bindings untuk OpenCL: Bindings level rendah yang dihasilkan secara otomatis dan kelas kenyamanan berorientasi objek

http://jocl.org/ : Java bindings untuk OpenCL: binding tingkat rendah yang merupakan pemetaan 1: 1 dari API OpenCL asli

http://jcuda.org/ : Java bindings untuk CUDA: binding level rendah yang merupakan pemetaan 1: 1 dari API CUDA asli

Lain-lain

http://sourceforge.net/projects/jopencl/ : Java bindings untuk OpenCL. Tampaknya tidak lagi dipertahankan sejak 2010

http://www.hoopoe-cloud.com/ : Java bindings untuk CUDA. Tampaknya tidak lagi dipertahankan


Marco13
sumber
pertimbangkan operasi menambahkan 2 matriks dan menyimpan hasil dalam matriks ketiga. Ketika mutli berulir di CPU tanpa OpenCL bottleneck akan selalu menjadi langkah di mana penambahan terjadi. Operasi ini jelas paralel data. Tetapi katakanlah kita tidak tahu apakah itu akan dihitung terikat atau memori terikat sebelumnya. Butuh banyak waktu dan sumber daya untuk mengimplementasikan dan kemudian melihat bahwa CPU jauh lebih baik dalam melakukan operasi ini. Jadi bagaimana seseorang mengidentifikasi ini sebelumnya tanpa menerapkan kode OpenCL.
Cool_Coder
2
@Cool_Coder Memang sulit untuk mengatakan sebelumnya apakah (atau berapa banyak) tugas tertentu akan mendapat manfaat dari implementasi GPU. Untuk perasaan pertama, seseorang mungkin perlu pengalaman dengan berbagai kasus penggunaan (yang saya akui juga tidak benar-benar memilikinya). Langkah pertama adalah melihat nvidia.com/object/cuda_showcase_html.html dan melihat apakah ada masalah "mirip" yang terdaftar. (Ini CUDA, tetapi secara konsep sangat dekat dengan OpenCL sehingga hasilnya dapat ditransfer dalam kebanyakan kasus). Dalam kebanyakan kasus, speedup juga disebutkan, dan banyak dari mereka memiliki tautan ke makalah atau bahkan kode
Marco13
+1 untuk aparapi - ini adalah cara sederhana untuk memulai dengan OpenGL di java, dan memungkinkan Anda untuk dengan mudah membandingkan kinerja CPU vs GPU untuk kasus-kasus sederhana. Juga, ini dikelola oleh AMD tetapi bekerja dengan baik dengan kartu Nvidia.
masak steve
12
Ini adalah salah satu respons terbaik yang pernah saya lihat di StackOverflow. Terima kasih untuk waktu dan usaha!
ViggyNash
1
@AlexPunnen Ini mungkin di luar cakupan komentar. Sejauh yang saya tahu, OpenCV memiliki beberapa dukungan CUDA, mulai dari docs.opencv.org/2.4/modules/gpu/doc/introduction.html . The developer.nvidia.com/npp memiliki banyak rutinitas pengolahan gambar, yang mungkin berguna. Dan github.com/GPUOpen-ProfessionalCompute-Tools/HIP dapat menjadi "alternatif" untuk CUDA. Ini mungkin menjadi mungkin untuk meminta ini sebagai pertanyaan baru, tapi satu harus berhati-hati untuk frase dengan benar, untuk downvotes menghindari untuk "pendapat berdasarkan" / "meminta pihak ketiga perpustakaan" ...
Marco13
4

Saya akan mulai dengan menggunakan salah satu proyek di luar sana untuk Jawa dan CUDA: http://www.jcuda.org/

JohnKlehm
sumber
2

Dari penelitian yang telah saya lakukan, jika Anda menargetkan Nvidia GPU dan telah memutuskan untuk menggunakan CUDA melalui OpenCL , saya menemukan tiga cara untuk menggunakan API CUDA di java.

  1. JCuda (atau alternatif) - http://www.jcuda.org/ . Ini sepertinya solusi terbaik untuk masalah yang sedang saya kerjakan. Banyak perpustakaan seperti CUBLAS tersedia di JCuda. Kernel masih ditulis dalam bahasa C.
  2. JNI - JNI interface bukan favorit saya untuk menulis, tetapi sangat kuat dan akan memungkinkan Anda untuk melakukan apa pun yang dapat dilakukan CUDA.
  3. JavaCPP - Ini pada dasarnya memungkinkan Anda membuat antarmuka JNI di Java tanpa menulis kode C secara langsung. Ada contoh di sini: Apa cara termudah untuk menjalankan kode CUDA yang berfungsi di Jawa? tentang cara menggunakan ini dengan dorongan CUDA. Bagi saya, ini sepertinya Anda bisa saja menulis antarmuka JNI.

Semua jawaban ini pada dasarnya hanyalah cara menggunakan kode C / C ++ di Jawa. Anda harus bertanya pada diri sendiri mengapa Anda perlu menggunakan Java dan jika Anda tidak dapat melakukannya di C / C ++.

Jika Anda suka Java dan tahu cara menggunakannya dan tidak ingin bekerja dengan semua manajemen pointer dan apa-tidak yang datang dengan C / C ++ maka JCuda mungkin jawabannya. Di sisi lain, perpustakaan CUDA Thrust dan perpustakaan lain seperti itu dapat digunakan untuk melakukan banyak manajemen pointer di C / C ++ dan mungkin Anda harus melihatnya.

Jika Anda menyukai C / C ++ dan tidak keberatan manajemen pointer, tetapi ada kendala lain yang memaksa Anda untuk menggunakan Java, maka JNI mungkin merupakan pendekatan terbaik. Padahal, jika metode JNI Anda hanya akan menjadi pembungkus untuk perintah kernel Anda mungkin juga hanya menggunakan JCuda.

Ada beberapa alternatif untuk JCuda seperti Cuda4J dan Root Beer, tetapi itu tampaknya tidak dipertahankan. Sedangkan pada saat penulisan ini JCuda mendukung CUDA 10.1. yang merupakan CUDA SDK terbaru.

Selain itu ada beberapa perpustakaan java yang menggunakan CUDA, seperti deeplearning4j dan Hadoop, yang mungkin dapat melakukan apa yang Anda cari tanpa mengharuskan Anda untuk menulis kode kernel secara langsung. Saya belum melihat mereka terlalu banyak.

David Griffin
sumber
1

Marco13 sudah memberikan jawaban yang bagus .

Jika Anda mencari cara untuk menggunakan GPU tanpa mengimplementasikan kernel CUDA / OpenCL, saya ingin menambahkan referensi ke finmath-lib-cuda-extensions (finmath-lib-gpu-extensions) http: // finmath .net / finmath-lib-cuda-extensions / (disclaimer: Saya adalah pengelola proyek ini).

Proyek ini menyediakan implementasi "kelas vektor", tepatnya, antarmuka yang disebut RandomVariable, yang menyediakan operasi aritmatika dan pengurangan vektor. Ada implementasi untuk CPU dan GPU. Ada implementasi menggunakan diferensiasi algoritmik atau penilaian sederhana.

Peningkatan kinerja pada GPU saat ini kecil (tetapi untuk vektor ukuran 100.000 Anda mungkin mendapatkan faktor> 10 peningkatan kinerja). Ini karena ukuran kernel yang kecil. Ini akan membaik di versi mendatang.

Implementasi GPU menggunakan JCuda dan JOCL dan tersedia untuk GPU Nvidia dan ATI.

Perpustakaannya adalah Apache 2.0 dan tersedia melalui Maven Central.

Kentang goreng
sumber