CUDA编程指南说,任何原子操作都可以使用atomicCAS()
来实现,并举例说明了原子双重加法:
__device__ float single(double *address,double val)
{
unsigned long long int *address_as_ull =(unsigned long long int*)address;
unsigned long long int assumed;
unsigned long long int old = *address_as_ull;
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);
}
现在,我面临的问题是:
我想编写一个可以原子操作两个变量的函数。
例如: 原子加两个变量
输入
double *address_1, int *address_2
double val_1,int val_2
结果
*address_1 = *address_1+val_1;
*address_2 = *address_2+val_2;
我该如何解决这个问题?谢谢。
答案 0 :(得分:1)
我认为您错过了此处实施的操作要点。在a+=b
中,逻辑运算为a = a + b
,但是使用CAS可以避免在a
的读取和写入之间对虚假的更改。 b
使用一次,没有问题。
在a = b + c
中,两个值均不会出现两次,因此无需保护两者之间的任何更改。
答案 1 :(得分:1)
通常,您不能执行此操作。硬件不不支持对内存中多个位置的原子更改。如果两个变量都足够小以适合单个原子操作的大小,则可以避免这种情况;但是,如果总体上超过8个字节,则此方法将失败。您将遇到"too much milk"问题。
您可以做的一件事就是使用某种同步协议来访问这两个值。例如,您可以使用只有一个线程可以获取的互斥锁,以安全地知道在该线程处理这些值时没有其他人在更改这些值。参见:Avoid taking a long time to finish the 'too much milk' scenario。
当然,这在GPU设置中非常昂贵。您最好执行以下操作之一(通过增加优先级):
答案 2 :(得分:0)
感谢所有人回覆我! 我现在有解决方案。 我们可以将两个变量组合成一个结构。因此我们可以将“具有两个地址的两个变量”转换为“具有一个地址的一个结构”。这是代码:
#include <stdio.h>
struct pair_t
{
float x;
int y;
};
__device__ float single(double *address,double val)
{
unsigned long long int *address_as_ull =(unsigned long long int*)address;
unsigned long long int assumed;
unsigned long long int old = *address_as_ull;
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);
}
__device__ void myadd(pair_t *address, double val_1 ,int val_2)
{
union myunion
{
pair_t p;
unsigned long long int ull;
};
unsigned long long int *address_as_ull;
address_as_ull = (unsigned long long int *)address;
union myunion assumed;
union myunion old_value;
union myunion new_value;
old_value.p = *(pair_t *)address_as_ull;
do
{
assumed = old_value;
// cirtical area begin--------------------
new_value.p.x = assumed.p.x+val_1;
new_value.p.y = assumed.p.y+val_2;
// cirtical area end----------------------
old_value.ull = atomicCAS(address_as_ull,assumed.ull,new_value.ull);
}while(assumed.ull !=old_value.ull);
}
__global__ void kernel (pair_t *p)
{
myadd(p,1.5,2);
}
int main()
{
pair_t p;
p.x=0;
p.y=0;
pair_t *d_p = NULL;
cudaMalloc((pair_t **)&d_p, sizeof(pair_t));
cudaMemcpy(d_p, &p, sizeof(pair_t), cudaMemcpyHostToDevice);
kernel<<<100, 100>>>(d_p);
cudaMemcpy(&p, d_p, sizeof(pair_t), cudaMemcpyDeviceToHost);
cudaDeviceSynchronize();
printf("x=%lf\n", p.x);
printf("y=%d\n", p.y);
cudaDeviceReset();
return 0;
}
解决方案是
x=15000.000000
y=20000
现在一切都会好的〜