我一直在尝试编写一个内核来计算 N 给定点之间距离的倒数之和。C 中的串行尾声就像
average = 0;
for(int i = 0; i < Np; i++){
for(int j = i + 1; j < Np; j++){
average += 1.0e0f/sqrtf((rx[i]-rx[j])*(rx[i]-rx[j]) + (ry[i]-ry[j])*(ry[i]-ry[j]));
}
}
average = average/(float)N;
其中 rx 和 ry 分别是 x 和 y 坐标。
我通过使用随机数生成器的内核生成点。对于内核,我每个块使用 128(256) 个线程来获得 4k(8k) 个点。在它上面每个线程执行inner above inner loop,然后将结果传递给reduce sum函数,如下
生成点:
__global__ void InitRNG ( curandState * state, const int seed ){
int tIdx = blockIdx.x*blockDim.x + threadIdx.x;
curand_init (seed, tIdx, 0, &state[tIdx]);
}
__global__
void SortPoints(float* X, float* Y,const int N, curandState *state){
float rdmn1, rdmn2;
unsigned int tIdx = blockIdx.x*blockDim.x + threadIdx.x;
float range;
if(tIdx < N){
rdmn1 = curand_uniform(&state[tIdx]);
rdmn2 = curand_uniform(&state[tIdx]);
range = sqrtf(0.25e0f*N*rdmn1);
X[tIdx] = range*cosf(2.0e0f*pi*rdmn2);
Y[tIdx] = range*sinf(2.0e0f*pi*rdmn2);
}
}
减少:
__device__
float ReduceSum2(float In){
__shared__ float data[BlockSize];
unsigned int tIdx = threadIdx.x;
data[tIdx] = In;
__syncthreads();
for(unsigned int i = blockDim.x/2; i > 0; i >>= 1){
if(tIdx < i){
data[tIdx] += data[tIdx + i];
}
__syncthreads();
}
return data[0];
}
核心:
__global__
void AvgDistance(float *X, float *Y, float *Avg, const int N){
int tIdx = blockIdx.x*blockDim.x + threadIdx.x;
int bIdx = blockIdx.x;
float x , y;
float d = 0.0f;
if(tIdx < N){
for(int i = tIdx + 1; i < N ; i++){
x = X[tIdx] - X[i];
y = Y[tIdx] - Y[i];
d += 1.0e0f/(sqrtf(x*x + y*y));
}
__syncthreads();
Avg[bIdx] = ReduceSum2(d);
}
}
内核配置和启动如下:
dim3 threads(BlockSize,BlockSize);
dim3 blocks(ceil(Np/threads.x),ceil(Np/threads.y));
InitRNG<<<blocks.x,threads.x>>>(d_state,seed);
SortPoints<<<blocks.x,threads.x>>>(d_rx,d_ry,Np,d_state);
AvgDistance<<<blocks.x,threads.x,threads.x*sizeof(float)>>>(d_rx,d_ry,d_Avg,Np);
最后,我将数据复制回主机,然后执行剩余的总和:
Avg = new float[blocks.x];
CHECK(cudaMemcpy(Avg,d_Avg,blocks.x*sizeof(float),cudaMemcpyDeviceToHost),ERROR_CPY_DEVTOH);
float average = 0;
for(int i = 0; i < blocks.x; i++){
average += Avg[i];
}
average = average/(float)Np;
4k点,ok!结果是:
Average distance between points (via Kernel) = 108.615
Average distance between points (via CPU) = 110.191
在这种情况下,总和可能会以不同的顺序执行,导致两个结果相互分歧,我不知道......
但是当谈到 8k 时,结果却完全不同:
Average distance between points (via Kernel) = 153.63
Average distance between points (via CPU) = 131.471
对我来说,内核和串行代码似乎都是以相同的方式编写的。是什么让我不相信 CUDA 计算浮点数的精度。这有意义吗?或者当某些线程同时从 X 和 Y 加载相同的数据时,对全局内存的访问是否会导致一些冲突?或者我编写内核的方式在某种程度上是“错误的”(我的意思是,我是否正在做一些导致两个结果相互分歧的事情?)。