代码之家  ›  专栏  ›  技术社区  ›  axel22

C++0x的比较与交换

  •  16
  • axel22  · 技术社区  · 14 年前

    来自 C++0x proposal C++与Atomic Types运算

    29.1顺序和一致性[原子顺序]

    新增一个子条款,包括以下段落。

    枚举 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
    

    其中可以指定CA的内存顺序。


    我的理解是 记忆顺序 _157;只需要同步操作所需的内存位置,而其他内存位置可能保持不同步(它不会表现为内存围栏)。

    现在,我的问题是-如果我选择__ 记忆顺序 并应用 compare_swap 例如,对于整数类型,这通常如何在现代消费处理器(如多核Inteli7)上转换为机器代码?其他常用的架构(X64、SPARC、PPC、ARM)呢?

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

    1. 如何将整数位置与上述操作进行比较和交换?
    2. 这样的代码将产生什么指令序列?
    3. I7上的操作锁是否空闲?
    4. 这样的操作是否会运行一个完整的缓存一致性协议,将不同处理器核心的缓存进行同步,就好像它是i7上的内存围栏一样?还是只同步此操作所需的内存位置?
    5. 与前面的问题相关-使用 acq_rel I7上的语义?其他架构呢?

    谢谢你的回答。

    1 回复  |  直到 14 年前
        1
  •  7
  •   edA-qa mort-ora-y    14 年前

    这里的答案并不简单。究竟发生了什么,意味着什么取决于许多事情。对于缓存一致性/内存的基本理解,我最近的博客条目可能会有所帮助:

    但除此之外,让我试着回答几个问题。首先,下面的说明对所支持的内容非常有希望。

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

    体系结构不能完全按照您的要求实现这一点。当您指定内存顺序时,您将指定重新排序的工作方式。要使用Intel的术语,您将指定您想要的围栏类型,其中有三种:全围栏、装载围栏和存储围栏。仅仅因为你想在这个操作上设置一个特定的围栏并不意味着它得到了支持,我希望它总是回到一个完整的围栏中。

    编译器可能会使用 CMPXCHG 指示执行调用。如果您指定了除“轻松”之外的其他内容,它将用 lock 表示函数应该是原子的。这是否是“无锁”在很大程度上取决于你对“锁”的看法。

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

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

    --对一些评论的回答:

    您必须区分执行写指令和写入内存位置意味着什么。这就是我在博客中试图解释的。当“0”被提交到0x100时,所有核心都会看到这个零。写整数也是原子的,也就是说,即使没有锁,当你写到一个位置时,如果所有核心想要使用它,它们都会立即拥有这个值。

    问题是,要使用您可能首先将其加载到寄存器中的值,之后对位置的任何更改显然都不会触及寄存器。这就是为什么一个人需要互斥锁,尽管有一个缓存一致的内存。

    至于相互矛盾的主张,一般你会看到各种各样的主张。不管它们是否矛盾,归根结底就是“see”“load”“execute”在上下文中的确切含义。如果您将“1”写入0x100,这是否意味着您执行了写入指令,或者CPU实际提交了该值。差异来自于重新排序。CPU可以延迟写入“1”,但您可以确定,当它最终提交“1”时,所有核心都会看到它。围栏控制着这一秩序。