我有一个浮动包,可以通过各种线程进行更新。数组的大小远大于线程数。因此,对特定浮标的同时访问是相当罕见的。我需要一个C ++ 03的解决方案。
以下代码以原子方式向其中一个浮点数(live demo)添加值。假设它有效,它可能是最好的解决方案。 我能想到的唯一选择是将数组分成多个串并用互斥锁保护每个串。但我不希望后者更有效率。
我的问题如下。是否存在原子添加浮子的替代解决方案?任何人都可以预见哪种效率最高?是的,我愿意做一些基准测试。也许下面的解决方案可以通过放松memorder约束来改进,即通过其他东西交换__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
(docu),则总和的结果仅存储在expected == *ip_x
。如果不是这种情况,则执行循环继续,expected
包含找到的广告ip_x == x
的更新值。
GCC的原子访问函数(__atomic_load_n
和__atomic_compare_exchange_n
)可以很容易地被其他编译器的实现交换。
答案 0 :(得分:1)
为了提高此功能的效率,您可以使用__ATOMIC_ACQUIRE
__atomic_load_n
__ATOMIC_RELEASE
和__ATOMIC_RELAXED
__atomic_compare_exchange_n
success_memorder
和{分别为{1}}。
在x86-64上虽然不会改变生成的程序集,因为它的内存模型相对较强。与内存较弱的ARM模式不同。
答案 1 :(得分:1)
是否存在原子添加浮子的替代解决方案?谁能预测哪种效率最高?
当然,至少有几个想到的东西:
使用同步原语,即自旋锁。会比比较交换慢一点。
交易扩展(see Wikipedia)。会更快,但这个解决方案可能会限制可移植性。
总的来说,您的解决方案非常合理:它很快,但可以在任何平台上运行。
在我看来,所需的记忆顺序是:
__ATOMIC_ACQUIRE
- 当我们阅读__atomic_load_n()
__ATOMIC_RELEASE
- __atomic_compare_exchange_n()
成功时__ATOMIC_ACQUIRE
- __atomic_compare_exchange_n()
失败时