CUDA共享内存写入导致无法解释的长延迟

CUDA共享内存写入导致无法解释的长延迟,cuda,shared-memory,Cuda,Shared Memory,这让我快发疯了。我有一个由1D块组成的3D网格。每个块包含272个线程。每个线程对两个向量进行点积,并将其结果存储在共享内存数组中的相应位置,该数组的大小为[272],线程数相同。主线程正在调用多个内核,我正在计算执行它们所花费的时间。当我注释掉写入共享内存的行时,执行时间大约为2401毫秒。当我取消注释共享内存写入行时,执行时间非常长,比如450309毫秒。我尝试使用int值而不是double。我还尝试使用if(threadIdx.x==0)语句只让一个线程进行写入,以避免可能的库冲突。似乎什

这让我快发疯了。我有一个由1D块组成的3D网格。每个块包含272个线程。每个线程对两个向量进行点积,并将其结果存储在共享内存数组中的相应位置,该数组的大小为[272],线程数相同。主线程正在调用多个内核,我正在计算执行它们所花费的时间。当我注释掉写入共享内存的行时,执行时间大约为2401毫秒。当我取消注释共享内存写入行时,执行时间非常长,比如450309毫秒。我尝试使用int值而不是double。我还尝试使用if(threadIdx.x==0)语句只让一个线程进行写入,以避免可能的库冲突。似乎什么都不管用。 以下是调用线程代码:

  double theta=0;
int count=0;
cudaEventRecord(start,0);
while(theta <180)
{
theta+=0.18;

calc_LF<<<gridDim, blockDim>>>(ori_dev, X_dev, Y_dev, Z_dev, F_dev, F_grad_dev, g_oriD, r_vD, LF);
calc_S<<<gridDim, 272>>>(g_traD, LF, Ci, C);
count++;
}
cudaEventRecord( stop, 0 );
cudaEventSynchronize( stop );
cudaEventElapsedTime( &elapsedTime, start, stop );
err = cudaGetLastError();
if ( cudaSuccess != err )
    {
    fprintf( stderr, "Cuda error in file '%s' in line %i : %s.\n",
         __FILE__, __LINE__, cudaGetErrorString( err) );
    }
else
    {
    fprintf( stderr, "\n \n Cuda NO error in file '%s' in line %i : %s.\n",
         __FILE__, __LINE__, cudaGetErrorString( err) );
    printf("\n %d orientation updates: Total Time = %3.10f ms\n", count, elapsedTime);
    }
double theta=0;
整数计数=0;
cudaEventRecord(开始,0);

而(θ您看到的效果是编译器优化的结果。获取基本内核代码的可编译版本:

#define H (128)
#define W (128)

__device__
double getElement(const double *g, int t, int j, int w)
{
    return g[t + j*w];
}

__global__ 
void calc_S(double* g_traD, double* LF, double* Ci, double* C)
{
    __shared__ double G[H];

    // Get this block's global index 
    int blockId= blockIdx.x + gridDim.x*blockIdx.y + 
                   gridDim.x*gridDim.y*blockIdx.z;
    int tx= threadIdx.x;
    // This thread's global index
    int gtx= blockId*blockDim.x + threadIdx.x;

    int myTRA[W];
    double my_LF[W];
    for (int i=0; i<W; i++){
        my_LF[i]= LF[gtx];
    }

    for(int j=0; j<W; j++){
        myTRA[j]= getElement(g_traD, tx, j, W);
    }
    double sum;
    for(int j=0; j<W; j++)
    {
        sum += myTRA[j] * my_LF[j];
    }       

    // Write your sum to shared memory
    G[tx]=sum;
    __syncthreads();
}
有一个关于共享内存变量
G
未被使用的警告,但是编译器会尊重它并发出占用23个寄存器的代码。因此,现在,如果我在内核末尾注释掉
G[tx]=sum
,它会编译成这样:

$ nvcc -m64 -arch=sm_20 -cubin -Xptxas="-v"  dead_code.cu 
dead_code.cu(13): warning: variable "G" was declared but never referenced

dead_code.cu(13): warning: variable "G" was declared but never referenced

ptxas info    : 0 bytes gmem
ptxas info    : Compiling entry function '_Z6calc_SPdS_S_S_' for 'sm_20'
ptxas info    : Function properties for _Z6calc_SPdS_S_S_
    0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
ptxas info    : Used 2 registers, 64 bytes cmem[0]
现在只使用了两个寄存器,工具链发出以下信息:

$ cuobjdump -sass dead_code.cubin 

    code for sm_20
        Function : _Z6calc_SPdS_S_S_
    /*0000*/     /*0x00005de428004404*/     MOV R1, c [0x1] [0x100];
    /*0008*/     /*0xfc1fdc03207e0000*/     IMAD.U32.U32 RZ, R1, RZ, RZ;
    /*0010*/     /*0xffffdc0450ee0000*/     BAR.RED.POPC RZ, RZ;
    /*0018*/     /*0x00001de780000000*/     EXIT;
四条汇编指令。你所有的代码都不见了


这种影响的根本原因是编译器死代码的删除。编译器足够聪明,可以确定对全局或共享内存输出没有影响的代码是不需要的,并且可以删除。在这种情况下,一个对
G
的写操作被删除,整个内核实际上是毫无意义的,编译器只是优化了w把东西挖出来。你可以看到其他一些去除死代码的例子及其效果。后者在OpenCL中,但同样的机制也适用。

当你注释掉一行并得到这样的大修改时,这可能意味着编译器正在优化掉大量不再有意义的代码。你的代码ks对我来说很奇怪。你的内核以唯一的输出写入共享内存而结束?我不清楚你在这里问什么。你想让人解释为什么注释掉一行代码会带来很大的改变吗?请发布一个最小的可复制性。发布的代码不完整,并且有大量的变量您尚未指定大小或值。感谢您的回复。代码非常大,因此我不确定最小可复制性应该包括哪些内容。共享内存写入不是我内核中的最后一件事,但我已注释掉所有剩余代码,以找出内核占用大量时间的位置。值得一提的是,我将o我自己懒得处理VS2008中的所有路径和依赖关系,所以我只是在NVIDIA GPU computing SDK 4.0附带的MatrixMul SDK示例中插入了我的代码。感谢您提供的见解。因此,如果我错了,请纠正我,这意味着我进行共享内存写入的时间实际上是内核运行所需的时间完整地执行点积。这实际上是有意义的,因为每个线程执行大约544次乘法和544次加法,这可能会导致严重的序列化。我将尝试进行一些平铺,看看我能得到多大的改进。再次感谢:)您的理解是正确的。在我链接到的第一个答案中,您可以看到一个技巧,如果您试图对内核的各个部分进行基准测试,可以使用它来击败死代码优化。
    __global__ void calc_S(double* g_traD, double* LF, double* Ci, double* C)
{
__shared__ double T[H];
__shared__ double G[H];

   // Get this block's global index 
  int blockId= blockIdx.x + gridDim.x*blockIdx.y + gridDim.x*gridDim.y*blockIdx.z;
  int tx= threadIdx.x;
// This thread's global index
   int gtx= blockId*blockDim.x + threadIdx.x;

int myTRA[W];
double my_LF[W];
for (int i=0; i<W; i++){
   my_LF[i]= LF[gtx];
}

for(int j=0; j<W; j++){
    myTRA[j]= getElement(g_traD, tx, j, W);
        }
    double sum;
    for(int j=0; j<W; j++)
    {
         sum += myTRA[j] * my_LF[j];
    }       

// Write your sum to shared memory
    G[tx]=sum;
    __syncthreads();
      }
