与CPU计算相比,cuda中的sincos计算不准确

时间:2016-10-11 10:17:55

标签: cuda

基本上我只需要计算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(即herehere)的实现,而不是内核调用。

所以我有两个问题:

  1. 为什么准确性取决于threadsPerBlock,即使计算应始终相同?

  2. 即使是threadsPerBlock的大值(如threadsPerBlock=256),有没有办法提高准确度?

0 个答案:

没有答案