Cuda 如何在多个内存位置原子地设置值?
CUDA编程指南指出,任何原子操作都可以使用Cuda 如何在多个内存位置原子地设置值?,cuda,atomic,Cuda,Atomic,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
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);
}
现在,我面临的问题是:
我想写一个函数,可以操作两个变量的地址原子
例如:
关于两个变量的原子加法
输入
结果
*address_1 = *address_1+val_1;
*address_2 = *address_2+val_2;
我该如何处理这个问题?谢谢。我认为您没有抓住此处实施的操作要点。在
a+=b
中,逻辑操作是a=a+b
,但使用CAS可以避免在读写之间对a
进行虚假更改<代码>b只使用一次,没有问题
在
a=b+c
中,没有一个值出现两次,因此不需要在这两个值之间进行任何更改。谢谢所有人回复我!
我现在有了解决办法。
我们可以把这两个变量组合成一个结构。因此,我们可以将“两个变量两个地址”转换为“一个结构一个地址”。代码如下:
#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;
}
现在一切都会好起来~一般来说,你不能这样做。硬件不支持对内存中的多个位置进行原子更改。如果两个变量都足够小,足以适应单个原子操作的大小,则可以避免这种情况,但如果总字节数超过8个,则这种方法将失败。你会遇到这个问题 您可以做的一件事是使用某种同步协议来访问这两个值。例如,您可以使用只有一个线程可以获得的互斥来安全地知道,在该线程处理值时,没有其他线程正在更改这些值。请参阅: 当然,这在GPU设置中是相当昂贵的。您可能最好执行以下操作之一(通过增加偏好顺序):
- 在较大的数组中使用指针或索引,而不是原子地更改结构,而是原子地切换指针。这解决了并发性问题,但会降低访问速度
- 更改您的算法,以便可以分离访问,而不必以原子方式进行访问
- 进一步更改算法,以避免多个线程写入单个复杂数据结构
#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