私はCUDAカーネルスケジューラを書いています。スケジューラーはTask
ポインターのベクトルを取得し、それらを実行に持ち込みます。ポインタは、任意のパラメータを持つカーネルをサポートするために、異なる型パラメータのKernelTask
オブジェクトを指しています。GPUのCUDAカーネルスケジューラ
スケジューラのCPU版とGPU版があります。 CPUのバージョンはうまく動作します。仮想関数Task::start
を呼び出してカーネルを実行します。 GPUバージョンには3つの問題があります。
- 仮想関数はCUDAでは使用できません。どのように私はダウンキャストなしでそれらを避けることができますか?
- std :: getはホスト関数です。標準を実装する方法はありますか::自分自身でGPUを入手しますか?
- (低優先度)
KernelTask
のオブジェクトのサイズが異なるため、すべてで別々にコピーします。バッチコピーの方法はありますか?ここで
コードです:
// see http://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer
template<int ...>
struct seq { };
template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };
template<int ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
class Task {
private:
bool visited;
bool reached;
protected:
std::vector<std::shared_ptr<Task>> dependsOn;
Task();
public:
Task **d_dependsOn = NULL;
int d_dependsOnSize;
Task *d_self = NULL;
int streamId;
int id;
cudaStream_t stream;
virtual void copyToGPU() = 0;
virtual void start() = 0;
virtual void d_start() = 0;
virtual ~Task() {}
void init();
void addDependency(std::shared_ptr<Task> t);
cudaStream_t dfs();
};
template<typename... Args>
class KernelTask : public Task {
private:
std::tuple<Args...> params;
dim3 threads;
dim3 blocks;
void (*kfp)(Args...);
template<int ...S>
void callFunc(seq<S...>) {
// inserting task into stream
this->kfp<<<this->blocks, this->threads, 0, this->stream>>>(std::get<S>(params) ...);
checkCudaErrors(cudaGetLastError());
if (DEBUG) printf("Task %d: Inserting Task in Stream.\n", this->id);
}
template<int ...S>
__device__ void d_callFunc(seq<S...>) {
// inserting task into stream
this->kfp<<<this->blocks, this->threads, 0, this->stream>>>(std::get<S>(params) ...);
if (DEBUG) printf("Task %d: Inserting Task in Stream.\n", this->id);
}
KernelTask(int id, void (*kfp)(Args...), std::tuple<Args...> params, dim3 threads, dim3 blocks);
public:
~KernelTask();
void copyToGPU();
void start() override {
callFunc(typename gens<sizeof...(Args)>::type());
}
__device__ void d_start() override {
d_callFunc(typename gens<sizeof...(Args)>::type());
}
static std::shared_ptr<KernelTask<Args...>> create(int id, void (*kfp)(Args...), std::tuple<Args...> params, dim3 threads, dim3 blocks);
};
class Scheduler {
private:
std::vector<std::shared_ptr<Task>> tasks;
public:
Scheduler(std::vector<std::shared_ptr<Task>> &tasks) {
this->tasks = tasks;
}
void runCPUScheduler();
void runGPUScheduler();
};
EDIT:CUDAで
(1)仮想関数:私は、次の例にscheduler
でWarp Illegal Address
例外を取得:
struct Base {
__host__ __device__ virtual void start() = 0;
virtual ~Base() {}
};
struct Derived : Base {
__host__ __device__ void start() override {
printf("In start\n");
}
};
__global__ void scheduler(Base *c) {
c->start();
}
int main(int argc, char **argv) {
Base *c = new Derived();
Base *d_c;
checkCudaErrors(cudaMalloc(&d_c, sizeof(Derived)));
checkCudaErrors(cudaMemcpy(d_c, c, sizeof(Derived), cudaMemcpyHostToDevice));
c->start();
scheduler<<<1,1>>>(d_c);
checkCudaErrors(cudaFree(d_c));
return 0;
}
(2)thrust::tuple
は問題なく動作します。
(3)私は提案があります。
(4)どのようにカーネルに関数ポインタを渡すのですか?私は、次の例のWarp Misaligned Address
例外を取得:
__global__ void baz(int a, int b) {
printf("%d + %d = %d\n", a, b, a+b);
}
void schedulerHost(void (*kfp)(int, int)) {
kfp<<<1,1>>>(1,2);
}
__global__ void schedulerDevice(void (*kfp)(int, int)) {
kfp<<<1,1>>>(1,2);
}
int main(int argc, char **argv) {
schedulerHost(&baz);
schedulerDevice<<<1,1>>>(&baz);
return 0;
}
'仮想関数はCUDA'では許可されません。彼らです。 'std :: get myself'を実装する方法はありますか?はい、厳密に言えば、これは標準によって許可されていません。 –
タスクオブジェクトからd_start()を呼び出すと、次のSignal: 'CUDA_EXCEPTION_14:Warp Illegal Address'が発生します。あなたはstd :: getを実装する方法を知っていますか? – martin
投稿してください[mcve] –