比较和交换C ++ 0x

从C ++原子类型和操作的C ++ 0x建议:

29.1顺序和一致性[atomics.order]

添加一个新的子条款与以下段落。

枚举memory_order指定了[由N2334或其采用的后继者添加的新节]中定义的详细的常规(非原子)内存同步顺序,并且可以提供操作顺序。 其枚举值及其含义如下。

  • memory_order_relaxed
  • 该操作不会对内存进行排序。

  • memory_order_release
  • 对受影响的内存位置执行释放操作,从而通过它所应用的原子变量使其他线程可以看到正常的内存写入。

  • memory_order_acquire
  • 对受影响的内存位置执行获取操作,从而使通过应用它的原子变量释放的其他线程进行常规内存写入,这对当前线程是可见的。

  • memory_order_acq_rel
  • 该操作既获取又释放语义。

  • memory_order_seq_cst
  • 该操作既获取也释放语义,并且具有顺序一致的操作顺序。

    建议较低:

    bool A::compare_swap( C& expected, C desired,
            memory_order success, memory_order failure ) volatile
    

    在哪里可以指定CAS的内存顺序。


    我的理解是,“ memory_order_acq_rel ”只会同步操作所需的那些内存位置,而其他内存位置可能保持不同步(它不会像内存篱笆一样)。

    现在,我的问题是 - 如果我选择“ memory_order_acq_rel ”并将compare_swap应用于整数类型(例如整数),那么通常如何将其转换为现代消费类处理器(如多核英特尔i7)上的机器代码? 其他常用体系结构(x64,SPARC,ppc,arm)呢?

    特别是(假设一个具体的编译器,比如说gcc):

  • 如何使用上述操作比较和交换整数位置?
  • 这样的代码会产生什么指令序列?
  • i7上的操作是否可以锁定?
  • 这样的操作是否会运行完整的缓存一致性协议,将不同处理器内核的缓存同步,就好像它是i7上的内存围栏一样? 或者它只会同步这个操作所需的内存位置?
  • 与上一个问题相关 - 在i7上使用acq_rel语义是否有任何性能优势? 其他架构呢?
  • 感谢所有的答案。


    这里的答案不是微不足道的。 究竟发生了什么,什么是依赖于许多事情。 为了基本理解缓存的一致性/内存,或许我最近的博客条目可能会有所帮助:

  • CPU重新排序 - 实际上正在重新排序?
  • CPU内存 - 为什么我需要一个互斥锁?
  • 但是,除此之外,让我试着回答几个问题。 首先,下面的说明对于支持什么是非常有希望的。

    compare_swap( C& expected, C desired,
            memory_order success, memory_order failure )
    

    体系结构不会完全按照您的要求来实现。 当您指定memory_order时,您将指定重新排序的方式。 要使用英特尔的术语,您将指定您想要的栅栏类型,其中有三个栅栏,完整栅栏,装载栅栏和商店栅栏。 仅仅因为你想要在这个操作上设置一个特定的围栏,并不意味着它会被支持,我希望它总是会回落到一个完整的围栏。

    编译器可能会使用CMPXCHG指令来实现该调用。 如果你已经指定了放松之外的东西,它会用lock来标记以表明函数应该是原子的。 这是否“无锁”取决于您在“锁定”方面的想法。

    在内存同步方面,您需要了解缓存一致性如何工作(我的博客可能会有所帮助)。 新的CPU使用ccNUMA体系结构(以前是SMP)。 本质上,内存上的“视图”永远不会失去同步。 代码中使用的栅栏实际上并不强制任何冲刷发生。 如果两个内核在高速缓存行中都缓存了相同的内存位置,则其中一个将被标记为脏,另一个将根据需要重新加载。 对一个非常复杂的过程的简单解释

    要回答你最后的问题,你应该总是使用逻辑上需要正确的内存语义。 大多数体系结构不支持您在程序中使用的所有组合。 但是,在很多情况下,您会得到很好的优化,特别是在您请求的订单没有栅栏的情况下(这很常见)。

    - 一些评论的答案:

    您必须区分执行写入指令和写入内存位置的含义。 这是我试图在我的博客文章中解释的。 当“0”被提交到0x100时,所有核心都看到零。 写入整数也是原子的,即使没有锁定,当你写入一个位置时,如果他们希望使用它,所有的核心都会立即拥有该值。

    麻烦的是,要使用您可能首先将其加载到寄存器中的值,在此之后对该位置的任何更改显然不会触及寄存器。 这就是为什么尽管存在缓存一致性需要互斥体的原因。

    至于矛盾的说法,通常你会看到各种各样的说法。 它们是否相互矛盾,直接来源于上下文中“看”,“加载”,“执行”的含义。 如果将“1”写入0x100,这是否意味着您执行了写入指令或者CPU是否实际提交了该值。 差异来自重新排序。 CPU可以延迟写入“1”,但是您可以确定它最终确认所有内核都看到它为“1”。 栅栏控制着这个顺序。

    链接地址: http://www.djcxy.com/p/30715.html

    上一篇: Compare and swap C++0x

    下一篇: C++0x memory model and speculative loads/stores