我想编写一个c ++ CUDA程序,我将一个类传递给内核。该类只通过调用operator()计算内核上的函数。如果我在课堂上硬连接这个功能,一切都像我一样有效。但是我希望课程有一些灵活性,所以我希望这个类能够用不同的函数进行实例化。通过传入指针函数来说。我无法使指针功能实现起作用。下面我定义了两个类,一个具有定义的函数(fixedFunction),另一个带有指向函数的指针(genericFunction)
//Functions.hh
#include <iostream>
#include <stdio.h>
class fixedFunction{
public:
__host__ fixedFunction() {}
__host__ __device__ double operator()(double x) {
return x*x;
}
};
double f1(double x){
return x*x;
}
typedef double (*pf) (double var);
class genericFunction{
public:
__host__ genericFunction(double (*infunc)(double)) : func(infunc){}
__host__ __device__ double operator()(double x) {
return func(x);
}
private:
pf func;
};
__global__ void kernel1(fixedFunction* g1){
unsigned int tid = blockIdx.x *blockDim.x + threadIdx.x;
printf("Func val is: %f\n", (*g1)(tid));
}
__global__ void kernel2(genericFunction* g1){
unsigned int tid = blockIdx.x *blockDim.x + threadIdx.x;
printf("Func val is: %f\n", (*g1)(tid));
}
实例化这两个类并在主机上运行它们。传递给相关的内核,我看到该类调用指针函数的kernel2失败
#include "Functions.hh"
int main(){
fixedFunction h_g1;
fixedFunction* d_g1;
cudaMallocManaged(&d_g1, sizeof(h_g1));
//Host call
std::cout << h_g1(2.0) << "\n";
//device call
kernel1<<<1,32>>>(d_g1);
cudaDeviceSynchronize();
genericFunction h_g2(f1);
genericFunction* d_g2;
cudaMallocManaged(&d_g2, sizeof(h_g2));
//Host call
std::cout << h_g2(3.0) << "\n";
//device call
kernel2<<<1,32>>>(d_g2);
cudaDeviceSynchronize();
我可以看到指针功能中的问题可以是任何大小,并且在设备上没有考虑到。那么有没有办法将指针函数传递给类并在设备上运行它?
由于
答案 0 :(得分:4)
这大约是最小的&#34;我可以对您的代码进行的更改次数,以使其大致按照您的意图运行。另请注意,关于CUDA中的函数指针还有许多其他问题,this answer链接到几个。
使用f1
装饰__host__ __device__
。这是使编译器为其生成设备可调用例程所必需的。否则,仅生成主机代码。
我们需要捕获上面1中创建的f1
设备可调用版本的设备入口地址。有很多方法可以做到这一点。我将捕获它&#34;静态&#34;使用其他__device__
变量(f1_d
),然后使用cudaMemcpyFromSymbol
将其拉入主机代码。
您的genericFunction
类被修改为能够同时保存所需函数的__host__
和单独的__device__
入口点(函数指针)。此外,根据我们是否正在编译类的主机或设备版本(__CUDA_ARCH__
宏),修改类以选择正确的类,并修改类构造函数以接受并分配两个入口点。 / p>
最后,我们还需要初始化设备上的d_g2
对象。在d_g1
对象的情况下,该对象没有类数据成员,因此我们可以&#34;逃避&#34;创造一个&#34;空&#34; d_g1
指向的对象,它可以正常工作,因为该对象的类成员函数的入口点在设备代码中已知。但是,在d_g2
的情况下,我们通过类数据成员间接访问函数,类成员是指向函数的相应主机和设备版本(入口点)的指针。因此,在主机代码中初始化h_g2
对象并在设备代码中为d_g2
对象建立存储后,我们必须使用{{d_g2
的内容初始化h_g2
1 {} cudaMemcpy
之后的cudaMallocManaged
。
通过这些更改,您的代码按照我的测试编写:
d_g2