カーネル関数を分離して C++11 コードをビルド.
nvcc でないとビルドできないのはカーネル関数なので, ここと通常の C++ コードを分離してそれぞれビルドしたオブジェクトをリンクすれば, 普通に C++11 を使ったコードが(カーネル関数以外で)書けるようになる.
後, CUDA を使うために必要なライブラリとか, リンクするのが面倒なので nvcc にやってもらう.
以下で検証.
CUDA 4.2
gcc 4.6.3
// kernel.h #ifndef _KERNEL_H_ #define _KERNEL_H_ void call_kernel(int block, int thread, float * x); #endif
// kernel.cu __global__ void kernel_func(float * x) { // do something. } void call_kernel(int block, int thread, float * x) { kernel_func<<<block, thread>>>(x); }
#include "kernel.h" // カーネル関数を呼び出すコード. call_kernel(block, thread, x);
かなりはしょったけど, こんな感じ, cudaMemcpy などのランタイム API は cuda_runtime_api.h に宣言されているので,
Makefile で適当に書くとこんな感じになるはず.
CC = g++ NVCC = nvcc CPPFLAG = -std=gnu++0x -O3 -Wall NVCCFLAG = -O3 -Xcompiler -Wall INCLUDE = -I/usr/local/cuda/include OBJS = kernel.o main.o NAME = a.out .SUFFIXES : .cu $(NAME) : $(OBJS) $(NVCC) $(NVCCFLAG) -o $(NAME) $(OBJS) .cpp.o : $(CC) $(INCLUDE) $(CPPFLAG) -c $< .cu.o : $(NVCC) $(INCLUDE) $(NVCCFLAG) -c $<
(ハイライトが微妙におかしい...)
clean とかは省略.
例えば, カーネル関数が float, double など複数の型で欲しいときは, どうしようもないのでカーネル関数自体は template にして call_kernel といった関数を必要な型の分定義するしかない.
そういう面倒なところがあっても, C++ コードを C++11 でかけるのは利点がでかい. 例えばデバイスメモリを RAII で保存する為に unique_ptr とか, move semantics とか, ラムダ式とか. auto が使えるだけでも個人的には嬉しい. 若干メタいコードを良く書くので.
結構備忘録.