JavaからCUDAを触る GPUプログラミング

GPUGPGPU(汎用計算)目的で操作する方法は、CUDAOpenCLが有名です。

ただし、CUDAはNvidiaGPUでしか使えませんし、OpenCLGPU専用ではないのでとっつきにくいです。というわけでNvidiaGPUがあるなら最初はCUDAをオススメします。どちらも、C言語をベースにした言語でプログラミングします。

CUDA

CUDA言語は、C言語の拡張で、CUDA ToolKitに含まれるnvccコンパイラを用いてコンパイルします。

__device__ __host__ float2 operator+(float2 a, float2 b)
{
    return make_float2(a.x + b.x, a.y + b.y);
}

__device__ や __host__ を記述することで、関数がGPUで使えるのか、CPUで使えるのか定義できます。

 

GPU処理の起点

extern "C" __global__ void mapping(int *map, short *ctvol, int *vol)
{
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    vol[tid] = map[ctvol[tid] + 32768];
}

CPU側のコードからCUDAランタイムAPIを呼び出して、__global__が記された関数をGPUで実行させることができます。上記の例だと、tidは何番目の処理かに相当する。

 

JavaからGPUを動かす

Javaコードを直接GPUで動かせるライブラリはAparapiくらいしかありません。Aparapiはビルドが少々難しいうえ、GPUの限られた機能しか使えません。

そこで、JavaコードからCUDAランタイムAPIを呼び出すことを考えます。JavaコードでCPU処理部分を記述し、GPU処理部分はCUDA言語で記述します。

JavaコードからCUDAランタイムを呼び出せるライブラリ

JCuda

おそらく最もライブラリとして完全です。今回はスルー

lwjgl.Cuda

LWJGLはゲームライブラリですが、GLからCUDA、ほかにも沢山組み合わせることができ、Minecraftでも利用されています。Maven一発で入れられるのもいい点です。

 

LWJGLからCUDAを呼び出すサンプルはこちらが参考になります。

github.com

内容はこんな感じです。

  1. GPU初期化
  2. CUDAコードコンパイル
  3. CPUでデータ用意
  4. GPUにデータコピー
  5. GPUで並列処理
  6. CPUに結果をコピー
  7. CPUでチェック
  8. CPU・GPUで確保したメモリを解放

注意点

  • 最初のCUDAファイルは文字列で渡してコンパイルしてもらう
  • Java側でデータはスタック領域に配置しなければGPUメモリにコピーできない
  • スタック領域では明示的なメモリ開放が必要
  • 一次元配列以外のデータを渡すのはけっこう面倒

CUDAでのベクトル関数はこちらが便利

github.com