Cuda 为什么PTX显示128位结构赋值的32位加载操作?
我定义了128位的自定义结构,如下所示-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
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编译器已经包含了这个优化十几年了。您的说明还强调了为什么在问题中提供一个最小的完整示例很重要。根据提供的代码,您在这里观察到的效果是站点参与者无法合理预期的。是的,您是对的,下次将记住这一点。