gpt4 book ai didi

也许是 CUDA C/C++ : Calculate the average of inverse of distance per point (interaction energy,?)

转载 作者:行者123 更新时间:2023-12-01 05:22:50 26 4
gpt4 key购买 nike

我一直在尝试编写一个内核,用于计算 N 上 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 坐标。

我通过使用随机数生成器的内核生成点。对于内核,我为每个 block 使用 128(256) 个线程以获得 4k(8k) 个点。每个线程在上面执行inner above inner循环,然后将结果传递给一个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 计算 float 的精度。这有意义吗?或者当某些线程同时从 X 和 Y 加载相同数据时,对全局内存的访问是否会导致一些冲突?或者我编写内核的方式在某种程度上是“错误的”(我的意思是,我是否在做一些导致两个结果彼此不同的事情?)。

最佳答案

实际上,据我所知,问题似乎出在 CPU 方面。我根据您的代码创建了示例代码。

我能够重现您的结果。

首先,我将 sinfcosfsqrtf 的所有实例都切换为相应的 double 版本。这对结果没有影响。

接下来,我包含了一个 typedef,这样我就可以轻松地将精度从 float 切换到 double 并返回,替换 float 中的每个相关实例mytype 的代码是我的 typedef。

当我使用 float 的 typedef 和 4096 的数据大小运行代码时,我得到了这些结果:

GPU average = 108.294922
CPU average = 109.925285

当我使用 double 的类型定义和 4096 的数据大小运行代码时,我得到了这些结果:

GPU average = 108.294903
CPU average = 108.294903

当我使用 float 的类型定义和 8192 的数据大小运行代码时,我得到了这些结果:

GPU average = 153.447327
CPU average = 131.473526

当我使用 double 的类型定义和 8192 的数据大小运行代码时,我得到了这些结果:

GPU average = 153.447380
CPU average = 153.447380

至少有 2 个观察结果:

  1. GPU 结果在 float 和 double 之间没有变化,除了小数点后第 5 位
  2. The CPU results vary by 1-20% or so between float and double, but when double is selected, they line up exactly (to the 6th decimal place, anyway) with the GPU results.

基于此,我相信 CPU 正在提供可变的、有问题的行为。

这是我的引用代码:

#include <stdio.h>
#include <curand.h>
#include <curand_kernel.h>
#define DSIZE 8192
#define BlockSize 32
#define pi 3.14159f


#define cudaCheckErrors(msg) \
do { \
cudaError_t __err = cudaGetLastError(); \
if (__err != cudaSuccess) { \
fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
msg, cudaGetErrorString(__err), \
__FILE__, __LINE__); \
fprintf(stderr, "*** FAILED - ABORTING\n"); \
exit(1); \
} \
} while (0)

typedef double mytype;

__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(mytype* X, mytype* Y,const int N, curandState *state){

mytype rdmn1, rdmn2;

unsigned int tIdx = blockIdx.x*blockDim.x + threadIdx.x;
mytype range;
if(tIdx < N){
rdmn1 = curand_uniform(&state[tIdx]);
rdmn2 = curand_uniform(&state[tIdx]);
range = sqrt(0.25e0f*N*rdmn1);
X[tIdx] = range*cos(2.0e0f*pi*rdmn2);
Y[tIdx] = range*sin(2.0e0f*pi*rdmn2);
}
}

__device__
mytype ReduceSum2(mytype In){

__shared__ mytype 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(mytype *X, mytype *Y, mytype *Avg, const int N){

int tIdx = blockIdx.x*blockDim.x + threadIdx.x;
int bIdx = blockIdx.x;

mytype x , y;
mytype 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/(sqrt(x*x + y*y));
}
__syncthreads();
Avg[bIdx] = ReduceSum2(d);

}
}

mytype cpu_avg(const mytype *rx, const mytype *ry, const int size){
mytype average = 0.0f;
for(int i = 0; i < size; i++){
for(int j = i + 1; j < size; j++){
average += 1.0e0f/sqrt((rx[i]-rx[j])*(rx[i]-rx[j]) + (ry[i]-ry[j])*(ry[i]-ry[j]));
}
}
average = average/(mytype)size;
return average;
}

int main() {

int Np = DSIZE;
mytype *rx, *ry, *d_rx, *d_ry, *d_Avg, *Avg;
curandState *d_state;
int seed = 1;

dim3 threads(BlockSize,BlockSize);
dim3 blocks((int)ceilf(Np/(float)threads.x),(int)ceilf(Np/(float)threads.y));
printf("number of blocks = %d\n", blocks.x);
printf("number of threads= %d\n", threads.x);
rx = (mytype *)malloc(DSIZE*sizeof(mytype));
if (rx == 0) {printf("malloc fail\n"); return 1;}
ry = (mytype *)malloc(DSIZE*sizeof(mytype));
if (ry == 0) {printf("malloc fail\n"); return 1;}

cudaMalloc((void**)&d_rx, DSIZE * sizeof(mytype));
cudaMalloc((void**)&d_ry, DSIZE * sizeof(mytype));
cudaMalloc((void**)&d_Avg, blocks.x * sizeof(mytype));
cudaMalloc((void**)&d_state, DSIZE * sizeof(curandState));
cudaCheckErrors("cudamalloc");



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(mytype)>>>(d_rx,d_ry,d_Avg,Np);
cudaCheckErrors("kernels");


Avg = new mytype[blocks.x];

cudaMemcpy(Avg,d_Avg,blocks.x*sizeof(mytype),cudaMemcpyDeviceToHost);
cudaMemcpy(rx, d_rx, DSIZE*sizeof(mytype),cudaMemcpyDeviceToHost);
cudaMemcpy(ry, d_ry, DSIZE*sizeof(mytype),cudaMemcpyDeviceToHost);
cudaCheckErrors("cudamemcpy");
mytype average = 0;

for(int i = 0; i < blocks.x; i++){
average += Avg[i];
}
average = average/(mytype)Np;

printf("GPU average = %f\n", average);
average = cpu_avg(rx, ry, DSIZE);
printf("CPU average = %f\n", average);

return 0;
}

我在 RHEL 5.5、CUDA 5.0、Intel Xeon X5560 上运行

编译:

nvcc -O3 -arch=sm_20 -lcurand -lm -o t93 t93.cu

编辑:在观察到 CPU 方面的可变性之后,我发现我可以通过像这样修改 CPU 平均代码来消除大部分 CPU 可变性:

mytype cpu_avg(const mytype *rx, const mytype *ry, const int size){
mytype average = 0.0f;
mytype temp = 0.0f;
for(int i = 0; i < size; i++){
for(int j = i + 1; j < size; j++){
temp += 1.0e0f/sqrt((rx[i]-rx[j])*(rx[i]-rx[j]) + (ry[i]-ry[j])*(ry[i]-ry[j]));
}
average += temp/(mytype)size;
temp = 0.0f;
}
return average;
}

所以我会说 CPU 端的中间结果存在问题。有趣的是它没有出现在 GPU 结果上。我怀疑这样做的原因是 GPU 平均值的最终求和是在 CPU 上完成的(因此每个单独的 GPU block 结果按大小缩小,例如 8192),并且这些可能具有足以生存直到最后的划分。如果您内联 CPU 平均计算,您可能会再次观察到一些不同的东西。

关于也许是 CUDA C/C++ : Calculate the average of inverse of distance per point (interaction energy,?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15371123/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com