__global__ void kern (int* gpuArr) {
int a;
a = gpuArr[threadIdx.x+1];
}
int main(char** argv, int argc) {
int* gpuArr;
cudaMalloc((void**)&gpuArr,628*sizeof(int));
kern<<<1,32>>>(gpuArr);
cudaDeviceSynchronize();
}
在Nvidia visual profiler中分析上面的代码时,我得到以下内存带宽分析。
据我所知,会发生什么:
来自全局的内存负载 - L2未命中
将5 * 32B从全局复制到L2(我认为L2的缓存行大小始终为32B)
将2 * 128B从L2复制到L1(包括来自L2的附加数据,因为L1缓存行是128B?)
为每个线程
执行等效的a = L1_position [threadIdx.x]那么每个线程中的局部变量“a”存储在哪里?根据调试器,它不在寄存器中(通常在变量选项卡中显示为@register int,但它代表@local int)。 “Local Stores 1”的意思是什么?我们已经做了2 * 128B L2到L1拷贝(“Global Loads”),那么“Local Store”是什么意思。当有2个“全球商店”时,为什么只有1个“本地商店”?
另外,我正在使用cc 3.0,因此根据规范不可能在L1中进行全局内存缓存
答案 0 :(得分:3)
首先,正如已经指出的那样:
您正在运行使用调试开关(-G
)编译的代码。这不会给你带来最佳性能,并且没有代表性(无论是性能方面还是行为方面)编译没有它的代码,因此对这些代码进行分析是一项值得怀疑的活动。
由于-G
禁用编译器优化,因此此类代码的行为可能与您预期的不同。
那么每个线程中的局部变量“a”存储在哪里?
它存储在本地逻辑空间中。这正是分析器(和调试器)告诉您的内容。 “本地”逻辑空间可以存在于寄存器中或物理(板上DRAM)存储器中。它不在寄存器中的原因是因为您已使用-G
开关禁用了优化,并且逻辑数据在寄存器中的这种放置是一种优化。您将无法通过消除-G
开关直接确认这一点,因为如果您这样做,编写的代码将被编译器完全优化,因为它对任何全局状态都没有影响。
“本地商店1”行究竟是什么意思?
如上所述,变量a
位于本地逻辑空间中,因此全局读取和本地写入发生在此处:
a = gpuArr[threadIdx.x+1];
当a
被“写”时,将导致本地商店。
当有2个“全球商店”时,为什么只有1个“本地商店”?
存储在DRAM存储器中的属于本地逻辑空间的变量将以这样的方式存储在存储器中:当进行DRAM存储器事务处理时,经线中的线程的连续访问将产生相邻(即“合并”)访问,读取或写入此类值。这意味着如果我为每个线程都有一个局部变量a
,那么首先存储线程0的a
,然后是线程1的a
,然后是线程的a
。 2,等等,这样如果每个线程读取(或写入)a
,结果访问将被合并。由于您正好有一个32个线程的warp,每个线程写入int
a
值,因此会产生一个128字节的本地存储事务。
在您的全局内存情况(即读取)中,您已使读取跨越缓存行/段边界,并将1添加到数组索引中:
a = gpuArr[threadIdx.x+1];
^
所以它需要两个全局事务来收集warp请求的数据。如果要确认这一点,请删除数组索引上的+1,全局事务应从2减少到1。
作为一个警告,分析非常少量的活动可能并不总是能给你你期望的结果(虽然它似乎在这种情况下起作用)。这样做的原因是为某些SM子集捕获了一些分析器度量标准,然后乘以SM的数量以反映完整的GPU活动。如果非常小的数据集的结果没有意义,那么对于更大的数据集,您可能会获得更明智的结果,这些数据集更适合“填充”GPU并在SM之间保持一致的活动。