原子倍增和除法?

时间:2017-04-11 19:38:12

标签: cuda

有atomicAdd和atomicSub,但似乎atomicMul和atomicDiv不存在!可能吗?我需要实现以下代码:

 atomicMul(&accumulation[index],value)

我该怎么办?

2 个答案:

答案 0 :(得分:0)

好的,我解决了。但是我无法理解atomicMul是如何工作的,我不知道如何为浮点数写它。

#include <stdio.h>
#include <cuda_runtime.h>

__device__ double atomicMul(double* address, double val) 
{ 
  unsigned long long int* address_as_ull = (unsigned long long int*)address; 
  unsigned long long int old = *address_as_ull, assumed; 
  do { 
 assumed = old; 
 old = atomicCAS(address_as_ull, assumed, __double_as_longlong(val * __longlong_as_double(assumed))); 
 } while (assumed != old); return __longlong_as_double(old);
}      
__global__ void try_atomicMul(double* d_a, double* d_out)
{
     atomicMul(d_out,d_a[threadIdx.x]);
} 
int main()
{
  double h_a[]={5,6,7,8}, h_out=1;
  double *d_a, *d_out;

 cudaMalloc((void **)&d_a, 4 * sizeof(double));
 cudaMalloc((void **)&d_out,sizeof(double));

 cudaMemcpy(d_a, h_a, 4 * sizeof(double),cudaMemcpyHostToDevice);
 cudaMemcpy(d_out, &h_out, sizeof(double),cudaMemcpyHostToDevice);

 dim3 blockDim(4);
 dim3 gridDim(1);

  try_atomicMul<<<gridDim, blockDim>>>(d_a,d_out);
 cudaMemcpy(&h_out, d_out, sizeof(double), cudaMemcpyDeviceToHost);

 printf("%f \n",h_out);
 cudaFree(d_a);
 return 0;
}

答案 1 :(得分:0)

根据我对atomicCAS的理解,我会补充horus的答案。我的回答可能是错误的,因为我没有查看atomicCAS函数,只是阅读了有关它的文档(atomicCASAtomic Functions)。随意解决我的问题。

atomicMul如何运作

根据我的理解,atomicCAS(int* address, int compare, int val)的行为如下。

  1. *address复制到old(即old = *address
  2. (old == compare ? val : old)存储到*address。 (此时,old*address的值可能会有所不同,具体取决于条件是否匹配。)
  3. 返回old
  4. 当我们一起查看atomicMul函数的定义时,了解它的行为会变得更好。

    unsigned long long int* address_as_ull = (unsigned long long int*)address; 
    unsigned long long int oldValue = *address_as_ull, assumed; // Modified the name 'old' to 'oldValue' because it can be confused with 'old' inside the atomicCAS. 
    do { 
      assumed = oldValue; 
      // other threads can access and modify value of *address_as_ull between upper and lower line. 
      oldValue = atomicCAS(address_as_ull, assumed, __double_as_longlong(val * 
                           __longlong_as_double(assumed))); 
    } while (assumed != oldValue); return __longlong_as_double(oldValue);
    

    我们要做的是从address读取值(其值为eqaul到address_as_ull),然后将一些值乘以它然后再写回来。问题是其他线程可以在读取,修改和写入之间访问和修改*address的值。

    为了确保没有其他线程的拦截,我们检查*address的值是否等于我们assumed的值。假设*addressassumed=oldValue之后其他线程修改了oldValue = atomicCAS(...)的值。修改后的*address值将被复制到old内的atomicCAS变量(请参阅上面atomicCAS的行为1.)。  由于atomicCAS根据*address更新*address = (old == compare ? val : old)*address将不会更改(old==*address)。

    然后atomicCAS返回old并进入oldValue,以便循环可以继续,我们可以在下一次迭代尝试另一次拍摄。如果在读取和写入之间未修改*address,则会将val写入*address,并且循环将结束。

    如何为浮动写

    简短回答:

    __device__ float atomicMul(float* address, float val) 
    { 
      int* address_as_int = (int*)address; 
      int old = *address_as_int, assumed; 
      do { 
        assumed = old; 
        old = atomicCAS(address_as_int, assumed, __float_as_int(val * 
    __float_as_int(assumed))); 
     } while (assumed != old); return __int_as_float(old);
    }
    

    我没有测试它,所以可能会有一些错误。如果我错了,请修理我。

    它是如何运作的: 出于某种原因,atomicCAS仅支持整数类型。所以我们应该手动将float / double类型变量转换为整数类型以输入到函数,然后将整数结果重新转换为float / double类型。我上面修改的是doublefloatunsigned long longint,因为float的大小与int匹配。