Cuda 为什么PTX显示128位结构赋值的32位加载操作?

Cuda 为什么PTX显示128位结构赋值的32位加载操作?,cuda,gpu,ptx,Cuda,Gpu,Ptx,我定义了128位的自定义结构,如下所示- typedef struct dtype{ int val; int temp2; int temp3; int temp4; }dtype; 然后我执行了一项任务:- dtype temp= h_a[i]; //where h_a is dtype * 我本以为是128位加载,但PTX却显示了一个32位加载操作- mul.wide.s32 %rd4, %r18, 16; add.s64 %rd5, %rd1, %rd4; ld

我定义了128位的自定义结构,如下所示-

typedef struct dtype{
int val;
int temp2;
int temp3;
int temp4;
}dtype;
然后我执行了一项任务:-

dtype temp= h_a[i]; //where h_a is dtype *
我本以为是128位加载,但PTX却显示了一个32位加载操作-

mul.wide.s32    %rd4, %r18, 16;
add.s64         %rd5, %rd1, %rd4;
ld.global.u32   %r17, [%rd5];
它不应该看起来像ld.global.v4.u32%r17,[%rd5]


哪里出错了?

如果保证内存与类型的大小对齐,编译器将只发出矢量化加载或存储指令,并且使用类型的所有元素,否则矢量指令将优化为标量指令以节省带宽

如果您这样做:

struct dtype{
int val;
int temp2;
int temp3;
int temp4;
};

struct __align__ (16) adtype{
int val;
int temp2;
int temp3;
int temp4;
};

__global__
void kernel(adtype* x, dtype* y)
{
    adtype lx = x[threadIdx.x];
    dtype ly;
    ly.val = lx.temp4;
    ly.temp2 = lx.temp3;
    ly.temp3 = lx.val;
    ly.temp4 = lx.temp2;

    y[threadIdx.x] = ly;
}
你应该得到这样的东西:

visible .entry _Z6kernelP6adtypeP5dtype(
        .param .u64 _Z6kernelP6adtypeP5dtype_param_0,
        .param .u64 _Z6kernelP6adtypeP5dtype_param_1
)
{

        ld.param.u64    %rd1, [_Z6kernelP6adtypeP5dtype_param_0];
        ld.param.u64    %rd2, [_Z6kernelP6adtypeP5dtype_param_1];
        cvta.to.global.u64      %rd3, %rd2;
        cvta.to.global.u64      %rd4, %rd1;
        mov.u32         %r1, %tid.x;
        mul.wide.u32    %rd5, %r1, 16;
        add.s64         %rd6, %rd4, %rd5;
        ld.global.v4.u32        {%r2, %r3, %r4, %r5}, [%rd6];
        add.s64         %rd7, %rd3, %rd5;
        st.global.u32   [%rd7], %r5;
        st.global.u32   [%rd7+4], %r4;
        st.global.u32   [%rd7+8], %r2;
        st.global.u32   [%rd7+12], %r3;
        ret;
}
在这里,您可以清楚地看到对齐类型的矢量化加载,以及非对齐类型的非矢量化存储。如果内核已更改,以使存储与对齐的版本一致:

__global__
void kernel(adtype* x, dtype* y)
{
    dtype ly = y[threadIdx.x];
    adtype lx;
    lx.val = ly.temp4;
    lx.temp2 = ly.temp3;
    lx.temp3 = ly.val;
    lx.temp4 = ly.temp2;

    x[threadIdx.x] = lx;
}
您将获得以下信息:

.visible .entry _Z6kernelP6adtypeP5dtype(
        .param .u64 _Z6kernelP6adtypeP5dtype_param_0,
        .param .u64 _Z6kernelP6adtypeP5dtype_param_1
)
{

        ld.param.u64    %rd1, [_Z6kernelP6adtypeP5dtype_param_0];
        ld.param.u64    %rd2, [_Z6kernelP6adtypeP5dtype_param_1];
        cvta.to.global.u64      %rd3, %rd1;
        cvta.to.global.u64      %rd4, %rd2;
        mov.u32         %r1, %tid.x;
        mul.wide.u32    %rd5, %r1, 16;
        add.s64         %rd6, %rd4, %rd5;
        add.s64         %rd7, %rd3, %rd5;
        ld.global.u32   %r2, [%rd6+12];
        ld.global.u32   %r3, [%rd6+8];
        ld.global.u32   %r4, [%rd6+4];
        ld.global.u32   %r5, [%rd6];
        st.global.v4.u32        [%rd7], {%r2, %r3, %r5, %r4};
        ret;
}
现在,对齐类型与矢量化指令一起存储


[使用默认的Godbolt toolchain 10.2为sm_53编译的所有代码]

我添加了一个附加点,以防任何人碰巧遇到相同的问题

{
        dtype temp = h_a[i];                  //Loading data  exactly needed

        sum.val += temp.val;
}
我遵循了上述^^答案中给出的步骤,但是我没有得到128位的加载,尽管上述方法绝对正确

问题是编译器看到在结构的4个字段中,我在一些加法操作中只使用了1个字段。所以它非常聪明地只加载了我需要的块。所以不管我怎么做,我总是得到一个32位的负载

{
        dtype temp = h_a[i];                  //Loading data  exactly needed

        sum.val += temp.val;
        sum.temp2 += temp.temp2;
        sum.temp3 += temp.temp3;
        sum.temp4 += temp.temp4;
}
一点零钱。 现在我正在使用所有字段。因此编译器加载了所有字段! 是的,现在使用上面^^答案中给出的方法,使用^对齐^ 16,我得到了正确的128位加载。
虽然这对很多人来说可能很明显,但我不是一个资深的程序员。我只在某些地方使用编码来完成我的项目。这对我来说是非常有洞察力的,我希望有人也能从中受益

您必须使用正确的对齐方式定义类型我使用了_align _16,但仍然显示相同的内容。对我来说,它不是类型定义结构_align _16 dtype{int val,temp2,temp3,temp4;}dtype;也许我搞错了。这就是你的建议吗?你会很高兴听到CUDA编译器已经包含了这个优化十几年了。您的说明还强调了为什么在问题中提供一个最小的完整示例很重要。根据提供的代码,您在这里观察到的效果是站点参与者无法合理预期的。是的,您是对的,下次将记住这一点。