我正在做一个项目,其中每个线程将1字节值写入全局内存,并且我试图最小化项目中的全局内存写入延迟。
在第5.3.2节中。 CUDA C编程指南(link)中写道:
全局内存驻留在设备内存中,设备内存通过32,64或128字节内存事务进行访问。这些内存事务必须自然对齐:只有与其大小对齐的设备内存的32,64或128字节段(即,其第一个地址是其大小的倍数)可以由内存读取或写入交易。
所以我认为连续的1字节写入全局内存应该用一个事务处理,因为它们已经正确对齐。
但是当我在Visual Studio中使用Nsight配置以下最小示例时,虽然4个线程访问连续的1字节地址需要1个事务(如预期),但在5个线程的情况下,它需要2个事务。
__global__ void copyKernel(const unsigned char* a, unsigned char* b)
{
int i = threadIdx.x;
a[i] = b[i];
}
int main()
{
char *d_a;
char *d_b;
// ... (stuffs like cudaMalloc)
// to check that the address is aligned
printf("%p\n", d_a); // aligned to 512-Byte
printf("%p\n", d_b); // aligned to 512-Byte
// copy 4 elements
copyKernel<<<1, 4>>>(d_a, d_b);
// copy 5 elements
copyKernel<<<1, 5>>>(d_a, d_b);
// ...
}
分析结果如下。 (左 - 4个线程/右 - 5个线程)
我在这里缺少什么?我应该如何编写代码以使其在一个事务中执行写操作?
环境:Windows 10,Visual Studio 2015,GeForce GTX 1080(cc 6.1)
答案 0 :(得分:0)
似乎我正在查看错误实验的结果。 Nsight为“Profile CUDA Application”提供了大量实验,问题中发布的图像来自“Memory Statistics - Global”实验的结果。在Nsight用户指南中,“全球”实验报告以下数据:
每个请求的事务数据图表显示了每个执行的全局内存指令所需的平均L1事务数,分别用于加载和存储操作。
因此,“全局”实验中显示的写入事务数实际上是L1缓存,而不是L2。 (虽然在Nsight UI中,它表示它是L2。)
另一方面,“内存统计 - 缓存”似乎显示了L2事务的数量,其中包含的数据与我正在寻找的内容更相关。这些数字与罗伯特克罗维拉评论相同。
1M线程的测试结果:
<强>更新强>
似乎L2事务的粒度为32字节。查看4字节连续存储的分析结果,为1M线程报告的L2存储事务数为131,072,等于1M(#threads)乘以4(数据大小)除以32。
所以我得出结论,我的问题中的引用声明“设备内存可以通过128字节事务访问”无法用Nsight验证,因为它似乎不计算L2和设备内存之间的事务。 (test code)