C++ CUDA:如何从主机函数返回设备lambda

C++ CUDA:如何从主机函数返回设备lambda,c++,lambda,cuda,C++,Lambda,Cuda,我有一个虚拟函数,它根据派生类返回不同的lambda: class Base { public: virtual std::function<float()> foo(void) = 0; }; class Derived : public Base { public: std::function<float()> foo(void) { return [] __device__ (void) { return 1

我有一个虚拟函数,它根据派生类返回不同的lambda:

class Base
{
public:
    virtual std::function<float()> foo(void) = 0;
};

class Derived : public Base
{
public:
    std::function<float()> foo(void) {
        return [] __device__ (void) {
            return 1.0f;
        };
    }
};
类基
{
公众:
虚拟标准::函数foo(void)=0;
};
派生类:公共基
{
公众:
函数foo(void){
返回[]\uuuuu设备(无效){
返回1.0f;
};
}
};
然后我想将这个lambda传递给CUDA内核,并从设备调用它。换句话说,我想这样做:

template<typename Func>
__global__ void kernel(Func f) {
    f();
}

int main(int argc, char** argv)
{
    Base* obj = new Derived;
    kernel<<<1, 1>>>(obj->foo());
    cudaDeviceSynchronize();
    return 0;
}
模板
__全局无效内核(Func f){
f();
}
int main(int argc,字符**argv)
{
Base*obj=新导出的;
内核(obj->foo());
cudaDeviceSynchronize();
返回0;
}
上面给出的错误如下:
不允许从全局函数(“内核<::std::function>”)调用主机函数(“std::function::operator()”)

如您所见,我将lambda声明为
\uuuu device\uuuu
,但是
foo()
方法将其存储在
std::function
中以返回它。因此,传递给
内核()
的是一个主机地址,当然它不起作用。我想这是我的问题,对吧?因此,我的问题是:

  • 是否有可能创建一个
    \uuuu设备\uuuuu std::function
    并从
    foo()
    方法返回它

  • 如果这是不可能的,是否有其他方法可以动态选择lambda并将其传递给CUDA内核?使用所有可能的lambda对
    kernel()
    的多个调用进行硬编码不是一个选项

到目前为止,根据我所做的快速研究,CUDA没有/不支持使函数返回设备lambda所需的必要语法。我只是希望我错了有什么想法吗


提前谢谢在回答之前,我想知道你的问题是否是一个问题。也就是说,我默认怀疑人们是否有理由通过设备上的lambdas/函数指针执行代码

但我不会那样回避你的问题

是否有可能创建一个
\uuuu设备\uuuuu std::函数
,并从foo()方法返回该函数

简短回答:不,试试别的

更详细的回答:如果您想在设备端实现一大块标准库,那么您可能需要一个类似设备端的类。但我甚至不确定这是否可能(很可能不可能),而且无论如何,除了经验丰富的库开发人员之外,这超出了每个人的能力。那么,做点别的吧

如果这是不可能的,是否有其他方法可以动态选择lambda并将其传递给CUDA内核?使用所有可能的lambda硬编码对kernel()的多次调用不是一个选项

首先,请记住lambda本质上是匿名类——因此,如果它们不捕获任何内容,那么它们可以简化为函数指针,因为匿名类没有数据,只有一个
操作符()

因此,如果lambda具有相同的签名且没有捕获,则将其转换为(非成员)函数指针并将其传递给函数;这确实有效,请参见nVIDIA论坛


另一种可能性是使用运行时映射,从类型id或其他此类键映射到这些类型的实例,或者更确切地说,映射到构造函数。也就是说,使用工厂。但我不想深入细节,不想给出比现在更长的答案;这可能不是一个好主意。

虽然我认为使用返回设备lambda的虚拟函数无法实现您想要的功能,但您可以通过将静态设备成员函数作为模板参数传递给内核来实现类似的功能。下面提供了一个例子。请注意,如果您愿意,本例中的类也可以是结构

#include <iostream>

// Operation: Element-wise logarithm
class OpLog {
    public:
    __device__ static void foo(int tid, float * x) {
        x[tid] = logf(x[tid]);
    };
};

// Operation: Element-wise exponential
class OpExp {
    public:
    __device__ static void foo(int tid, float * x) {
        x[tid] = expf(x[tid]);
    }
};

// Generic kernel
template < class Op >
__global__ void my_kernel(float * x) {
    int tid = threadIdx.x;
    Op::foo(tid,x);
}

// Driver
int main() {

    using namespace std;

    // length of vector
    int len = 10;

    // generate data
    float * h_x = new float[len];
    for(int i = 0; i < len; i++) {
        h_x[i] = rand()/float(RAND_MAX);
    }

    // inspect data
    cout << "h_x = [";
    for(int j = 0; j < len; j++) {
        cout << h_x[j] << " ";
    }
    cout << "]" << endl;

    // copy onto GPU
    float * d_x;
    cudaMalloc(&d_x, len*sizeof(float));
    cudaMemcpy(d_x, h_x, len*sizeof(float), cudaMemcpyHostToDevice);

    // Take the element-wise logarithm
    my_kernel<OpLog><<<1,len>>>(d_x);

    // get result
    cudaMemcpy(h_x, d_x, len*sizeof(float), cudaMemcpyDeviceToHost);
    cout << "h_x = [";
    for(int j = 0; j < len; j++) {
        cout << h_x[j] << " ";
    }
    cout << "]" << endl;

    // Take the element-wise exponential
    my_kernel<OpExp><<<1,len>>>(d_x);

    // get result
    cudaMemcpy(h_x, d_x, len*sizeof(float), cudaMemcpyDeviceToHost);
    cout << "h_x = [";
    for(int j = 0; j < len; j++) {
        cout << h_x[j] << " ";
    }
    cout << "]" << endl;


}
#包括
//运算:元素对数
类OpLog{
公众:
__设备\静态无效foo(整数tid,浮点*x){
x[tid]=logf(x[tid]);
};
};
//操作:按元素指数
类OpExp{
公众:
__设备\静态无效foo(整数tid,浮点*x){
x[tid]=expf(x[tid]);
}
};
//泛型核
模板
__全局\uuuu无效我的\u内核(浮点*x){
int tid=threadIdx.x;
Op::foo(tid,x);
}
//司机
int main(){
使用名称空间std;
//向量长度
int len=10;
//生成数据
浮动*h_x=新浮动[len];
对于(int i=0;i难道我不认为这是一个语法问题。
std::function
在设备上不受支持,这是编译错误的根源,从我所看到的您可能想要阅读。我认为当然可以使用
\uuuu device\uuuu
lambda作为内核参数/参数,但您可能正在使用
std::function
>因为你想“泛化”它-你不喜欢这样的事实。我不认为你将无法使用
std::function
来解决这个问题。使用函子可能更容易。