我有一堆由各种线程更新的 float 。数组的大小远大于线程数。因此,同时访问特定的 float 是相当罕见的。我需要 C++03 的解决方案。
以下代码以原子方式将一个值添加到其中一个 float ( live demo )。假设它有效,它可能是最好的解决方案。 我能想到的唯一选择是将数组分成几束,并用互斥锁保护每一束。但我不希望后者更有效率。
我的问题如下。有没有其他解决方案可以原子地添加 float ?任何人都可以预测哪个是最有效的吗?是的,我愿意做一些基准测试。也许可以通过放宽内存约束来改进下面的解决方案,即将 __ATOMIC_SEQ_CST
换成其他东西。我没有这方面的经验。
void atomic_add_float( float *x, float add )
{
int *ip_x= reinterpret_cast<int*>( x ); //1
int expected= __atomic_load_n( ip_x, __ATOMIC_SEQ_CST ); //2
int desired;
do {
float sum= *reinterpret_cast<float*>( &expected ) + add; //3
desired= *reinterpret_cast<int*>( &sum );
} while( ! __atomic_compare_exchange_n( ip_x, &expected, desired, //4
/* weak = */ true,
__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) );
}
其工作原理如下。在 //1
处,x
的位模式被解释为 int
,即我假设 float
和int
具有相同的大小(32 位)。在 //2
处,要增加的值以原子方式加载。在 //3
处,int
的位模式被解释为 float
并添加被加数。 (请记住,expected
包含在 ip_x == x
处找到的值。)这不会更改 ip_x == x
下的值。在 //4
如果没有其他线程更改该值,则求和结果仅存储在 ip_x == x
中,即如果 expected == *ip_x
(docu)。如果不是这种情况,则 do-loop 继续并且 expected
包含找到的更新值 ad ip_x == x
。
GCC 的原子访问函数(__atomic_load_n
和 __atomic_compare_exchange_n
)可以很容易地被其他编译器的实现交换。
最佳答案
Are there any alternative solutions for adding floats atomically? Can anyone anticipate which is the most efficient?
当然,至少有几个想到:
使用同步原语,即自旋锁。会比 compare-exchange 慢一点。
交易扩展 ( see Wikipedia )。会更快,但此解决方案可能会限制可移植性。
总的来说,您的解决方案非常合理:速度很快,而且可以在任何平台上运行。
在我看来,所需的内存顺序是:
__ATOMIC_ACQUIRE
-- 当我们读取__atomic_load_n()
中的值时
__ATOMIC_RELEASE
-- 当__atomic_compare_exchange_n()
成功时__ATOMIC_ACQUIRE
-- 当__atomic_compare_exchange_n()
失败时
关于c++ - 有没有更有效的方法来原子地添加两个 float ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48746540/