Java'yı Nvidia GPU'larla (CUDA) kullanma


144

Java'da yapılan bir iş projesi üzerinde çalışıyorum ve iş piyasalarını hesaplamak için büyük hesaplama gücüne ihtiyaç duyuyor. Basit matematik, ancak büyük miktarda veri ile.

Bazı CUDA GPU'ları denemek için sipariş verdik ve Java CUDA tarafından desteklenmediği için nereden başlayacağımı merak ediyorum. Bir JNI arayüzü oluşturmalı mıyım? JCUDA kullanmalı mıyım yoksa başka yollar var mı?

Bu alanda deneyimim yok ve araştırmaya ve öğrenmeye başlayabilmem için birinin beni bir şeye yönlendirip yönlendiremeyeceğini istiyorum.


2
GPU'lar, bilgi işlem açısından belirli sorun türlerini hızlandırmanıza yardımcı olacaktır. Ancak çok fazla veriye sahipseniz, ES'ye bağlı olma olasılığınız daha yüksektir. Büyük olasılıkla GPU'lar çözüm değildir.
steve cook

1
"GPGPU'ları Kullanarak Java Performansını Arttırma" -> arxiv.org/abs/1508.06791
BlackBear

4
Biraz açık bir soru, Marco13'ün cevabı inanılmaz derecede yararlı olduğu için modların kapanmadığına sevindim! Bir wiki IMHO olmalı
JimLohse

Yanıtlar:


443

Her şeyden önce, CUDA'nın otomatik olarak hesaplamaları daha hızlı hale getirmeyeceğinin farkında olmalısınız. GPU programlama bir sanattır çünkü bir taraftan, ve çok bunu elde etmek için zorlu, çok olabilir sağ . Öte yandan, GPU'lar sadece belirli hesaplama türleri için çok uygun olduğu için .

Bu kafa karıştırıcı gelebilir, çünkü temel olarak GPU'daki herhangi bir şeyi hesaplayabilirsiniz . Kilit nokta, elbette, iyi bir hızlanma elde edip etmeyeceğinizdir. Buradaki en önemli sınıflandırma, bir sorunun görev paralel veya veri paralel olup olmadığıdır . Birincisi, kabaca konuşmak gerekirse, birkaç iş parçacığının az çok bağımsız olarak kendi görevleri üzerinde çalıştığı sorunları ifade eder. İkincisi, birçok iş parçacığının aynı şeyi yaptığı, ancak verilerin farklı kısımlarındaki sorunları ifade eder .

İkincisi, GPU'ların iyi olduğu bir tür problemdir: Birçok çekirdeğe sahiptir ve tüm çekirdekler aynı şeyi yapar, ancak giriş verilerinin farklı bölümlerinde çalışır.

"Basit bir matematik ama büyük miktarda veri" olduğunu söyledi. Her ne kadar bu, mükemmel bir veri-paralel problemi gibi görünse de ve dolayısıyla bir GPU için uygun olduğu gibi, göz önünde bulundurulması gereken başka bir husus daha vardır: GPU'lar teorik hesaplama gücü (FLOPS, Saniyede Kayan Nokta İşlemleri) açısından gülünç derecede hızlıdır. Ancak bunlar genellikle bellek bant genişliği tarafından kısıtlanır.

Bu, sorunların başka bir sınıflandırmasına yol açar. Yani problemlerin hafızaya bağlı mı yoksa hesaplamalara mı bağlı olduğu .

Birincisi, her bir veri elemanı için yapılan talimat sayısının düşük olduğu problemleri ifade eder. Örneğin, paralel bir vektör eklemeyi düşünün: İki veri öğesini okumanız , ardından tek bir ekleme yapmanız ve toplamı sonuç vektörüne yazmanız gerekir. GPU'da bunu yaparken bir hızlanma görmezsiniz, çünkü tek bir ekleme belleği okuma / yazma çabalarını telafi etmez.

İkinci terim olan "hesaplama sınırı", okuma / yazma bellek sayısına kıyasla talimat sayısının yüksek olduğu problemleri ifade eder. Örneğin, bir matris çarpımı düşünün: n matrisin boyutu olduğunda, talimat sayısı O (n ^ 3) olacaktır. Bu durumda, GPU'nun belirli bir matris boyutunda bir CPU'dan daha iyi performans göstermesi beklenebilir. Başka bir örnek, "az" veri elemanları üzerinde birçok karmaşık trigonometrik hesaplamanın (sinüs / kosinüs vb.) Gerçekleştirilmesi olabilir.

Genel bir kural olarak: "Ana" GPU bellekten bir veri öğesi okumanın / yazmanın yaklaşık 500 komut gecikmesi olduğunu varsayabilirsiniz.

