发布获取语义以计算平均值的上下限 [英] Release Acquire Semantics to Compute Lower and Upper Bound of Average

查看:189
本文介绍了发布获取语义以计算平均值的上下限的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基于一个上一个问题,我想知道以下代码是否可以计算上下限?使用原子测量的属性的平均值:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