如何将模板内核函数的地址传递给CUDA函数?

时间:2013-07-12 07:45:05

标签: templates cuda

我想使用CUDA运行时API函数接受带有内核模板的CUDA内核函数指针。

我可以在没有模板的情况下执行以下操作:

__global__ myKernel()
{
  ...
}

void myFunc(const char* kernel_ptr)
{
  ...
  // use API functions like
  cudaFuncGetAttributes(&attrib, kernel_ptr);
  ...
}

int main()
{
  myFunc(myKernel);
}

但是,当内核是模板时,上述操作无效。

另一个例子:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>

template<typename T>
__global__ void addKernel(T *c, const T *a, const T *b)
{
    int i = threadIdx.x;
    c[i] = a[i] + b[i];
}

int main()
{
    cudaFuncAttributes attrib;
    cudaError_t err;

    //OK:
    err = cudaFuncGetAttributes(&attrib, addKernel<float>); // works fine
    printf("result: %s, reg1: %d\n", cudaGetErrorString(err), attrib.numRegs);

    //NOT OK:
    //try to get function ptr to pass as an argument:
    const char* ptr = addKernel<float>; // compile error
    err = cudaFuncGetAttributes(&attrib, ptr);
    printf("result: %s, reg2: %d\n", cudaGetErrorString(err), attrib.numRegs);
}

以上结果导致编译错误:

  

错误:没有函数模板“addKernel”的实例匹配   所需类型

编辑: 到目前为止我发现的唯一解决方法是将myFunc中的内容(参见第一个代码示例)放入一个宏,这很丑,但它不需要传递指针参数,它工作正常:

#define MY_FUNC(kernel) \
  { \
     ...\
     cudaFuncGetAttributes( &attrib, kernel ); \
     ...\
  }

用法:

MY_FUNC( myKernel<float> )

3 个答案:

答案 0 :(得分:2)

addKernel<void>的类型不是char *,它是一种函数类型。

相反,请获取addKernel<float>的地址,如下所示:

typedef void (*fun_ptr)(float*,const float *, const float*);
fun_ptr ptr = addKernel<float>; // compile error
err = cudaFuncGetAttributes(&attrib, ptr);

答案 1 :(得分:2)

参考“另一个例子:”

中包含的代码

改变这个:

const char* ptr = addKernel<float>; // compile error

到此:

void (*ptr)(float *, const float *, const float *) = addKernel<float>;

我相信它会编译并正确运行。

我不知道你在尝试的整体范围内是否有用。

编辑回复评论中的问题:

一旦我从函数中“提取”了指针,我就可以将其转换为另一种类型。试试吧。例如,以下代码也有效:

void (*ptr)(float *, const float *, const float *) = addKernel<float>;
const char *ptr1 = (char *)ptr;
err = cudaFuncGetAttributes(&attrib, ptr1);

所以,为了回答你的问题,一旦你有了函数指针,可以将你的函数指针强制转换为const char*

顺便说一句,你发布的代码作为答案会在gcc 4.1.2和gcc 4.4.6上为我抛出编译错误:

$ nvcc -arch=sm_20 -O3 -o t201 t201.cu
t201.cu: In function âint main()â:
t201.cu:25: error: address of overloaded function with no contextual type information
t201.cu:29: error: address of overloaded function with no contextual type information
$

如果我删除这两行中的&,我也会收到错误:

$ nvcc -arch=sm_20 -O3 -o t201 t201.cu
t201.cu: In function âint main()â:
t201.cu:25: error: insufficient contextual information to determine type
t201.cu:29: error: insufficient contextual information to determine type
$

因此,根据从A点到B点需要采取哪些步骤,其中一些可能与编译器有关。

答案 2 :(得分:0)

编辑:添加了基于cuda运行时的模板版本和Robert Crovella的答案。

这是一个使用void函数指针和模板的完整工作示例。

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>

template <typename T>
__global__ void addKernel(T *c, const T *a, const T *b)
{
    int i = threadIdx.x;
    c[i] = a[i] + b[i];
}

cudaError_t func1( cudaFuncAttributes* attrib, void (*ptr)() )
{
    return cudaFuncGetAttributes(attrib, ptr);
}

cudaError_t func2( cudaFuncAttributes* attrib, const char* ptr )
{
    return cudaFuncGetAttributes(attrib, ptr);
}

template <typename T>
cudaError_t func2( cudaFuncAttributes* attrib, T ptr )
{
    return func2( attrib, (const char*) ptr);
}

int main()
{
    cudaFuncAttributes attrib;
    cudaError_t err;

    void (*ptr2)() = (void(*)())(addKernel<float>);  // OK on Visual Studio
    err = func1(&attrib, ptr2);
    printf("result: %s, reg1: %d\n", cudaGetErrorString(err), attrib.numRegs);

    err = func2(&attrib, addKernel<double> ); // OK nice and standard
    printf("result: %s, reg2: %d\n", cudaGetErrorString(err), attrib.numRegs);
}