Bu nedenle, GPU'ların performansı için bir başka önemli nokta veri konumudur : Veri okumak veya yazmak zorundaysanız (ve çoğu durumda ;-) gerekir), o zaman verilerin yakın tutulduğundan emin olmalısınız GPU çekirdekleri için mümkün. Bu nedenle GPU'lar, genellikle yalnızca birkaç KB boyutunda olan, ancak özellikle bir hesaplamaya dahil olmak üzere olan veriler için etkili olan belirli bellek alanlarına ("yerel bellek" veya "paylaşılan bellek" olarak adlandırılır) sahiptir.

Bunu tekrar vurgulamak için: GPU programlama bir sanattır, bu sadece CPU'daki paralel programlamayla uzaktan ilgilidir. Java'daki Threads gibi şeyler, vb.Gibi eşzamanlılık altyapısı vb. ThreadPoolExecutors, ForkJoinPoolsİşinizi bir şekilde bölmeniz ve birkaç işlemci arasında dağıtmanız gerektiği izlenimini verebilir. GPU'da, çok daha düşük bir seviyede zorluklarla karşılaşabilirsiniz: Doluluk, kayıt baskısı, paylaşılan bellek basıncı, bellek birleştirme ... sadece birkaçını belirtmek için.

Bununla birlikte, çözülecek veri-paralel, bilgi işlemle ilgili bir sorununuz olduğunda, GPU gitmenin yoludur.


Genel bir açıklama: Özellikle CUDA'yı istediniz. Ancak OpenCL'ye de göz atmanızı şiddetle tavsiye ederim. Birkaç avantajı vardır. Her şeyden önce, satıcıdan bağımsız, açık endüstri standardıdır ve AMD, Apple, Intel ve NVIDIA tarafından OpenCL uygulamaları vardır. Ayrıca, Java dünyasında OpenCL için çok daha geniş bir destek var. CUDA'ya yerleşmeyi tercih ettiğim tek durum, FFT için CUFFT veya BLAS için CUBLAS (Matris / Vektör işlemleri) gibi CUDA çalışma zamanı kitaplıklarını kullanmak istediğinizde. Her ne kadar OpenCL için benzer kütüphaneler sağlamaya yönelik yaklaşımlar olsa da, bu kütüphaneler için kendi JNI bağlarınızı oluşturmadığınız sürece bunlar doğrudan Java tarafından kullanılamaz.


Ekim 2012'de OpenJDK HotSpot grubunun "Sumatra" projesini başlattığını duymak da ilginç olabilir: http://openjdk.java.net/projects/sumatra/ . Bu projenin amacı , JIT desteği ile doğrudan JVM'de GPU desteği sağlamaktır . Mevcut durum ve ilk sonuçlar http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev adresindeki posta listelerinde görülebilir.


Ancak, bir süre önce, genel olarak "GPU'da Java" ile ilgili bazı kaynaklar topladım. Bunları burada tekrar, belirli bir sırayla özetleyeceğim.

