ここでの問題は、テンプレート化されたコードは通常、使用場所でインスタンス化されますがfoo
、g++ で解析できないカーネル呼び出しが含まれているため機能しません。テンプレートを明示的にインスタンス化し、それをホスト コンパイラに対して前方宣言するというアプローチは正しいものです。これを行う方法は次のとおりです。コードを少し修正し、3 つのファイルに分割しました。
- gpu.cu
- gpu.cuh
- cpu.cpp
gpu.cuh
このファイルには、 で使用するテンプレート化されたコードが含まれていますgpu.cu
。foo()
関数が機能することを確認するために、関数にいくつかの目的を追加しました。
#pragma once
#include <cuda_runtime.h>
template <typename T>
struct bar {
__device__ __host__ T operator()(T t1, T t2)
{
return t1 + t2;
}
};
template <template <typename> class Operator, typename T>
__global__ void fooKernel(T t1, T t2, T* t3)
{
Operator<T> op;
*t3 = op(t1, t2);
}
template <template <typename> class Operator, typename T>
T foo(T t1, T t2)
{
T* t3_d;
T t3_h;
cudaMalloc(&t3_d, sizeof(*t3_d));
fooKernel<Operator><<<1, 1>>>(t1, t2, t3_d);
cudaMemcpy(&t3_h, t3_d, sizeof(*t3_d), cudaMemcpyDeviceToHost);
cudaFree(t3_d);
return t3_h;
}
gpu.cu
このファイルは、foo()
関数をインスタンス化して、リンクに使用できるようにするだけです。
#include "gpu.cuh"
template int foo<bar>(int, int);
cpu.cpp
この単純な C++ ソース ファイルでは、コンパイル エラーが発生するため、テンプレートのインスタンス化を取得しないようにする必要があります。bar
代わりに、構造体と関数のみを前方宣言しますfoo
。コードは次のようになります。
#include <cstdio>
template <template <typename> class Operator, typename T>
T foo(T t1, T t2);
template <typename T>
struct bar;
int main()
{
printf("%d \n", foo<bar>(3, 4));
}
メイクファイル
これにより、コードがすべて実行可能ファイルにまとめられます。
.PHONY: clean all
all: main
clean:
rm -f *.o main
main: gpu.o cpu.o
g++ -L/usr/local/cuda/lib64 $^ -lcudart -o $@
gpu.o: gpu.cu
nvcc -c -arch=sm_20 $< -o $@
cpu.o: cpu.cpp
g++ -c $< -o $@
デバイス コードは によってコンパイルされnvcc
、ホスト コードは によってコンパイルされ、g++
すべて によってリンクされg++
ます。実行すると、美しい結果が表示されます。
7
ここで覚えておくべき重要なことは、カーネルの起動とカーネルの定義は、.cu
によってコンパイルされるファイルに含まれている必要があるということですnvcc
。今後の参考のために、このリンクもここに残しておきます。リンクとコンパイルの分離については CUDA を使用します。