我正在尝试使用常量参数分配共享内存,但是遇到了错误。我的内核看起来像这样:
__global__ void Kernel(const int count)
{
__shared__ int a[count];
}
我收到了一个错误信息:
错误:表达式必须具有常量值
count是const类型的!为什么会出现这个错误?我该如何绕过此错误?
我正在尝试使用常量参数分配共享内存,但是遇到了错误。我的内核看起来像这样:
__global__ void Kernel(const int count)
{
__shared__ int a[count];
}
我收到了一个错误信息:
错误:表达式必须具有常量值
count是const类型的!为什么会出现这个错误?我该如何绕过此错误?
CUDA支持动态共享内存分配。如果你像这样定义内核:
__global__ void Kernel(const int count)
{
extern __shared__ int a[];
}
然后将所需字节数作为内核启动的第三个参数传递
Kernel<<< gridDim, blockDim, a_size >>>(count)
然后它可以在运行时调整大小。请注意,运行时仅支持每个块的单个动态声明分配。如果您需要更多,则需要使用指向该单个分配内部偏移量的指针。另外,请注意在使用指针时,共享内存使用32位字,所有分配必须是32位字对齐的,无论共享内存分配的类型如何。
const
不是指 "常量",它表示 "只读"。
常量表达式是指编译器在编译时已知其值的表达式。
选项一:使用恒定值声明共享内存(与const
不同)。
__global__ void Kernel(int count_a, int count_b)
{
__shared__ int a[100];
__shared__ int b[4];
}
选项二:在内核启动配置中动态声明共享内存:
__global__ void Kernel(int count_a, int count_b)
{
extern __shared__ int *shared;
int *a = &shared[0]; //a is manually set at the beginning of shared
int *b = &shared[count_a]; //b is manually set at the end of a
}
sharedMemory = count_a*size(int) + size_b*size(int);
Kernel <<<numBlocks, threadsPerBlock, sharedMemory>>> (count_a, count_b);
注意:动态共享内存的指针全部具有相同的地址。我使用两个共享内存数组来说明如何手动设置共享内存中的两个数组。
extern __shared__ int *shared
,而在另一个内核中使用extern __shared__ float *shared
时,nvcc编译失败。 - jmilloy从“CUDA C编程指南”中:
执行配置是通过插入以下形式的表达式来指定的:
<<<Dg, Db, Ns, S>>>
参数说明:
因此,用户可以使用动态参数Ns来指定一个内核函数可以使用的共享内存的总大小,无论在该内核中有多少共享变量。
你不能像这样声明共享变量。
__shared__ int a[count];
尽管如果您对数组a的最大大小足够确定,那么可以直接声明如下:
__shared__ int a[100];
但在这种情况下,您应该担心程序中有多少个块,因为将共享内存固定到一个块(而没有充分利用)会导致与全局内存(高延迟)进行上下文切换,从而导致性能不佳...
对于这个问题有一个好的解决方案,就是声明
extern __shared__ int a[];
在从内存调用内核时分配内存
Kernel<<< gridDim, blockDim, a_size >>>(count)
但是你在这里也应该感到困扰,因为如果你在块中使用的内存比你在内核中分配的内存还要多,那么你将会得到意想不到的结果。
a_size
具有128字节的粒度:启动使用'a_size = 1'(需要时为16)的内核似乎运行良好,并且对于大的a_size - 127
也能正常工作,而a_size - 128
则会显示过小分配的影响。(我相当确信“良好”不是指“偶然”。)这是否与L1行大小= 128字节有关? - P Mareckia
长度的参数,还是这样做行不通? - BugShotGGextern
参数。我建议使用内核的模板实例,而不是动态分配共享内存。当数组大小在编译时已知时,编译器可以更好地优化代码。 - talonmies