web-dev-qa-db-ja.com

Nvidia GPUでJavaを使用(cuda)

私はJavaで行われているビジネスプロジェクトに取り組んでおり、ビジネスマーケットを計算するには膨大な計算能力が必要です。単純な数学ですが、膨大な量のデータがあります。

私たちはいくつかのcuda gpuを試してみましたが、Javaはcudaでサポートされていないため、どこから始めればいいのでしょうか。 JNIインターフェースを構築する必要がありますか? JCUDAを使用する必要がありますか、それとも他の方法がありますか?

私はこの分野での経験がありません。誰かが私に何かを教えてくれて、研究と学習を始められるようになりたいです。

116
Hans

まず第一に、CUDAは自動的に計算を高速化しないという事実に注意する必要があります。一方で、GPUプログラミングは芸術であり、それを実現するのは非常に困難な場合があるためですright。一方、GPUは特定の種類の計算にのみ適しているためです。

GPUで基本的にanythingを計算できるため、これは混乱を招くかもしれません。キーポイントは、もちろん、あなたが良いスピードアップを達成するかどうかです。ここで最も重要な分類は、問題がtask paralleldata parallel。最初のものは、大まかに言って、いくつかのスレッドが独自のタスクで多かれ少なかれ独立して作業している問題を指します。 2つ目は、多くのスレッドがすべて同じを実行しているが、データの異なる部分にある問題を指します。

後者はGPUが得意とする種類の問題です。GPUにはmanyコアがあり、すべてのコアは同じことを行いますが、入力データの異なる部分で動作します。

あなたは「簡単な数学だが膨大な量のデータがある」と言った。これは完全にデータ並列の問題のように聞こえるかもしれず、GPUに適しているように思えますが、考慮すべきもう1つの側面があります:GPUは理論上の計算能力(FLOPS、1秒あたりの浮動小数点演算)の点で途方もなく高速です。ただし、多くの場合、メモリ帯域幅によって抑制されます。

これは、問題の別の分類につながります。つまり、問題がメモリバウンドcomputeバウンドかです。

最初の問題は、各データ要素に対して実行される命令の数が少ない問題を指します。たとえば、並列ベクトル加算を考えます。2つのデータ要素をreadし、1回の加算を実行してから、write結果ベクトルへの合計。 GPUでこれを行うと、メモリの読み取り/書き込みの労力が1回の追加で補正されないため、高速化は見られません。

2番目の用語「計算限界」は、メモリの読み取り/書き込みの数と比較して命令の数が多い問題を指します。たとえば、行列の乗算を考えます。nが行列のサイズの場合、命令の数はO(n ^ 3)になります。この場合、GPUが特定のマトリックスサイズでCPUを上回ることが期待できます。別の例として、「少数の」データ要素に対して多くの複雑な三角関数計算(サイン/コサインなど)が実行される場合があります。

経験則として、「メイン」GPUメモリからの1つのデータ要素の読み取り/書き込みには、約500命令のレイテンシがあると仮定できます。

したがって、GPUのパフォーマンスのもう1つの重要なポイントはdata localityです:データの読み取りまたは書き込みが必要な場合(そしてほとんどの場合、 ;-))、GPUコアにできるだけ近いデータを保持することを確認する必要があります。したがって、GPUには特定のメモリ領域(「ローカルメモリ」または「共有メモリ」と呼ばれる)があり、通常は数KBのサイズですが、計算に関与するデータに対して特に効率的です。

繰り返しますが、GPUプログラミングは芸術であり、CPUでの並列プログラミングにのみ関連しています。 ThreadPoolExecutorsForkJoinPoolsなどのすべての並行性インフラストラクチャを備えたJavaのスレッドのようなものは、何らかの方法で作業を分割し、複数のプロセッサに分散するだけの印象を与えるかもしれません。 GPUでは、占有、レジスタのプレッシャー、共有メモリのプレッシャー、メモリーの合体など、はるかに低いレベルの課題に直面する可能性があります。

ただし、解決するデータ並列のコンピュートバウンド問題がある場合、GPUが最適です。


一般的な発言:CUDAを具体的に求めました。しかし、OpenCLもご覧になることを強くお勧めします。これにはいくつかの利点があります。まず、ベンダーに依存しないオープンな業界標準であり、AMD、Apple、Intel、NVIDIAによるOpenCLの実装があります。さらに、Javaの世界ではOpenCLのサポートがはるかに広くなっています。 CUDAで解決したい唯一のケースは、FFTのCUFFTやBLASのCUBLAS(行列/ベクトル演算)などのCUDAランタイムライブラリを使用する場合です。 OpenCLに同様のライブラリを提供する方法はありますが、これらのライブラリ用に独自のJNIバインディングを作成しない限り、Java側から直接使用することはできません。


また、2012年10月にOpenJDK HotSpotグループがプロジェクト「Sumatra」を開始したことを聞くのも面白いかもしれません: http://openjdk.Java.net/projects/sumatra/ 。このプロジェクトの目標は、JVMでGPUサポートを直接、JITからのサポートを提供することです。現在のステータスと最初の結果は、メーリングリストの http://mail.openjdk.Java.net/mailman/listinfo/sumatra-dev で確認できます。


ただし、しばらく前に、「GPU上のJava」全般に関連するいくつかのリソースを収集しました。ここで、これらを特に順不同で要約します。

免責事項:私は http://jcuda.org/ および http:/の著者です/jocl.org/

(バイト)コード変換およびOpenCLコード生成:

