在CUDA C编程指南中,有一部分说:
全局内存指令支持读取或写入大小的单词 等于1,2,4,8或16个字节。任何访问(通过变量或 (指针)驻留在全局内存中的数据编译为单个全局 存储器指令当且仅当数据类型的大小为1,2时, 4,8或16字节,数据自然对齐(即其地址 是这个大小的倍数。)
如果未满足此尺寸和对齐要求,则访问权限 使用交错访问模式编译多个指令 阻止这些指令完全合并。因此 建议使用满足此要求的类型 居于全球记忆中。
我正在使用Java包装器在我的代码中使用CUDA(JCuda)。我在Java中定义了自己的float3
等价物(它只是交错的x,y和z元素的float[]
数组)。
我的问题是,由于我定义的float3
占用3 x sizeof(float) = 12 bytes
且12个字节不等于CUDA提取的单词的长度,我应该在末尾手动添加填充元素并使其成为16个字节?
作为一个非常相关的副问题:
我的内核需要一个指向float3
数据的指针,因此当我从Java调用它时,我传递了我拥有的float []数据,其中包含Java端的所有float3
元素。现在我的java float3
没有对齐,我处理错误的值吗?我问,因为在编程指南的另一部分中它说:
读取非自然对齐的8字节或16字节字 不正确的结果(用几句话说),所以必须特别小心 保持对齐任何值或数组的起始地址 这些类型的价值观。一个典型的情况,这可能很容易 忽略的是当使用一些自定义全局内存分配方案时, 从而分配多个数组(多次调用 cudaMalloc()或cuMemAlloc())被单个分配所取代 在这种情况下,大块内存分区为多个数组 每个数组的起始地址都从块的起始位置偏移 地址。
这是否意味着当我的数据未对齐并且我请求该数据中的某个偏移量时,我会获取错误的值?
提前感谢您的答案: - )
答案 0 :(得分:4)
这个问题有两个方面:
第一项:正如CUDA文档所指出的,为了正确加载和存储数据,每次访问的地址必须可以被访问的大小整除。例如,类型为float
的对象的大小为四个字节,因此必须在四倍的地址访问它。如果违反了对齐要求,将无法读取和存储数据,也就是说,数据会出现乱码。
对于内置的非复合类型,所需的对齐方式等于类型的大小,这称为“自然对齐”。对于用户定义的复合类型(例如结构),所需的对齐是最大组件类型的对齐。这适用于问题中用户定义的float3
类型,其具有四字节对齐要求,因为最大组件的类型为float
。程序员可以使用__align__()
属性来增加所需的对齐。请参阅:How to specify alignment for global device variables in CUDA
对于内置复合类型,CUDA要求对齐等于复合类型的大小。例如,类型int2
和float2
的对象必须在8字节边界上对齐,而类型float4
和double2
的对象必须与16字节对齐边界。
第二项:GPU能够执行对齐的4字节,8字节和16字节访问,通常,每次访问越宽,总内存吞吐量越高。 GPU硬件的大致简化视图是硬件内部有固定大小的队列,用于跟踪每个内存访问。每次内存访问越宽,可以排队等待传输的总字节数就越大,从而提高了延迟容忍度和整体内存吞吐量。
出于这个原因,我建议尽可能从自定义float3
类型切换到内置float4
类型。前者将导致数据以四个字节的块加载,而后者允许以16个字节的块加载数据。
答案 1 :(得分:1)
因此经过一些试验和错误后,似乎使用填充float3
确实可以提高程序的性能。因此,我决定使用填充float3
和跨步内存(使用cudaMallocPitch
)。
但是,对于我的问题的第二部分,我仍然没有听到一个好的答案。