【问题标题】:Does CUDA automatically convert float4 arrays into a struct of arrays?CUDA 会自动将 float4 数组转换为数组结构吗?
【发布时间】:2019-04-07 05:15:07
【问题描述】:

我有以下sn-p的代码:

#include <stdio.h>

struct Nonsense {
    float3 group;
    float other;
};

__global__ void coalesced(float4* float4Array, Nonsense* nonsenseArray) {
    float4 someCoordinate = float4Array[threadIdx.x];
    someCoordinate.x = 5;
    float4Array[threadIdx.x] = someCoordinate;

    Nonsense nonsenseValue = nonsenseArray[threadIdx.x];
    nonsenseValue.other = 3;
    nonsenseArray[threadIdx.x] = nonsenseValue;
}

int main() {
    float4* float4Array;
    cudaMalloc(&float4Array, 32 * sizeof(float4));
    cudaMemset(float4Array, 32 * sizeof(float4), 0);

    Nonsense* nonsenseArray;
    cudaMalloc(&nonsenseArray, 32 * sizeof(Nonsense));
    cudaMemset(nonsenseArray, 32 * sizeof(Nonsense), 0);

    coalesced<<<1, 32>>>(float4Array, nonsenseArray);
    cudaDeviceSynchronize();
    return 0;
}

当我通过 Nsight 中的 Nvidia 分析器运行它并查看全局内存访问模式时,float4Array 具有完美的合并读取和写入。同时,Nonsense 数组的访问模式很差(因为它是一个结构数组)。

NVCC 是否会自动将 float4 数组(在概念上是结构数组)转换为数组结构,以获得更好的内存访问模式?

【问题讨论】:

    标签: cuda nsight


    【解决方案1】:

    不,它不会将其转换为数组结构。我认为,如果您仔细考虑这一点,您会得出结论,编译器几乎不可能以这种方式重新组织数据。毕竟,被传递的东西是一个指针。

    只有一个数组,并且那个数组的元素仍然有相同顺序的结构元素:

    float address (i.e. index):      0      1      2      3      4      5 ...
    array element             : a[0].x a[0].y a[0].z a[0].w a[1].x a[1].y ...
    

    但是,float4 数组提供了更好的模式,因为编译器生成了a single 16-byte load per thread。这有时被称为“向量加载”,因为我们正在为每个线程加载一个向量(在这种情况下为float4)。因此,相邻线程仍在读取相邻数据,并且您具有理想的合并行为。在上面的示例中,线程 0 将读取 a[0].xa[0].ya[0].za[0].w,线程 1 将读取 a[1].xa[1].y 等。所有这些都将发生在单个 请求(即 SASS 指令),但可以拆分为多个事务。将请求拆分为多个事务不会导致任何效率损失(在这种情况下)。

    Nonsense 结构的情况下,编译器无法识别该结构也可以以类似的方式加载,因此在后台它必须为每个线程生成 3 或 4 次加载:

    • 一个 8 字节加载(或两个 4 字节加载)加载float3 group 的前两个字
    • 一个 4 字节加载来加载float3 group 的最后一个字
    • 一个 4 字节加载来加载 float other

    如果您绘制每个线程的上述负载,也许使用上图,您会看到每个负载都涉及一个步幅(每个线程加载的项目之间未使用的元素),因此会导致效率较低。

    通过在结构中使用仔细的类型转换或联合定义,您可以让编译器在一次加载中加载您的 Nonsense 结构。

    This answer 还涵盖了与 AoS -> SoA 转换和相关效率提升相关的一些想法。

    This answer 涵盖矢量加载细节。

    【讨论】:

    • 我不知道矢量负载是一回事。谢谢!
    • 也许作为旁注:编译器无法生成向量加载/存储以访问您的 struct Nonsense 的原因是因为向量加载/存储要求数据以 8 或 16 字节对齐对于您的结构,不能保证边界。如果您只是为您的结构提供必要的对齐 (struct alignas(16) Nonsense {…};),那么编译器 will be able to 会将结构成员访问权打包到向量加载/存储中……
    猜你喜欢
    • 1970-01-01
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    • 1970-01-01
    • 2023-04-08
    • 2014-11-13
    • 2018-04-19
    相关资源
    最近更新 更多