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.
Jawaban:
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
,ForkJoinPools
dll. 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
sumber
Saya akan mulai dengan menggunakan salah satu proyek di luar sana untuk Jawa dan CUDA: http://www.jcuda.org/
sumber
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.
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.
sumber
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.
sumber