我正在尝试运行以下内核,类似于稀疏矩阵向量乘法(SpMV)内核。
__global__ void cost_spmv(const int *population,const int *row,const int *col,int *out){
/*Calculate the cost vector for multiplication of the matrices*/
//int tid=threadIdx.x+blockDim.x*blockIdx.x;
int lane=threadIdx.x;
extern __shared__ int val[];
int r=blockIdx.x;
int rowStart=row[r];
int rowEnd=row[r+1];
val[threadIdx.x]=0;
for(int i=rowStart+lane;i<rowEnd;i+=32)
val[threadIdx.x]+= population[col[i]];
__syncthreads();
if(lane<16)
val[threadIdx.x]+=val[threadIdx.x+16];
if(lane<8)
val[threadIdx.x]+=val[threadIdx.x+8];
if(lane<4)
val[threadIdx.x]+=val[threadIdx.x+4];
if(lane<2)
val[threadIdx.x]+=val[threadIdx.x+2];
if(lane<1)
val[threadIdx.x]+=val[threadIdx.x+1];
if(lane==0)
out[r]=val[threadIdx.x];
}
使用
调用内核cost_spmv<<<numRows,32,32*sizeof(int)>>>(population,rowArray,colArray, out)
其中numRows
是数组的大小,out和rowArray(numRows+1 actually)
。 rowArray[i]
包含属于第i行的元素的起始索引.colArray的大小为rowArray[numRows]
。 colArray[i]
包含使用rowArray
描述的行的非零值的列号。
然而,在针对计算能力 3.5在Tesla P4上 进行编译时,与我获得的计算能力6.1相比,我获得了不同的答案。我在特斯拉P4上使用计算 功能6.1 的答案与我在920m上使用计算 功能3.5 <得到的答案相同/ strong>即可。可能是什么原因?
答案 0 :(得分:0)
请记住,CUDA编译器具有世界的单线程视图。它不知道用于执行代码的运行时配置,这在编译时是不可用的。
在val[]
的加载和先前写入val[]
之间的代码中没有表达依赖关系。因此,编译器可以根据需要自由移动负载。在某些情况下,它可能选择提前发出一些或所有负载以增加代码的负载延迟容限,例如,通过转换代码如下:
int __temp0 = val[threadIdx.x+16];
int __temp1 = val[threadIdx.x+ 8];
int __temp2 = val[threadIdx.x+ 4];
int __temp3 = val[threadIdx.x+ 2];
int __temp4 = val[threadIdx.x+ 1];
if(lane<16)
val[threadIdx.x]+=__temp0;
if(lane<8)
val[threadIdx.x]+=__temp1;
if(lane<4)
val[threadIdx.x]+=__temp2;
if(lane<2)
val[threadIdx.x]+=__temp3;
if(lane<1)
val[threadIdx.x]+=__temp4;
根据编译器选择放置负载的位置,缩减序列的结果会有所不同。 CUDA编译器中的代码生成和特别是指令调度因GPU架构而异,因此在编译不同的GPU架构时可能会观察到不同的结果。
为了在加载和存储之间强制实现所需的依赖关系,CUDA编程模型认可的方法是在每个缩减步骤之后使用__syncthreads()
来创建障碍。实现期望结果的可能更快但更糟糕的方法是通过使用val
修饰符来声明代码范围之外的代理可以更改volatile
。这可以防止编译器移动val[]
的负载。