CUDA 中每个 Warp 关系的数据大小与指令
Data Size to Instructions per Warp relationship in CUDA
我试图查看当数据类型的大小发生变化时内核中执行的指令数
为了获得自定义大小的数据结构,我创建了如下结构,
#define DATABYTES 40
__host__ __device__
struct floatArray
{
float a[DATABYTES/4];
};
然后创建了一个内核只是为了将上述数据类型数组从一个数组复制到另一个数组
__global__
void copy_large_data(floatArray * d_in, floatArray * d_out)
{
d_out[threadIdx.x] = d_in[threadIdx.x];
}
然后用一个块只为 32 个线程调用上面的内核
floatArray * d_in;
floatArray * d_out;
cudaMalloc(&d_in, 32 * sizeof(floatArray));
cudaMalloc(&d_out, 32 * sizeof(floatArray));
copy_large_data<<<1, 32>>>(d_in, d_out);
当我使用 nvprof
分析程序并检查 instructions per warp
时,我可以看到参数值随着 DATABYTES
值的变化而变化。
我的问题是,这个指令计数增加的原因是否是由于 floatArray
结构中的数组。因为当我们在内核中调用复制时,它实际上是在 floatArray
结构内部扩展并复制数组 a
的每个元素,从而创建更多指令。
有没有办法使用一条指令在内核中复制自定义结构变量?
你的假设是正确的,当你改变数组的大小时复制指令的数量会增加。您可以在 PTX 代码和程序集中检查它,如下所示。
load/store 指令的最大长度为 128 位,参见例如here。这意味着对于您的情况,您仍然可以通过使用 float4
而不是 float
.
提高 4 倍
或者,您可以明确指定数据结构的对齐方式,如 programming guide:
中所述
#define DATABYTES 32
struct __align__(16) floatArray
{
float a[DATABYTES/4];
};
要查看 PTX 代码生成目标文件 nvcc -c ...
并使用 cubobjdump --dump-ptx objfile.o
。
对于您的示例,相关部分如下所示:
ld.global.f32 %f1, [%rd7];
ld.global.f32 %f2, [%rd7+4];
ld.global.f32 %f3, [%rd7+8];
ld.global.f32 %f4, [%rd7+12];
ld.global.f32 %f5, [%rd7+16];
ld.global.f32 %f6, [%rd7+20];
ld.global.f32 %f7, [%rd7+24];
ld.global.f32 %f8, [%rd7+28];
ld.global.f32 %f9, [%rd7+32];
ld.global.f32 %f10, [%rd7+36];
st.global.f32 [%rd6+36], %f10;
st.global.f32 [%rd6+32], %f9;
st.global.f32 [%rd6+28], %f8;
st.global.f32 [%rd6+24], %f7;
st.global.f32 [%rd6+20], %f6;
st.global.f32 [%rd6+16], %f5;
st.global.f32 [%rd6+12], %f4;
st.global.f32 [%rd6+8], %f3;
st.global.f32 [%rd6+4], %f2;
st.global.f32 [%rd6], %f1;
如果进一步增加数组,您会发现编译器将选择循环而不是为每个 load/store.
发出指令的点
因此,您可以使用 cubobjdump --dump-sass objfile.o
检查程序集
我试图查看当数据类型的大小发生变化时内核中执行的指令数
为了获得自定义大小的数据结构,我创建了如下结构,
#define DATABYTES 40
__host__ __device__
struct floatArray
{
float a[DATABYTES/4];
};
然后创建了一个内核只是为了将上述数据类型数组从一个数组复制到另一个数组
__global__
void copy_large_data(floatArray * d_in, floatArray * d_out)
{
d_out[threadIdx.x] = d_in[threadIdx.x];
}
然后用一个块只为 32 个线程调用上面的内核
floatArray * d_in;
floatArray * d_out;
cudaMalloc(&d_in, 32 * sizeof(floatArray));
cudaMalloc(&d_out, 32 * sizeof(floatArray));
copy_large_data<<<1, 32>>>(d_in, d_out);
当我使用 nvprof
分析程序并检查 instructions per warp
时,我可以看到参数值随着 DATABYTES
值的变化而变化。
我的问题是,这个指令计数增加的原因是否是由于 floatArray
结构中的数组。因为当我们在内核中调用复制时,它实际上是在 floatArray
结构内部扩展并复制数组 a
的每个元素,从而创建更多指令。
有没有办法使用一条指令在内核中复制自定义结构变量?
你的假设是正确的,当你改变数组的大小时复制指令的数量会增加。您可以在 PTX 代码和程序集中检查它,如下所示。
load/store 指令的最大长度为 128 位,参见例如here。这意味着对于您的情况,您仍然可以通过使用 float4
而不是 float
.
或者,您可以明确指定数据结构的对齐方式,如 programming guide:
中所述#define DATABYTES 32
struct __align__(16) floatArray
{
float a[DATABYTES/4];
};
要查看 PTX 代码生成目标文件 nvcc -c ...
并使用 cubobjdump --dump-ptx objfile.o
。
对于您的示例,相关部分如下所示:
ld.global.f32 %f1, [%rd7];
ld.global.f32 %f2, [%rd7+4];
ld.global.f32 %f3, [%rd7+8];
ld.global.f32 %f4, [%rd7+12];
ld.global.f32 %f5, [%rd7+16];
ld.global.f32 %f6, [%rd7+20];
ld.global.f32 %f7, [%rd7+24];
ld.global.f32 %f8, [%rd7+28];
ld.global.f32 %f9, [%rd7+32];
ld.global.f32 %f10, [%rd7+36];
st.global.f32 [%rd6+36], %f10;
st.global.f32 [%rd6+32], %f9;
st.global.f32 [%rd6+28], %f8;
st.global.f32 [%rd6+24], %f7;
st.global.f32 [%rd6+20], %f6;
st.global.f32 [%rd6+16], %f5;
st.global.f32 [%rd6+12], %f4;
st.global.f32 [%rd6+8], %f3;
st.global.f32 [%rd6+4], %f2;
st.global.f32 [%rd6], %f1;
如果进一步增加数组,您会发现编译器将选择循环而不是为每个 load/store.
发出指令的点因此,您可以使用 cubobjdump --dump-sass objfile.o