发布获取语义以计算平均值的上下限 [英] Release Acquire Semantics to Compute Lower and Upper Bound of Average
问题描述
基于一个上一个问题,我想知道以下代码是否可以计算上下限?使用原子测量的属性的平均值:
Based on a previous question, I was wondering if the following code would work to compute lower and upper bounds to the average value of a property being measured using atomics:
std::atomic< unsigned int > m_accLower;
std::atomic< unsigned int > m_countLower;
std::atomic< unsigned int > m_accUpper;
std::atomic< unsigned int > m_countUpper;
// ...
void Class::UpdateLower( unsigned int delta )
{
m_countLower.fetch_add( 1 , std::memory_order_relaxed );
m_accLower.fetch_add( delta , std::memory_order_release );
}
double Class::GetAverageLower( )
{
auto acc = m_accLower.load( std::memory_order_acquire );
auto count = m_countLower.load( std::memory_order_relaxed );
return acc/(double)count;
}
void Class::UpdateUpper( unsigned int delta )
{
m_accUpper.fetch_add( delta , std::memory_order_relaxed );
m_countUpper.fetch_add( 1 , std::memory_order_release );
}
double Class::GetAverageUpper( )
{
auto count = m_countUpper.load( std::memory_order_acquire );
auto acc = m_accUpper.load( std::memory_order_relaxed );
return acc/(double)count;
}
假设较低和较高的更新始终一起发布,并且没有并发更新.
Suppose the lower and upper updates are always issued together and there's no concurrent updates.
由于最后一个字段的发布要求,功能GetAverageLower()
可以确保在m_accLower
中进行更新之前立即查看发布的任何m_countLower
更新,但也许还有更多.双重情况发生在GetAverageUpper()
.
The function GetAverageLower()
is guaranteed to see any m_countLower
updates issued right before the update it's seing in m_accLower
due to the release-acquire on this last field, but maybe some more. The dual case occurs in GetAverageUpper()
.
这种思路正确吗?是否可以保证较低的版本实际上总是得到下限,而较高的版本实际上是平均值的上限?
Is this line of thought right? Is it guaranteed that the lower version actually gets always a lower bound and the upper version, an upper bound of the average?
如果这些方法确实达到了我的预期,那么实际平均值将处于封闭区间:[GetAverageLower() , GetAverageUpper()]
If those methods are indeed doing what I expect, then the actual average would be in the closed interval: [GetAverageLower() , GetAverageUpper()]
推荐答案
在我回答这个问题之前,我觉得有必要陈述一些东西:
仅通过使用(松弛的)原子,就可以确保一个线程将看到其他线程中发生的原子更改.内存重新排序与可见性无关.这是关于防止编译器和CPU加扰代码行.
Before I'll answer this question, I feel the need to state something:
Just by using (relaxed) atomics you are guaranteed that one thread will see a change in atomic that occurred in other threads. memory reordering is not about visibility. it's about preventing the compiler and CPU from scrambling lines of code.
现在我们已经确定,如果您在同一线程中调用GetAverageUpper
然后到UpdateUpper
,则会出现问题.内联后,合并的代码将如下所示:
Now that we established that, there is an issue if you call GetAverageUpper
and then to UpdateUpper
in the same thread. after inlining, the merged code will look like that:
auto count = m_countUpper.load( std::memory_order_acquire );
auto acc = m_accUpper.load( std::memory_order_relaxed );
auto inlinedValue = acc/(double)count;
m_accUpper.fetch_add( delta , std::memory_order_relaxed );
m_countUpper.fetch_add( 1 , std::memory_order_release );
现在,编译器/CPU无法重新排序acquire
之前的任何行以及release
之后的任何代码行,但是中间的两个relaxed
呢?它们可以重新排序:
Now, the Compiler/CPU cannot reorder any lines which comes before acquire
and any lines of code which comes after release
, but what about the two relaxed
in the middle? they can be reordered:
auto count = m_countUpper.load( std::memory_order_acquire );
m_accUpper.fetch_add( delta , std::memory_order_relaxed );
auto acc = m_accUpper.load( std::memory_order_relaxed );
auto inlinedValue = acc/(double)count;
m_countUpper.fetch_add( 1 , std::memory_order_release );
这当然会破坏您的代码逻辑.
Which of course breaks your code-logic.
这篇关于发布获取语义以计算平均值的上下限的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!