C++ Cuda:将设备常量声明为模板

C++ Cuda:将设备常量声明为模板,c++,templates,cuda,C++,Templates,Cuda,我正在开发一个使用模板的CUDA程序。内核将使用数据类型cuComplex或cuDoubleComplex进行实例化。根据内核实例化使用的数据类型,我需要声明一个常量,该常量将驻留在CUDA设备的常量内存空间中。为了实现这一点,我做了以下工作: // declared this globally template <typename T> __device__ __constant__ T some_constant; // set the constant through a f

我正在开发一个使用模板的CUDA程序。内核将使用数据类型
cuComplex
cuDoubleComplex
进行实例化。根据内核实例化使用的数据类型,我需要声明一个常量,该常量将驻留在CUDA设备的常量内存空间中。为了实现这一点,我做了以下工作:

// declared this globally
template <typename T> 
__device__ __constant__ T some_constant;
// set the constant through a function call in main().

// kernel templated
template <typename T>    
__global__ void kernel()
{
    T b_k = cuGet<T>(0.0, 0.0);
    T x_k_1 = cuGet<T>(2.0, 2.0); 
    // cuGet returns a complex no. of type 
    // cuComplex or cuDoubleComplex depending on T.

    b_k = cuAdd(b_k, cuMul(some_constant, x_k_1));
    // cuAdd, cuMul, cuGet are all overloaded functions.
    // They can take cuComplex, or cuDoubleComplex params.

    // Here, some_constant has to cuComplex or cuDoubleComplex, depending 
    // on the datatype of the other arg x_k_1 to cuMul.
    // Therefore, I went about implementing a templated constant.
}
//全局声明此
模板
__装置常数T某些常数;
//通过main()中的函数调用设置常量。
//内核模板
模板
__全局无效内核()
{
T b_k=cuGet(0.0,0.0);
T x_k_1=cuGet(2.0,2.0);
//cuGet返回类型的复杂编号
//取决于T的cuComplex或CuDoublex复合物。
b_k=cuAdd(b_k,cuMul(某些常数,x_k_1));
//cuAdd、cuMul、cuGet都是重载函数。
//它们可以采用cuComplex或cuDoubleComplex参数。
//在这里,一些_常数必须是cuComplex或cuDoubleComplex,这取决于
//在另一个参数x_k_1到cuMul的数据类型上。
//因此,我着手实现一个模板常量。
}
编译时会出现错误:“some_constant”不是函数或静态数据成员

解决此问题的一个选项是定义从
cuDoubleComplex
cuComplex
的类型转换,并将常量声明为
cuDoubleComplex
,而不是将其用作模板,并在内核中使用的任何位置对常量进行类型转换

除此之外还有别的办法吗


提前感谢。

如果主要的要求是避免类型转换,您可以使用带有内联设备函数的模板类来解决它(其余的灵感来自@RobertCrovella在评论中的建议)。最后,一个宏将进行调用(它实际上不是一个完全干净的设计,但保持相同的语法)。下面是它如何工作的一个示例:

template <typename T> 
struct holder
{
    static __device__ __inline__ T value () ;
    static __device__ __inline__ void init (T val) ;
} ;

__constant__ char data [16] ;

__device__ __inline__ int holder<int>::value () { return *((int*)data); }
__device__ __inline__ long holder<long>::value () { return *((long*)data); }

#define some_constant holder<T>::value()

template <typename T>
__global__ void kernel(T* res)
{
    *res = some_constant ;
}

int main ()
{
    int *dres ;
    cudaMalloc <> (&dres, sizeof(int)) ;
    int val = 42 ;
    cudaMemcpyToSymbol (data, &val, sizeof(int)) ;

    kernel<int><<<1,1>>>(dres) ;
    int hres ;
    cudaMemcpy (&hres, dres, sizeof(int), cudaMemcpyDeviceToHost) ;
    printf ("RES = %d\n", hres) ;
}
模板
结构持有者
{
静态设备内联值();
静态设备在线无效初始化(T val);
} ;
__常量字符数据[16];
__设备uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
__设备内联长持有者::值(){return*((long*)数据);}
#定义一些常量holder::value()
模板
__全局无效内核(T*res)
{
*res=某个_常数;
}
int main()
{
int*dres;
cudaMalloc(&dres,sizeof(int));
int-val=42;
cudaMemcpyToSymbol(数据和值,大小(int));
果仁;
int hres;
cudaMemcpy(&hres、dres、sizeof(int)、cudaMemcpyDeviceToHost);
printf(“RES=%d\n”,hres);
}
holder::value()
调用将被内联,类型转换将被优化器擦除,并从常量内存中返回适当的类型,而不进行转换(这里是生成的ptx):

/.globl\u z6kernelievpt_
.const.align 4.b8数据[16];
.visible.entry\u z6kernelievpt_(
.param.u32_z6kernelievpt_uuparam_0
)
{
.注册号b32%r;
ld.param.u32%r1,[\u z6kernelievpt\uu param\u 0];
cvta.to.global.u32%r2,%r1;
ld.const.u32%r3,[数据];
st.global.u32[%r2],%r3;
ret;
}

主要的缺点是宏期望类型是“代码> t>代码> .< /p>你确定全局变量可以被定义为C++中的模板变量吗?不,我不确定。我认为你的类型化思想是可能的,而且不是非常繁重的。我不是在回答这个问题,因为你明确要求的是“除此之外的任何其他方式”。您的问题可能无法回答。C++14允许变量模板:)在C++中,请注意,这不适用于正在使用的两种不同类型的内核。您必须在每次不同的内核运行之前调用cudaMemcpyToSymbol,如果它们在不同的线程中运行,这是不可能的,或者您可以修改

value
方法,使用假定最大数据类型大小的乘法器为不同的类型使用不同的偏移量,即
return*((T*)((T_max*)data+sizeof(T)/sizeof(T_最小)-1)
您还必须分配足够的数据:
char数据[sizeof(T_最大)*(sizeof(T_最大)/sizeof(T_最小)]
// .globl   _Z6kernelIiEvPT_
.const .align 4 .b8 data[16];

.visible .entry _Z6kernelIiEvPT_(
    .param .u32 _Z6kernelIiEvPT__param_0
)
{
    .reg .b32   %r<4>;


    ld.param.u32    %r1, [_Z6kernelIiEvPT__param_0];
    cvta.to.global.u32  %r2, %r1;
    ld.const.u32    %r3, [data];
    st.global.u32   [%r2], %r3;
    ret;
}