( Feragatname : http://jcuda.org/ ve http://jocl.org/ yazarıyım )

(Byte) kod çevirisi ve OpenCL kod üretimi:

https://github.com/aparapi/aparapi : AMD tarafından oluşturulan ve aktif olarak bakımı yapılan açık kaynaklı bir kütüphane. Özel bir "Çekirdek" sınıfında, paralel olarak yürütülmesi gereken belirli bir yöntemi geçersiz kılabilir. Bu yöntemin bayt kodu çalışma zamanında kendi bayt kodu okuyucu kullanılarak yüklenir. Kod, daha sonra OpenCL derleyicisi kullanılarak derlenen OpenCL koduna çevrilir. Sonuç daha sonra GPU veya CPU olabilecek OpenCL cihazında yürütülebilir. OpenCL derlemesi mümkün değilse (veya OpenCL mevcut değilse), kod yine de bir Thread Pool kullanılarak paralel olarak yürütülür.

https://github.com/pcpratts/rootbeer1 : Java'nın parçalarını CUDA programlarına dönüştürmek için açık kaynaklı bir kütüphane. GPU'da belirli bir sınıfın yürütülmesi gerektiğini belirtmek için uygulanabilecek özel arayüzler sunar. Aparapi'nin aksine, "ilgili" verileri (yani, nesne grafiğinin tüm ilgili kısmını!) GPU için uygun bir gösterime otomatik olarak serileştirmeye çalışır.

https://code.google.com/archive/p/java-gpu/ : Ek açıklamalı Java kodunu (bazı sınırlamalarla) CUDA koduna çevirmek için kullanılan ve daha sonra GPU'daki kodu çalıştıran bir kütüphaneye derlenen bir kütüphane. Kütüphane, çeviri süreci hakkında derinlemesine bilgi içeren bir doktora tezi bağlamında geliştirilmiştir.

https://github.com/ochafik/ScalaCL : OpenCL için Scala bağlamaları. Özel Scala koleksiyonlarının OpenCL ile paralel işlenmesini sağlar. Koleksiyon öğelerinde çağrılan işlevler, daha sonra OpenCL çekirdeklerine çevrilen olağan Scala işlevleri (bazı sınırlamalarla) olabilir.

Dil uzantıları

http://www.ateji.com/px/index.html : Java için, daha sonra OpenCL ile GPU'da yürütülen paralel yapılara (ör. döngüler için paralel, OpenMP stili) izin veren bir dil uzantısı. Ne yazık ki, bu çok umut verici proje artık sürdürülmüyor.

http://www.habanero.rice.edu/Publications.html (JCUDA): Özel Java Kodunu (JCUDA kodu olarak adlandırılır) Java ve CUDA-C koduna çevirebilen ve daha sonra derlenip yürütülebilen bir kütüphane GPU. Ancak, kütüphane kamuya açık görünmüyor.

https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html : CUDA arka ucuna sahip OpenMP yapıları için Java dili uzantısı

Java OpenCL / CUDA bağlama kitaplıkları

https://github.com/ochafik/JavaCL : OpenCL için Java bağlamaları: Otomatik oluşturulan düşük düzeyli bağlamaları temel alan nesne tabanlı bir OpenCL kütüphanesi

http://jogamp.org/jocl/www/ : OpenCL için Java bağları: Otomatik oluşturulan düşük düzeyli bağlamaları temel alan nesne tabanlı bir OpenCL kütüphanesi

http://www.lwjgl.org/ : OpenCL için Java bağları: Otomatik oluşturulan düşük düzey bağlamaları ve nesneye yönelik uygunluk sınıfları

http://jocl.org/ : OpenCL için Java ciltleri: Orijinal OpenCL API'sinin 1: 1 eşlemesi olan düşük düzeyli ciltlemeler

http://jcuda.org/ : CUDA için Java ciltleri: Orijinal CUDA API'sinin 1: 1 eşlemesi olan düşük düzeyli ciltlemeler

Çeşitli

http://sourceforge.net/projects/jopencl/ : OpenCL için Java bağlamaları. 2010'dan beri artık bakım yapılmıyor gibi görünüyor

http://www.hoopoe-cloud.com/ : CUDA için Java bağları. Artık bakım yapılmıyor gibi görünüyor



2 matris ekleme ve sonucu üçüncü bir matriste saklama işlemini düşünün. OpenCL olmadan CPU'da mutli iş parçacığına işlendiğinde, darboğaz her zaman ekleme işleminin gerçekleştiği adım olacaktır. Bu işlem açıkça verilere paraleldir. Ama diyelim ki önceden hesaplamaya bağlı mı yoksa hafızaya mı bağlı olacak bilemeyiz. Uygulamak ve CPU'nun bu işlemi yaparken çok daha iyi olduğunu görmek çok zaman ve kaynak gerektirir. Öyleyse, bunu önceden OpenCL kodunu uygulamadan nasıl tanımlar?
Cool_Coder

2
@Cool_Coder Gerçekten de belirli bir görevin GPU uygulamasından faydalanıp faydalanmayacağını (veya ne kadar) önceden söylemek zor. İlk bağırsak hissi için, muhtemelen farklı kullanım durumları ile biraz deneyime ihtiyaç duyar (ki kuşkusuz gerçekten de yok). İlk adım nvidia.com/object/cuda_showcase_html.html adresine bakmak ve listelenen "benzer" bir sorun olup olmadığını görmek olabilir . (CUDA, ancak kavramsal olarak OpenCL'ye o kadar yakın ki sonuçlar çoğu durumda aktarılabilir). Çoğu durumda, hızlandırmadan da bahsedilir ve birçoğunun kağıtlara veya hatta koda bağlantıları vardır
Marco13

Aparapi için +1 - java'da opencl ile çalışmaya başlamanın basit bir yoludur ve basit durumlar için CPU ve GPU performansını kolayca karşılaştırmanıza olanak tanır. Ayrıca, AMD tarafından korunur, ancak Nvidia kartlarıyla iyi çalışır.
steve cook

12
Bu StackOverflow'da gördüğüm en iyi yanıtlardan biri. Zaman ve çaba için teşekkürler!
ViggyNash

1
@AlexPunnen Bu muhtemelen yorumların kapsamı dışındadır. Bildiğim kadarıyla OpenCV'nin docs.opencv.org/2.4/modules/gpu/doc/introduction.html'den itibaren CUDA desteği var . Developer.nvidia.com/npp kullanışlı olabilir birçok görüntü işleme rutinleri vardır. Ve github.com/GPUOpen-ProfessionalCompute-Tools/HIP , CUDA için "alternatif" olabilir. Bunu yeni bir soru olarak sormak mümkün olabilir , ancak "fikir tabanlı" / "üçüncü taraf kütüphaneleri istemek" için aşağı oylardan kaçınmak için doğru şekilde ifade etmeye dikkat etmek gerekir ...
Marco13


2

Gönderen araştırma Nvidia GPU'ları hedefliyorsanız ve üzeri CUDA'yı kullanmaya karar eğer, yapmış OpenCL , ben java CUDA API kullanmak için üç yol buldular.

  1. JCuda (veya alternatif) - http://www.jcuda.org/ . Bu, üzerinde çalıştığım sorunlar için en iyi çözüm gibi görünüyor. CUBLAS gibi kütüphanelerin çoğu JCuda'da bulunmaktadır. Çekirdekler yine de C dilinde yazılmıştır.
  2. JNI - JNI arayüzleri yazmak benim favorim değil, ama çok güçlü ve CUDA'nın yapabileceği her şeyi yapmanıza izin verecek.
  3. JavaCPP - Bu temelde Java'da doğrudan C kodu yazmadan bir JNI arabirimi oluşturmanıza izin verir. Burada bir örnek var: Java'da çalışan CUDA kodunu çalıştırmanın en kolay yolu nedir? CUDA itme ile nasıl kullanılacağına dair Bana göre, bu sadece bir JNI arayüzü yazabileceğiniz gibi görünüyor.

Tüm bu cevaplar temelde Java'da C / C ++ kodunu kullanmanın yollarıdır. Neden Java'yı kullanmanız gerektiğini ve bunun yerine C / C ++ ile yapamıyorsanız kendinize sormalısınız.

Java'yı seviyorsanız ve nasıl kullanılacağını biliyorsanız ve tüm işaretçi yönetimi ve C / C ++ ile birlikte gelen şeylerle çalışmak istemiyorsanız, JCuda muhtemelen cevaptır. Öte yandan, CUDA Thrust kütüphanesi ve bunun gibi diğer kütüphaneler, C / C ++ 'da çok sayıda işaretçi yönetimi yapmak için kullanılabilir ve belki de buna bakmalısınız.

C / C ++ 'ı seviyorsanız ve işaretçi yönetimini önemsemiyorsanız, ancak Java'yı kullanmaya zorlayan başka kısıtlamalar varsa, JNI en iyi yaklaşım olabilir. Yine de, JNI yöntemleriniz sadece çekirdek komutları için sarıcılar olacaksa, JCuda'yı da kullanabilirsiniz.

JCuda'ya Cuda4J ve Root Beer gibi birkaç alternatif var, ancak bunların bakımı yapılmıyor gibi görünüyor. Oysa bu JCuda yazarken CUDA 10.1'i destekliyor. Bu en güncel CUDA SDK'sıdır.

Ayrıca, doğrudan çekirdek kodu yazmanıza gerek kalmadan aradığınız şeyi yapabilen deeplearning4j ve Hadoop gibi CUDA kullanan birkaç java kütüphanesi vardır. Yine de onlara çok fazla bakmadım.


1

Marco13 zaten mükemmel bir cevap verdi .

CUDA / OpenCL çekirdekleri uygulamadan GPU'yu kullanmanın bir yolunu arıyorsanız, finmath-lib-cuda-uzantılarına (finmath-lib-gpu-uzantıları) bir referans eklemek istiyorum http: // finmath .net / finmath-lib-cuda-extensions / (feragatname: Ben bu projenin koruyucusuyum ).

Proje, kesin olarak, RandomVariablevektörlerde aritmetik işlemler ve indirgeme sağlayan bir arabirim olarak adlandırılan bir "vektör sınıflarının" uygulanmasını sağlar. CPU ve GPU için uygulamalar var. Algoritmik farklılaşma veya basit değerlemeler kullanan uygulamalar vardır.

GPU'daki performans iyileştirmeleri şu anda küçüktür (ancak 100.000 büyüklüğündeki vektörler için> 10 performans iyileştirmesi faktörü alabilirsiniz). Bu, küçük çekirdek boyutlarından kaynaklanmaktadır. Bu, gelecekteki bir sürümde düzelecektir.

GPU uygulaması JCuda ve JOCL kullanır ve Nvidia ve ATI GPU'lar için kullanılabilir.

Kütüphane Apache 2.0'dır ve Maven Central aracılığıyla edinilebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.