Cuda 对于atomicAdd函数,全局内存的不同位置之间有太多的时间间隔

Cuda 对于atomicAdd函数,全局内存的不同位置之间有太多的时间间隔,cuda,atomic,latency,Cuda,Atomic,Latency,内核的主要思想是获取全局内存的atomicAdd的延迟,因此首先要获取一个线程和一个块的atomicAdd的基本延迟。全局内存的不同位置之间有太多的时间间隔,为什么?内核如下: kernel.h \ifndef\u内核_ #定义内核_ 模板 __全局无效冲突(T*y,T*oldVal,ITYPE*间隔,ITYPE*时间) { ITYPE warp,vector_lane,thread_lane,thread_id,partial; 翘曲=32; 向量_-lane=(blockDim.x+warp

内核的主要思想是获取全局内存的atomicAdd的延迟,因此首先要获取一个线程和一个块的atomicAdd的基本延迟。全局内存的不同位置之间有太多的时间间隔,为什么?内核如下: kernel.h

\ifndef\u内核_
#定义内核_
模板
__全局无效冲突(T*y,T*oldVal,ITYPE*间隔,ITYPE*时间)
{
ITYPE warp,vector_lane,thread_lane,thread_id,partial;
翘曲=32;
向量_-lane=(blockDim.x+warp-1)/warp;
thread_lane=threadIdx.x&(warp-1);
thread_id=threadIdx.x/warp;
ITYPE threads=threadIdx.x;
输入开始时间、结束时间;
ITYPE位置=0;
T值=1.0;
T old=0.0f;
部分=螺纹IDX.x&(warp-1);
开始时间=时钟();
//为可变位置设置不同的值
旧=原子添加(&y[位置],值);
结束时间=时钟();
如果(线程通道==0)
时间[blockIdx.x*向量通道+线程id]=结束时间-开始时间;
oldVal[2]=旧的;
}
模板
无效冲突(T*y,T*oldVal,ITYPE*interval,ITYPE*time,ITYPE&number\SM)
{
const unsigned int THREADS_PER_BLOCK=1;
const unsigned int NUM_BLOCKS=1;
//获取多处理器的数量
itypedev=0;
cudaDeviceProp deviceProp;
CudAgetDeviceProperty(和deviceProp、dev);
number_SM=deviceProp.multiProcessorCount;
printf(“多处理器=%d\n”,数字);

如果(NUM_)BLOCKSWow,那么大量的代码与您试图展示的内容几乎无关。下次尝试删除不必要的部分

另外,您正在将一个
float
值作为第二个参数传递给
memset
memset
set
byte
数量,并期望第二个参数中有一个
无符号字符

使用您的代码,我能够重现
位置
值0和2之间的一些变化。对于0情况,我得到的时间为76,对于2情况,我得到的时间为118,因此没有您的变化那么大

但是,由于您正在进行更改,然后重新编译代码,因此编译器可以针对每种情况发出不同的指令流,从而使结果看起来有所不同

我建议改为尝试以下代码:

#include <iostream>
#define DWIDTH 32
typedef float mytype;

template <typename T>
__global__ void collision(int *time, T *data, T *old ){

  for (int i = 0; i < DWIDTH; i++){
    unsigned long start_time = clock64();
    T my_old = atomicAdd(data+i, (T) 1);
    unsigned long end_time = clock64();
    time[i] = end_time - start_time;
    old[i] = my_old;
    }

}

int main(){
  mytype *h_data, *d_data;
  int *h_time, *d_time;
  mytype *h_old, *d_old;
  cudaMalloc((void **)&d_time, DWIDTH*sizeof(int));
  h_time = (int *)malloc(DWIDTH*sizeof(int));
  cudaMalloc((void **)&d_data, DWIDTH*sizeof(mytype));
  h_data = (mytype *)malloc(DWIDTH*sizeof(mytype));
  cudaMalloc((void **)&d_old, DWIDTH*sizeof(mytype));
  h_old = (mytype *)malloc(DWIDTH*sizeof(mytype));

  for (int i=0; i<DWIDTH; i++){
    h_time[i] = 0;
    h_data[i] = (mytype) 0;
  }
  cudaMemcpy(d_data, h_data, DWIDTH*sizeof(mytype), cudaMemcpyHostToDevice);
  cudaMemcpy(d_time, h_time, DWIDTH*sizeof(int), cudaMemcpyHostToDevice);

  collision<<<1,1>>>(d_time, d_data, d_old);
  cudaMemcpy(h_time, d_time, DWIDTH*sizeof(int), cudaMemcpyDeviceToHost);
  cudaMemcpy(h_data, d_data, DWIDTH*sizeof(mytype), cudaMemcpyDeviceToHost);
  cudaMemcpy(h_old, d_old, DWIDTH*sizeof(mytype), cudaMemcpyDeviceToHost);

  std::cout << "times:" << std::endl;
  for (int i = 0; i < DWIDTH; i++)
    std::cout << h_time[i] << " ";
  std::cout << std::endl << "data:" << std::endl;
  for (int i = 0; i < DWIDTH; i++)
    std::cout << h_data[i] << " ";
  std::cout << std::endl << "old:" << std::endl;
  for (int i = 0; i < DWIDTH; i++)
    std::cout << h_old[i] << " ";
  std::cout << std::endl;
  return 0;
}
这段代码的好处是编译器没有机会根据我是将
位置设置为0还是2发出不同的指令流。因此,我得到了一致的结果

对于您的代码,当我使用非零值编译
位置
(和
sm_20
)时,我得到如下sass:

    /*0038*/     /*0x40011c042c000001*/     S2R R4, SR_ClockLo;
    /*0040*/     /*0x04411e036000c000*/     SHL.W R4, R4, 0x1;
    /*0048*/     /*0x80015de428004000*/     MOV R5, c [0x0] [0x20];
    /*0050*/     /*0x10519c034801c000*/     IADD R6.CC, R5, 0x4;
    /*0058*/     /*0x00015de218fe0000*/     MOV32I R5, 0x3f800000;
    /*0060*/     /*0x93f1dc4348004000*/     IADD.X R7, RZ, c [0x0] [0x24];
    /*0068*/     /*0x00615e056c7e2800*/     ATOM.E.ADD.F32.FTZ.RN R5, [R6], R5;
    /*0070*/     /*0x40019c042c000001*/     S2R R6, SR_ClockLo;
    /*0048*/     /*0x40019c042c000001*/     S2R R6, SR_ClockLo;
    /*0050*/     /*0x04619e036000c000*/     SHL.W R6, R6, 0x1;
    /*0058*/     /*0x0001dde218fe0000*/     MOV32I R7, 0x3f800000;
    /*0060*/     /*0x0021de056c7e1000*/     ATOM.E.ADD.F32.FTZ.RN R2, [R2], R7;
    /*0068*/     /*0x4000dc042c000001*/     S2R R3, SR_ClockLo;
当我为
position
(和
sm_20
)使用零值进行编译时,我得到如下sass:

    /*0038*/     /*0x40011c042c000001*/     S2R R4, SR_ClockLo;
    /*0040*/     /*0x04411e036000c000*/     SHL.W R4, R4, 0x1;
    /*0048*/     /*0x80015de428004000*/     MOV R5, c [0x0] [0x20];
    /*0050*/     /*0x10519c034801c000*/     IADD R6.CC, R5, 0x4;
    /*0058*/     /*0x00015de218fe0000*/     MOV32I R5, 0x3f800000;
    /*0060*/     /*0x93f1dc4348004000*/     IADD.X R7, RZ, c [0x0] [0x24];
    /*0068*/     /*0x00615e056c7e2800*/     ATOM.E.ADD.F32.FTZ.RN R5, [R6], R5;
    /*0070*/     /*0x40019c042c000001*/     S2R R6, SR_ClockLo;
    /*0048*/     /*0x40019c042c000001*/     S2R R6, SR_ClockLo;
    /*0050*/     /*0x04619e036000c000*/     SHL.W R6, R6, 0x1;
    /*0058*/     /*0x0001dde218fe0000*/     MOV32I R7, 0x3f800000;
    /*0060*/     /*0x0021de056c7e1000*/     ATOM.E.ADD.F32.FTZ.RN R2, [R2], R7;
    /*0068*/     /*0x4000dc042c000001*/     S2R R3, SR_ClockLo;

因此,我们可以看到,对于您的代码,
position
的值可能会影响生成的代码,从而影响时间。

如何启动GPU内核?如何测量时间?启动GPU内核是“碰撞(y,oldVal,interval,time);”,位于“\uuuuuuu碰撞(…)”中测量时间为end_time-start_time,存储在名为time的数组中,start_time为“start_time=clock();”end_time为“end_time=clock();”,它们也在上面的代码中。对不起,T是float,ITYPE是int。请提供一个完整的应用程序,而不仅仅是内核代码。这是在linux还是windows上?所有的代码都在原来的地方提供,由我修改。顺序如下:collision.cu->run.h->kernel.h,并提供makefile。我们之间的主要区别是en两个代码是atomicAdd(数据+i,(T)1),my代码将atomicAdd()的返回值分配给变体,Crovella没有。但是,end_time-start_time的值是问题原子操作,因为atomicAdd()没有返回值,换句话说,它不包括原子操作本身的时间。很好。我在答案中修改了我的代码,以保存
old
的值。源代码的计时部分与您的基本相同。我仍然得到一致的结果。此外,我通过对SASS的分析表明,您的代码无法可能会产生一致的结果,即使
atomicAdd
时间是一致的。编译器在每种情况下都会生成一组不同的要计时的指令。我修改了Crovella的内核:模板全局无效冲突(int*time,T*data,T*old){//for(int I=0;I数据+0
数据+2
,而不是
数据+I
)如果您想比较这两种情况下的性能,这是一个错误。您的结果是合理的,因为编译器在这两种情况下生成的代码不同。
$ nvcc -arch=sm_35 -o t284 t284.cu

$ ./t284
times:
98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98
data:
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
old:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
$
    /*0038*/     /*0x40011c042c000001*/     S2R R4, SR_ClockLo;
    /*0040*/     /*0x04411e036000c000*/     SHL.W R4, R4, 0x1;
    /*0048*/     /*0x80015de428004000*/     MOV R5, c [0x0] [0x20];
    /*0050*/     /*0x10519c034801c000*/     IADD R6.CC, R5, 0x4;
    /*0058*/     /*0x00015de218fe0000*/     MOV32I R5, 0x3f800000;
    /*0060*/     /*0x93f1dc4348004000*/     IADD.X R7, RZ, c [0x0] [0x24];
    /*0068*/     /*0x00615e056c7e2800*/     ATOM.E.ADD.F32.FTZ.RN R5, [R6], R5;
    /*0070*/     /*0x40019c042c000001*/     S2R R6, SR_ClockLo;
    /*0048*/     /*0x40019c042c000001*/     S2R R6, SR_ClockLo;
    /*0050*/     /*0x04619e036000c000*/     SHL.W R6, R6, 0x1;
    /*0058*/     /*0x0001dde218fe0000*/     MOV32I R7, 0x3f800000;
    /*0060*/     /*0x0021de056c7e1000*/     ATOM.E.ADD.F32.FTZ.RN R2, [R2], R7;
    /*0068*/     /*0x4000dc042c000001*/     S2R R3, SR_ClockLo;