Cuda 对于atomicAdd函数,全局内存的不同位置之间有太多的时间间隔
内核的主要思想是获取全局内存的atomicAdd的延迟,因此首先要获取一个线程和一个块的atomicAdd的基本延迟。全局内存的不同位置之间有太多的时间间隔,为什么?内核如下: kernel.hCuda 对于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
\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
setbyte
数量,并期望第二个参数中有一个无符号字符
使用您的代码,我能够重现位置
值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;