#define H (128)
#define W (128)

__device__
double getElement(const double *g, int t, int j, int w)
{
    return g[t + j*w];
}

__global__ 
void calc_S(double* g_traD, double* LF, double* Ci, double* C)
{
    __shared__ double G[H];

    // Get this block's global index 
    int blockId= blockIdx.x + gridDim.x*blockIdx.y + 
                   gridDim.x*gridDim.y*blockIdx.z;
    int tx= threadIdx.x;
    // This thread's global index
    int gtx= blockId*blockDim.x + threadIdx.x;

    int myTRA[W];
    double my_LF[W];
    for (int i=0; i<W; i++){
        my_LF[i]= LF[gtx];
    }

    for(int j=0; j<W; j++){
        myTRA[j]= getElement(g_traD, tx, j, W);
    }
    double sum;
    for(int j=0; j<W; j++)
    {
        sum += myTRA[j] * my_LF[j];
    }       

    // Write your sum to shared memory
    G[tx]=sum;
    __syncthreads();
}
$ nvcc -m64 -arch=sm_20 -cubin -Xptxas="-v"  dead_code.cu 
dead_code.cu(13): warning: variable "G" was set but never used

dead_code.cu(13): warning: variable "G" was set but never used

ptxas info    : 0 bytes gmem
ptxas info    : Compiling entry function '_Z6calc_SPdS_S_S_' for 'sm_20'
ptxas info    : Function properties for _Z6calc_SPdS_S_S_
    1536 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
ptxas info    : Used 23 registers, 1024 bytes smem, 64 bytes cmem[0]
$ nvcc -m64 -arch=sm_20 -cubin -Xptxas="-v"  dead_code.cu 
dead_code.cu(13): warning: variable "G" was declared but never referenced

dead_code.cu(13): warning: variable "G" was declared but never referenced

ptxas info    : 0 bytes gmem
ptxas info    : Compiling entry function '_Z6calc_SPdS_S_S_' for 'sm_20'
ptxas info    : Function properties for _Z6calc_SPdS_S_S_
    0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
ptxas info    : Used 2 registers, 64 bytes cmem[0]
$ cuobjdump -sass dead_code.cubin 

    code for sm_20
        Function : _Z6calc_SPdS_S_S_
    /*0000*/     /*0x00005de428004404*/     MOV R1, c [0x1] [0x100];
    /*0008*/     /*0xfc1fdc03207e0000*/     IMAD.U32.U32 RZ, R1, RZ, RZ;
    /*0010*/     /*0xffffdc0450ee0000*/     BAR.RED.POPC RZ, RZ;
    /*0018*/     /*0x00001de780000000*/     EXIT;