假设我有两个__device__
CUDA函数,每个函数都有以下局部变量:
__shared__ int a[123];
和另一个函数(说它是我的内核,即__global__
函数),用:
extern __shared__ int b[];
nVIDIA是明确允许/禁止的吗? (我在__shared__
上的programming guide B.2.3节中没有看到它。这些大小是否一起计入共享内存限制,或者它是一次可能使用的最大值?还是其他一些规则?
这可以被视为this one的后续问题。
答案 0 :(得分:3)
共享内存分为两部分:静态分配和动态分配。第一部分是在编译期间计算的,每个声明都是一个实际的分配 - 在编译期间激活ptxas信息说明了它:
ptxas info : Used 22 registers, 384 bytes smem, 48 bytes cmem[0]
在这里,我们有384
个字节,这是3
个32
个数组。 (见下面的样本corde)。
您可以将指向共享内存的指针从Kepler传递到另一个允许设备子功能访问另一个共享内存声明的函数。
然后,动态分配共享内存,在内核调用期间声明保留大小。
以下是一些功能中的一些不同用途的示例。请注意每个共享内存区域的指针值。
__device__ void dev1()
{
__shared__ int a[32] ;
a[threadIdx.x] = threadIdx.x ;
if (threadIdx.x == 0)
printf ("dev1 : %x\n", a) ;
}
__device__ void dev2()
{
__shared__ int a[32] ;
a[threadIdx.x] = threadIdx.x * 5 ;
if (threadIdx.x == 0)
printf ("dev2 : %x\n", a) ;
}
__global__ void kernel(int* res, int* res2)
{
__shared__ int a[32] ;
extern __shared__ int b[];
a[threadIdx.x] = 0 ;
b[threadIdx.x] = threadIdx.x * 3 ;
dev1();
__syncthreads();
dev2();
__syncthreads();
res[threadIdx.x] = a[threadIdx.x] ;
res2[threadIdx.x] = b[threadIdx.x] ;
if (threadIdx.x == 0)
printf ("global a : %x\n", a) ;
if (threadIdx.x == 0)
printf ("global b : %x\n", b) ;
}
int main()
{
int* dres ;
int* dres2 ;
cudaMalloc <> (&dres, 32*sizeof(int)) ;
cudaMalloc <> (&dres2, 32*sizeof(int)) ;
kernel<<<1,32,32*sizeof(float)>>> (dres, dres2);
int hres[32] ;
int hres2[32] ;
cudaMemcpy (hres, dres, 32 * sizeof(int), cudaMemcpyDeviceToHost) ;
cudaMemcpy (hres2, dres2, 32 * sizeof(int), cudaMemcpyDeviceToHost) ;
for (int k = 0 ; k < 32 ; ++k)
{
printf ("%d -- %d \n", hres[k], hres2[k]) ;
}
return 0 ;
}
此代码使用384 bytes smem
输出ptxas信息,即一个数组用于全局a
数组,第二个用于dev1方法a
数组,第三个用于dev2方法{{1数组。总计a
。
当动态共享内存等于3*32*sizeof(float)=384 bytes
运行内核时,指向32*sizeof(float)
的指针就在这三个数组之后立即启动。
修改强> 此代码生成的ptx文件包含静态定义的共享内存的声明,
b
除了在方法体中定义的入口点
.shared .align 4 .b8 _ZZ4dev1vE1a[128];
.shared .align 4 .b8 _ZZ4dev2vE1a[128];
.extern .shared .align 4 .b8 b[];
内存的共享空间在PTX文档here中定义:
共享(.shared)状态空间是CTA中用于共享数据的线程的每个CTA内存区域。共享内存中的地址可以由CTA中的任何线程读取和写入。使用ld.shared和st.shared访问共享变量。
虽然运行时没有详细说明。编程指南here中有一个词,没有关于两者混合的进一步细节。
在PTX编译期间,编译器可能知道静态分配的共享内存量。可能会有一些补充魔法。查看SASS,第一条指令使用SR_LMEMHIOFF
// _ZZ6kernelPiS_E1a has been demoted
并以相反的顺序调用函数为静态分配的共享内存分配不同的值(看起来非常像stackalloc的形式)。
我相信ptxas编译器会在最坏的情况下计算可能需要的所有共享内存,因为可能会调用所有方法(当不使用其中一个方法并使用函数指针时,1 IADD32I R1, R1, -0x8;
2 S2R R0, SR_LMEMHIOFF;
3 ISETP.GE.U32.AND P0, PT, R1, R0, PT;
地址不会更改,并且永远不会访问未分配的共享内存区域。
最后,正如einpoklum在评论中所说,这是实验性的,而不是规范/ API定义的一部分。