https://github.com/aparapi/aparapi :AMDが作成し、積極的に保守しているオープンソースライブラリ。特別な「カーネル」クラスでは、並行して実行される特定のメソッドをオーバーライドできます。このメソッドのバイトコードは、独自のバイトコードリーダーを使用して実行時にロードされます。コードはOpenCLコードに変換され、OpenCLコンパイラを使用してコンパイルされます。その後、結果をOpenCLデバイス(GPUまたはCPU)で実行できます。 OpenCLへのコンパイルが不可能な場合(またはOpenCLが利用できない場合)、コードはスレッドプールを使用して並行して実行されます。

https://github.com/pcpratts/rootbeer1 :Javaの一部をCUDAプログラムに変換するためのオープンソースライブラリ。特定のクラスをGPUで実行する必要があることを示すために実装できる専用インターフェイスを提供します。 Aparapiとは対照的に、「関連する」データ(つまり、オブジェクトグラフの完全な関連部分!)をGPUに適した表現に自動的にシリアル化しようとします。

https://code.google.com/archive/p/Java-gpu/ :注釈付きのJavaコード(制限付き)をCUDAコードに変換するためのライブラリ。次に、GPUでコードを実行するライブラリにコンパイルされます。ライブラリは、翻訳プロセスに関する深い背景情報を含む博士論文の文脈で開発されました。

https://github.com/ochafik/ScalaCL :OpenCLのScalaバインディング。特別なScalaコレクションをOpenCLと並行して処理できます。コレクションの要素で呼び出される関数は、通常のScala関数(制限付き)であり、その後OpenCLカーネルに変換されます。

言語拡張

http://www.ateji.com/px/index.html :Javaの言語拡張。これにより、並列構造(たとえば、並列forループ、OpenMPスタイル)が可能になります。 OpenCLを使用してGPUで実行されます。残念ながら、この非常に有望なプロジェクトはもはや維持されていません。

http://www.habanero.rice.edu/Publications.html (JCUDA):特別なJavaコード(JCUDAコードと呼ばれる)をJava-およびCUDAに変換できるライブラリ-Cコード。GPUでコンパイルおよび実行できます。ただし、ライブラリは公開されていないようです。

https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html :Java OpenMPコンストラクトの言語拡張、CUDAバックエンド

Java OpenCL/CUDAバインディングライブラリ

https://github.com/ochafik/JavaCL :Java OpenCLのバインディング:自動生成された低レベルバインディングに基づくオブジェクト指向のOpenCLライブラリ

http://jogamp.org/jocl/www/ :Java OpenCLのバインディング:自動生成された低レベルバインディングに基づくオブジェクト指向のOpenCLライブラリ

http://www.lwjgl.org/ :OpenCLのJavaバインディング:自動生成された低レベルのバインディングとオブジェクト指向の便利なクラス

http://jocl.org/ :Java OpenCLのバインディング:元のOpenCL APIの1:1マッピングである低レベルバインディング

http://jcuda.org/ :CUDAのJavaバインディング:元のCUDA APIの1:1マッピングである低レベルバインディング

その他

http://sourceforge.net/projects/jopencl/ :OpenCLのJavaバインディング。 2010年以降はメンテナンスされていないようです

http://www.hoopoe-cloud.com/ :CUDAのJavaバインディング。もはや維持されていないようです


388
Marco13

まず、JavaおよびCUDAのプロジェクトを使用します。 http://www.jcuda.org/

3
JohnKlehm

私が行った研究から、もしあなたがNvidia GPUを対象としており、openCLではなくCudaを使用することに決めた場合、JavaでCuda apiを使用する3つの方法を見つけました。

  1. JCuda(または代替)- http://www.jcuda.org/ これは、私が取り組んでいる問題に対する最善の解決策のようです。 CUBLASなどのライブラリの多くは、JCudaで利用できます。カーネルはまだCで書かれています。
  2. JNI-JNIインターフェースは筆者のお気に入りではありませんが、非常に強力であり、Cudaでできることは何でもできます。
  3. JavaCPP-これにより、Cコードを直接記述せずに、基本的にJavaでJNIインターフェイスを作成できます。ここに例があります https://stackoverflow.com/a/12871248/8692546 cudaスラストでこれを使用する方法。私には、これはあなたがJNIインターフェースを書くだけのように思えます。

これらの答えはすべて、基本的にJavaでc/c ++コードを使用する方法にすぎません。なぜJavaを使用する必要があるのか​​、c/c ++でそれを行うことができないのかを自問する必要があります。

Javaが好きで、それを使用する方法を知っていて、すべてのポインター管理と、c/c ++に付属しているものを使用したくない場合は、おそらくJCudaが答えです。一方、Cuda Thrustライブラリーやその他のライブラリーを使用して、c/c ++で多くのポインター管理を行うことができます。

C/c ++が好きで、ポインター管理を気にしないが、Javaの使用を強制する他の制約がある場合、JNIが最良のアプローチかもしれません。ただし、JNIメソッドがカーネルコマンドのラッパーになるだけの場合は、JCudaを使用することもできます。

Cuda4Jやルートビアなど、JCudaに代わるものがいくつかありますが、それらは維持されていないようです。これを書いている時点では、このJCudaはCuda 10.1をサポートしています。これは、最新のCuda SDKです。

さらに、deeplearning4jやHadoopなどのcudaを使用するいくつかのJavaライブラリがあり、カーネルコードを直接記述する必要なく、探していることを実行できる場合があります。私はそれらをあまり調べていません。

1
David Griffin