【发布时间】:2018-02-12 12:29:34
【问题描述】:
我有一束由各种线程更新的浮点数。数组的大小远大于线程数。因此同时访问特定的浮点数是相当罕见的。我需要 C++03 的解决方案。
以下代码以原子方式将一个值添加到其中一个浮点数 (live demo)。假设它有效,它可能是最好的解决方案。 我能想到的唯一选择是将数组分成几束并用互斥锁保护每一束。但我不希望后者更有效率。
我的问题如下。是否有任何替代解决方案可以原子地添加浮点数?谁能预测哪个是最有效的?是的,我愿意做一些基准测试。也许可以通过放宽内存限制来改进下面的解决方案,即用其他东西交换__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 ) );
}
它的工作原理如下。在//1x 的位模式被解释为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 包含在 ip_x == x 广告中找到的更新值。
GCC 的原子访问函数(__atomic_load_n 和 __atomic_compare_exchange_n)可以很容易地被其他编译器的实现所交换。
【问题讨论】:
-
@user463035818 要将其更改为 C 代码,只需通过经典 C 转换交换
reinterpret_cast。 -
@ClaasBontus 这可能适用于这个问题,不幸的是 c++ 答案并不总是很容易转换为 c 答案(反之亦然)。请不要标记无关的标签
-
假设代码可以正常工作,这看起来值得Code Review 批评。请务必先阅读A guide to Code Review for Stack Overflow users,因为那里有些事情的处理方式不同!
-
在 C++20 中,您可能会得到
std::atomic<float>,但现在不可用。 P0020R6 Floating Point Atomic
标签: c++ multithreading atomic c++03