基本上我只需要计算N1 * N2向量的exp函数的N2和,为简单起见,我将点积总是设置为1.使用CPU计算,这只是:
static void calculate_exp( float *realpart, float *imaginarypart)
{
float qdotx;
for (p = 0; p < N1; p++)
{
for (k = 0; k < N2; k++)
{
{
qdotx= 1.; //normally the dotproduct of input vectors
}
float sinvalue, cosvalue;
sincosf (qdotx,&sinvalue,&cosvalue);
realpart[k] += cosvalue;
imaginarypart[k]+= sinvalue;
}
}
}
这种易于并行化结构的原因这是在CUDA中实现并在GPU上运行它的完美示例。我这样做并编写了一个工作得很好的程序,它比CPU计算快得多。但是,由于每个块使用的线程越多,得到的总和与原始结果的差异越大。我的CUDA代码是:
__global__ void calculate_exp(float* output_sin, float* output_cos, float *qdotx){
int index;
__shared__ float cache_sin[threadsPerBlock];
__shared__ float cache_cos[threadsPerBlock];
for( int ql = 0; ql < N2; ql++)
{
int cacheIndex = threadIdx.x;
float tmp_sin=0. ,tmp_cos=0.;
cache_cos[cacheIndex] =0.; cache_sin[cacheIndex] =0.;
for(index= threadIdx.x + blockDim.x*blockIdx.x; index < N1; index += blockDim.x * gridDim.x)
{
qdotx[index] = 1.;
float sinvalue, cosvalue;
sincosf (qdotx[index],&sinvalue,&cosvalue);
tmp_cos += cosvalue;
tmp_sin += sinvalue;
}
cache_cos[cacheIndex] = tmp_cos;
cache_sin[cacheIndex] = tmp_sin;
__syncthreads();
int i = blockDim.x / 2;
while (i != 0) {
if (cacheIndex < i) {
cache_cos[cacheIndex] += cache_cos[cacheIndex +i];
cache_sin[cacheIndex] += cache_sin[cacheIndex +i];
}
__syncthreads();
i/=2;
}
__syncthreads();
if(cacheIndex == 0 ){
atomicAdd(&output_cos[ql] , cache_cos [0]);
atomicAdd(&output_sin[ql] , cache_sin [0]);
}
__syncthreads();
}
}
我的内核调用是
int N_p = int(N1/threadsPerBlock) +1;
calculate_exp <<<N_p,threadsPerBlock>>>(dev_output_sin,dev_output_cos,dev_qdotx);
很明显,GPU上的sincos
计算可能与标准数学GNU-libery略有不同。
我已经发现,对于非常大的qdotx
(即qdotx=1000.
),不准确性正在增加,这并不奇怪,但我不明白的是,为什么不准确取决于threadsPerBlock?
示例
选择N1=50000, N2=1000
并改变threadsPerBlock=xxx
的输入。
realpart
值的总和始终为27015.113831(所以50000*cos(1)
)
GPU的output_cos
结果与CPU的realpart
值之间的差异如下:
ThreadsPerBlock sum on GPU difference
1 27026.736328 11.623047
16 27014.515625 0.597656
32 27014.869141 0.244141
64 27015.251953 0.138672
>64 differs with each ql, differences from 9 to 32
我尝试了双精度而不是单浮点精度,但结果保持不变。
我正在寻找这个问题,但我找不到答案。我发现的只是处理sincos
(即here和here)的实现,而不是内核调用。
所以我有两个问题:
为什么准确性取决于threadsPerBlock
,即使计算应始终相同?
即使是threadsPerBlock
的大值(如threadsPerBlock=256
),有没有办法提高准确度?