JavaでGPU演算(Aparapi)してみた
今更感は有るが、Aparapi触ってみた。特に何か使う予定は無いけど、寒さに負けて生賴範義展に行けなかったのでなんとなく。
(1/31追記) 社内LTで再利用.
4/17追記
You linked to the wrong GitHub repository in your article. The one you linked is the older obsolete repo.
— Aparapi (@AparapiLib) 2018年4月16日
と指摘を受けたのでgithubのリンク先を https://github.com/Syncleus/aparapi に修正。
Aparapiとは
JavaVMからOpenCL API経由でGPU演算を行うライブラリ。GPU側へ持っていけるのはJava プリミティブ型の配列のみだけれど、簡単に書ける。
とりあえず、任意の画像をグレースケール化する処理をループで書いた場合とAparapiで書いた場合を比較。
JVM内でループを回した場合
private void convertToGrayScaleInJvm(final int[] pixels) { for (int i = 0; i < pixels.length; i++) { int pixel = pixels[i]; int alpha = pixel >> 24 & 0xFF; int red = pixel >> 16 & 0xFF; int green = pixel >> 8 & 0xFF; int blue = pixel & 0xFF; int y = (int)(0.298912 * red + 0.586611 * green + 0.114478 * blue); pixels[i] = alpha << 24 | y << 16 | y << 8 | y; } }
Aparapiを使ってGPU演算した場合
private void convertToGrayScaleInGpu(final int[] pixels) { Kernel kernel = new Kernel() { @Override public void run() { //GPU world int i = getGlobalId(); int pixel = pixels[i]; int alpha = pixel >> 24 & 0xFF; int red = pixel >> 16 & 0xFF; int green = pixel >> 8 & 0xFF; int blue = pixel & 0xFF; int y = (int)(0.298912 * red + 0.586611 * green + 0.114478 * blue); pixels[i] = alpha << 24 | y << 16 | y << 8 | y; }}; int size = pixels.length; kernel.execute(Range.create(size)); }
グレースケール化の方法については
https://ofo.jp/osakana/cgtips/grayscale.phtmlofo.jp
を参考とした。
ソース全体はこちら
https://gist.github.com/ka-ka-xyz/232bc0368ebf3c44bb46a7e1cb03810b
Aparapi注意点
JNI経由でOpenCLへアクセスするためにはネイティブライブラリが必要らしい。
aparapi/QuickReference.pdf at master · aparapi/aparapi · GitHub
無くても動くけれど、GPUは使わないよ的な事が書かれてた。 *1
あと、このPDFではAparapi関連のシステムプロパティのキー名が`com.amd.aparapi` で始まっているけれど、現在のv1.4.1では`com.aparapi` になっているので注意。
今回はここからdllを取得したけれど、これで良いんだろうか...
github.com
dllなりsoなりが存在するフォルダをJVMシステムプロパティ `java.library.path` で指定すれば読み込まれる。
ほんとにGPU呼んでるの?
NVIDIS GeForce GTX 1050の負荷をWin標準のパフォーマンスモニタで見てみた画像。
実行時にGPUさんが忙しそうにしてたので、呼ばれてるんだろう。多分。
パフォーマンスはどうよ?
X軸が画像のピクセル数、Y軸が処理にかかった時間(ms)。この時間はあくまでグレースケール化の処理時間で、ファイルの読み書きについては勘定していない。青がGPU演算のパフォーマンス。オレンジがJVM内での演算パフォーマンス。画像ピクセル数をあれこれ振り、CPU・JVM各5回の処理時間を取得して平均値をグラフ化。
グラフを見ると、GPU処理のためのオーバーヘッドがかなり高く、画像のピクセル数が少ないとJVMの中でループ回してたほうが圧倒的に速い。
13500x8598pxぐらいのサイズだとまだJVM内の処理が僅かに速く、18000x11464pxではGPU側が速い。
GPU演算だと並列処理のはずなのに計算量がO(N)に見えるのが気になる。仮説として
と憶測してる(未検証だけど、上で挙げたパフォーマンスモニタはそれっぽく見える)。