Memory 对全局内存连续写入1字节会导致多个事务

Memory 对全局内存连续写入1字节会导致多个事务,memory,cuda,nsight,Memory,Cuda,Nsight,我正在做一个项目,其中每个线程将1字节的值写入全局内存,我正在尝试最小化项目中的全局内存写入延迟 在第5.3.2节中。关于CUDA C编程指南()的说明如下: 全局内存驻留在设备内存中,通过32、64或128字节内存事务访问设备内存。这些内存事务必须自然对齐:内存事务只能读取或写入与其大小对齐的32、64或128字节设备内存段(即,其第一个地址是其大小的倍数) __global__ void copyKernel(const unsigned char* a, unsigned char* b)

我正在做一个项目,其中每个线程将1字节的值写入全局内存,我正在尝试最小化项目中的全局内存写入延迟

在第5.3.2节中。关于CUDA C编程指南()的说明如下:

全局内存驻留在设备内存中,通过32、64或128字节内存事务访问设备内存。这些内存事务必须自然对齐:内存事务只能读取或写入与其大小对齐的32、64或128字节设备内存段(即,其第一个地址是其大小的倍数)

__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);

    // ...
}
所以我认为,如果全局内存的连续1字节写入正确对齐,那么应该用一个事务来处理

但是,当我在VisualStudio中使用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);

    // ...
}
\uuuuu全局\uuuuu无效复制内核(const unsigned char*a,unsigned char*b)
{
int i=threadIdx.x;
a[i]=b[i];
}
int main()
{
char*d_a;
char*d_b;
//…(像库达马洛克这样的人)
//检查地址是否对齐
printf(“%p\n”,d_a);//对齐到512字节
printf(“%p\n”,d_b);//对齐到512字节
//复制4个元素
复制内核(d_a,d_b);
//复制5个元素
复制内核(d_a,d_b);
// ...
}
分析结果如下。(左-4螺纹/右-5螺纹)

我错过了什么?我应该如何编写代码以使其在一个事务中执行写操作


环境:Windows 10、Visual Studio 2015、GeForce GTX 1080(cc 6.1)

我似乎看到了错误实验的结果。Nsight提供了一系列“评测CUDA应用程序”的实验,问题中发布的图像来自“内存统计-全局”实验的结果。从Nsight的用户指南中,“全局”实验报告了以下数据:

每个请求的事务数图表分别显示加载和存储操作中每个执行的全局内存指令所需的L1事务的平均数

因此,“全局”实验中显示的写事务数实际上是到一级缓存的,而不是到二级缓存的。(尽管在Nsight用户界面中,它说它是L2。)

另一方面,“内存统计-缓存”似乎显示了L2事务的数量,其中包含与我所寻找的内容更相关的数据。那里的数字与罗伯特·克罗维拉评论的数字相同

1M螺纹的测试结果:


更新

似乎L2事务的粒度为32字节。查看4字节连续存储的评测结果,为1M线程报告的L2存储事务数为131072,等于1M(#线程)乘以4(数据大小)除以32


因此我得出结论,我问题中引用的“设备内存可以通过128字节事务访问”的说法无法通过Nsight进行验证,因为它似乎不计算L2和设备内存之间的事务。()

也尝试分析8个线程。硬件倾向于将传输的数据量最小化,而不是使用的事务数。对于大规模(超过4或5个线程,比如1M个线程),在
nvprof
中,对于全局存储操作,在Titan X(Pascal)上,我看到了事务总数(
gst_事务
)与线程数除以32(对于
char
)(与您预期的完全一致)相当,我看到每个请求的事务数(
gst\u transactions\u per\u request
)正好为1.0(同样,与您预期的完全一致)。在处理非常少量的活动(例如,少量线程、少量块)时,探查器可能不太“精确”,原因有很多。