我正在使用一个结构数组,我希望每个块在共享内存中加载一个数组的单元格。例如:块0将在共享存储器中加载数组[0],块1将加载数组[1]。
为了做到这一点,我在float *中强制转换结构数组,以便尝试合并内存访问。
我有两个版本的代码
版本1
__global__
void load_structure(float * label){
__shared__ float shared_label[48*16];
__shared__ struct LABEL_2D* self_label;
shared_label[threadIdx.x*16+threadIdx.y] =
label[blockIdx.x*sizeof(struct LABEL_2D)/sizeof(float) +threadIdx.x*16+threadIdx.y];
shared_label[(threadIdx.x+16)*16+threadIdx.y] =
label[blockIdx.x*sizeof(struct LABEL_2D)/sizeof(float) + (threadIdx.x+16)*16+threadIdx.y];
if((threadIdx.x+32)*16+threadIdx.y < sizeof(struct LABEL_2D)/sizeof(float)) {
shared_label[(threadIdx.x+32)*16+threadIdx.y] =
label[blockIdx.x*sizeof(struct LABEL_2D)/sizeof(float) +(threadIdx.x+32)*16+threadIdx.y];
}
if(threadIdx.x == 0){
self_label = (struct LABEL_2D *) shared_label;
}
__syncthreads();
return;
}
...
dim3 dimBlock(16,16);
load_structure<<<2000,dimBlock>>>((float*)d_Label;
计算时间:0.740032 ms
版本2
__global__
void load_structure(float * label){
__shared__ float shared_label[32*32];
__shared__ struct LABEL_2D* self_label;
if(threadIdx.x*32+threadIdx.y < *sizeof(struct LABEL_2D)/sizeof(float))
shared_label[threadIdx.x*32+threadIdx.y] =
label[blockIdx.x*sizeof(struct LABEL_2D)/sizeof(float)+threadIdx.x*32+threadIdx.y+];
if(threadIdx.x == 0){
self_label = (struct LABEL_2D *) shared_label;
}
__syncthreads();
return;
}
dim3 dimBlock(32,32);
load_structure<<<2000,dimBlock>>>((float*)d_Label);
计算时间:2.559264 ms
在这两个版本中,我都使用了nvidia分析器,全局负载效率为8%。
我有两个问题: 1 - 我不明白为什么时间有所不同。 2 - 我的电话合并了吗?
我正在使用具有2.1计算能力的视频卡(32线程/包装)
答案 0 :(得分:2)
您的全局负载未合并。 8%相当低,你可能做的最差的是3%。
我认为这样做的主要原因是您基于threadIdx.x和threadIdx.y进行索引的方式。让我们考虑来自第二个内核的这行代码(第一个内核有类似的问题):
shared_label[threadIdx.x*32+threadIdx.y] = label[blockIdx.x*sizeof(struct LABEL_2D)/sizeof(float)+threadIdx.x*32+threadIdx.y];
特别要考虑这个索引:
threadIdx.x*32+threadIdx.y
CUDA经线按X,Y,Z的顺序分组。这意味着warp中快速变化的索引将首先出现在X索引上,然后出现在Y上,然后出现在Z上。例如,如果我有一个16x16的线程块,那么第一个warp将具有threadIdx.x跨越的线程从0到15并且threadIdx.y仅跨越0到1.在这种情况下,相邻的线程将主要具有相邻的threadIdx.x索引。
您的代码的结果是您因索引而破坏了合并。如果您可以重新构建加载和存储以使用此类索引:
threadIdx.y*32+threadIdx.x
您将突然看到全球负载效率的显着提升。 (您的共享内存使用情况也可能更好。)
我意识到你有两个问题,当我想到第一个时,我很困惑。你告诉我们“计算时间”是大约。第二次实现的时间长4倍,但可能你指的是compute_interpolation
内核,你根本没有显示任何细节,除非在第二种情况下你启动了4倍的线程。也许这里没有神秘感。您尚未显示任何代码。并使用内核在共享内存中加载一堆东西然后退出也没有任何意义。共享内存内容不会从一个内核调用持续到下一个内核。
答案 1 :(得分:0)
我解决了我的问题,访问内存模式在以前的版本中不正确。 在阅读了cuda最佳实践指南的第6.2.1段之后,我发现如果它们一致,访问速度会更快。
为了对我的访问模式进行分析,我在结构中添加了一个“假”变量,以便将结构大小除以128(现金大小行)。
通过这个策略,我获得了良好的性能:为了将2000结构加载到2000块,它只用了0.16ms。
以下是代码的版本:
struct TEST_ALIGNED{
float data[745];
float aligned[23];
};
__global__
void load_structure_v4(float * structure){
// Shared structure within a block
__shared__ float s_structure[768];
__shared__ struct TEST_ALIGNED * shared_structure;
s_structure[threadIdx.x] =
structure[blockIdx.x*sizeof(struct TEST_ALIGNED)/sizeof(float) + threadIdx.x];
s_structure[threadIdx.x + 256] =
structure[blockIdx.x*sizeof(struct TEST_ALIGNED)/sizeof(float) + threadIdx.x + 256];
if(threadIdx.x < 745)
s_structure[threadIdx.x + 512] =
structure[blockIdx.x*sizeof(struct TEST_ALIGNED)/sizeof(float) + threadIdx.x + 512];
if(threadIdx.x == 0)
shared_structure = (struct TEST_ALIGNED*) s_structure;
__syncthreads();
return;
}
dim3 dimBlock(256);
load_structure_v4<<<2000,dimBlock>>>((float*)d_test_aligned);
我仍在寻找优化,如果找到一些优惠,我会在此发布。