我正在用C ++ AMP重写一个算法,然后遇到了原子写入的问题,更具体地说是atomic_fetch_add,这显然只适用于整数?
我需要以原子方式添加double_4(或者我必须使用float_4)。如何使用C ++ AMP的原子实现这一目标?
最好/唯一的解决方案是否真的有一个锁定变量,我的代码可以使用它来控制写入?我实际上需要为一长串输出双精度进行原子写操作,因此我基本上需要为每个输出都锁定。
我已经考虑过这个以获得更好的性能,但是现在我只是在第一次迭代中。
编辑: 感谢您已经给出的快速答案。 我虽然快速更新了我的问题。
我进行了以下锁定尝试,但似乎当warp中的一个线程通过锁定时,同一warp中的所有其他线程只是标记着。我期待第一个经线获得锁定,但我必须遗漏一些东西(请注意,自从我的cuda时代已经过去了几年,所以我刚刚变得愚蠢)
parallel_for_each(attracting.extent, [=](index<1> idx) restrict(amp)
{
.....
for (int j = 0; j < attracted.extent.size(); j++)
{
...
int lock = 0; //the expected lock value
while (!atomic_compare_exchange(&locks[j], &lock, 1));
//when one warp thread gets the lock, ALL threads continue on
...
acceleration[j] += ...; //locked write
locks[j] = 0; //leaving the lock again
}
});
这不是一个大问题,因为我应该首先写入一个共享变量,并且只在一个tile中的所有线程完成后才将它写入全局内存,但我只是不明白这种行为。
答案 0 :(得分:2)
所有原子添加操作仅适用于整数类型。对于float_4(我假设这是4个浮点数),您可以使用128位CAS(比较和交换)操作执行您想要的操作而不需要锁定,但是对于double_4,您不需要256位CAS操作。你要做的是有一个循环,从内存中原子地读取float_4,以常规方式执行float add,然后使用CAS测试&amp;如果它是原始值,则交换该值(如果不是则循环,即某些其他线程改变了read和amp; write之间的值)。请注意,128位CAS仅适用于64位体系结构,并且您的数据需要正确对齐。
答案 1 :(得分:1)
如果关键代码很短,您可以使用原子操作创建自己的锁:
int lock = 1;
while(__sync_lock_test_and_set(&lock, 0) == 0) // trying to acquire lock
{
//yield the thread or go to sleep
}
//critical section, do the work
// release lock
lock = 1;
优点是可以节省操作系统锁的开销。
答案 2 :(得分:0)
这个问题已被其他人回答,答案是你需要自己处理双原子。库中没有它的功能。
我还想详细说明我自己的编辑,以防其他人来到这里同样失败的锁定。
在下面的示例中,我的错误是没有意识到当交换失败时,它实际上改变了预期值!因此,第一个线程会期望锁定为零并在其中写入1。下一个线程会期望0并且将无法写入一个 - 但是然后交换机在变量中写入一个持有期望值的一个。这意味着下次线程尝试进行交换时,它需要锁定1!它得到它然后它认为它得到了锁。
我完全不知道&amp; lock在失败的交换比赛中会收到1!
parallel_for_each(attracting.extent, [=](index<1> idx) restrict(amp)
{
.....
for (int j = 0; j < attracted.extent.size(); j++)
{
...
int lock = 0; //the expected lock value
**//note that, if locks[j]!=lock then lock=1
//meaning that ACE will be true the next time if locks[j]==1
//meaning the while will terminate even though someone else has the lock**
while (!atomic_compare_exchange(&locks[j], &lock, 1));
//when one warp thread gets the lock, ALL threads continue on
...
acceleration[j] += ...; //locked write
locks[j] = 0; //leaving the lock again
}
});
似乎需要修复
parallel_for_each(attracting.extent, [=](index<1> idx) restrict(amp)
{
.....
for (int j = 0; j < attracted.extent.size(); j++)
{
...
int lock = 0; //the expected lock value
while (!atomic_compare_exchange(&locks[j], &lock, 1))
{
lock=0; //reset the expected value
};
//when one warp thread gets the lock, ALL threads continue on
...
acceleration[j] += ...; //locked write
locks[j] = 0; //leaving the lock again
}
});