Nvidia GPU(CUDA)でのJavaの使用


144

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

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

私はこの分野での経験がありません。誰かが私を何かに導き、研究と学習を開始できるようにしてほしいです。


2
GPUは、特定のタイプの計算集中型の問題をスピードアップするのに役立ちます。ただし、大量のデータがある場合は、IOバウンドになる可能性が高くなります。ほとんどの場合、GPUはソリューションではありません。
スティーブクック14

1
「GPGPUを使用したJavaパフォーマンスの向上
BlackBear

4
Marco13からの回答が非常に役に立ったので、modがシャットダウンしなかったことをうれしく思います。私見はwikiである必要があります
JimLohse 2016年

回答:


442

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

基本的にGPUで何でも計算できるので、これは混乱するように聞こえるかもしれません。もちろん、重要なポイントは、十分なスピードアップを達成できるかどうかです。ここで最も重要な分類は、問題がタスク並列データ並列かです。最初の問題は、おおまかに言って、複数のスレッドが多かれ少なかれ独立して独自のタスクに取り組んでいる問題を指します。2番目の問題は、多くのスレッドがすべて同じことを行っているが、データの異なる部分で発生する問題を示しています。

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

「単純な数学ですが、大量のデータがある」とのことですが、これは完全にデータ並列の問題のように聞こえるため、GPUに適しているように思われますが、考慮すべきもう1つの側面があります。ただし、メモリ帯域幅によって抑制されることがよくあります。

これは問題の別の分類につながります。つまり、問題がメモリに制限されているか、計算に制限されているか。

最初の問題は、各データ要素に対して実行される命令の数が少ないという問題に関係しています。たとえば、並列ベクトルの加算を考えてみましょう。2つのデータ要素を読み取ってから、1つの加算を実行し、その合計を結果のベクトルに書き込む必要があります。GPUでこれを行うと、メモリの読み取り/書き込みの労力が1回の追加で補正されないため、スピードアップは見られません。

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

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

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

したがって、これを強調しておきます。GPUプログラミングは芸術であり、CPUでの並列プログラミングにリモートでのみ関連しています。以下のようなすべての並行処理インフラストラクチャを使用してJavaのスレッド、のようなものThreadPoolExecutorsForkJoinPoolsなどはあなただけ何とか自分の仕事を分割し、複数のプロセッサ間でそれを分配しなければならないという印象を与えるかもしれません。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/。このプロジェクトの目標は、JITのサポートにより、JVMでGPUサポートを直接提供することです。現在のステータスと最初の結果は、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コードに変換するためのライブラリ。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:OpenMPコンストラクト用のJava言語拡張、CUDAバックエンドを使用

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

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

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

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

http://jocl.org/:OpenCLの Javaバインディング:元の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バインディング。もうメンテナンスされていないようです



2つの行列を追加し、結果を3番目の行列に格納する操作を考えます。OpenCLを使用せずにCPUでマルチスレッド化すると、ボトルネックが常に追加が発生するステップになります。この操作は明らかにデータの並列処理です。しかし、それが事前に計算にバインドされているのか、メモリにバインドされているのかわからないとしましょう。実装には多くの時間とリソースが必要であり、CPUがこの操作を実行するのにはるかに優れていることがわかります。では、OpenCLコードを実装せずに、これを前もって特定するにはどうすればよいでしょうか。
Cool_Coder 14

2
@Cool_Coder確かに、特定のタスクがGPU実装の恩恵を受けるかどうか(またはどれだけか)を事前に判断することは困難です。最初の直感としては、おそらくさまざまなユースケースでの経験が必要です(確かに私は実際には持っていません)。最初のステップは、nvidia.com / object / cuda_showcase_html.htmlを見て、「類似した」問題がリストされているかどうかを確認することです。(これはCUDAですが、概念的にはOpenCLに非常に近いため、ほとんどの場合、結果を転送できます)。ほとんどの場合、スピードアップも言及されており、それらの多くには、論文やコードへのリンクがあります
Marco13

+1 for aparapi-Javaでopenclを使い始める簡単な方法で、単純なケースでCPUとGPUのパフォーマンスを簡単に比較できます。また、AMDによって保守されていますが、Nvidiaカードで正常に動作します。
スティーブクック14

12
これは、StackOverflowでこれまでに見た中で最高の応答の1つです。時間と労力をありがとう!
ViggyNash

1
@AlexPunnenこれはおそらくコメントの範囲を超えています。私の知る限り、docs.opencv.org / 2.4 / modules / gpu / doc / introduction.htmlのように、OpenCVはいくつかのCUDAサポートを備えています。developer.nvidia.com/nppは便利かもしれ多くの画像処理ルーチンを持っています。そしてgithub.com/GPUOpen-ProfessionalCompute-Tools/HIPはCUDAの「代替」かもしれません。これを新しい質問として質問することは可能かもしれませんが、「意見に基づく」/「サードパーティのライブラリを要求する」への反対票を避けるために、適切に語るように注意する必要があります...
Marco13


2

私が行った調査から、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インターフェースを作成できるようにします。ここに例があります:Javaで作業中のCUDAコードを実行する最も簡単な方法は何ですか?CUDA推力でこれを使用する方法の。私には、JNIインターフェースを作成するだけでよいように思えます。

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

Javaが好きで、Javaの使い方を知っていて、すべてのポインター管理で作業したくない場合や、C / C ++に付属していない場合は、おそらくJCudaが答えです。一方、CUDA Thrustライブラリやその他のライブラリは、C / C ++で多くのポインター管理を行うために使用でき、おそらくそれを確認する必要があります。

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

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

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


1

Marco13はすでに優れた回答を提供しています

CUDA / OpenCLカーネルを実装せずにGPUを使用する方法を探している場合は、finmath-lib-cuda-extensions(finmath-lib-gpu-extensions)http:// finmathへの参照を追加したいと思います。 .net / finmath-lib-cuda-extensions /(免責事項:私はこのプロジェクトのメンテナーです)。

このプロジェクトは、「ベクトルクラス」の実装を提供します。正確には、と呼ばれるインターフェースでRandomVariable、算術演算とベクトルの削減を提供します。CPUとGPUの実装があります。アルゴリズムによる微分または単純な評価を使用した実装があります。

GPUのパフォーマンス向上は現在わずかです(ただし、サイズが100.000のベクターの場合、10を超えるパフォーマンス向上が得られる場合があります)。これは、カーネルサイズが小さいためです。これは将来のバージョンで改善されるでしょう。

GPU実装はJCudaおよびJOCLを使用し、NvidiaおよびATI GPUで使用できます。

ライブラリはApache 2.0であり、Maven Centralから入手できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.