【问题标题】:Is atomic decrementing more expensive than incrementing?原子递减比递增更昂贵吗?
【发布时间】:2013-06-06 15:23:47
【问题描述】:

在他的Blog Herb Sutter 中写道

[...] 因为增加智能指针引用计数 通常可以优化为与普通增量相同 在优化的shared_ptr 实现中——只是一个普通的增量指令, 并且在生成的代码中没有围栏。

但是,减量必须是原子减量或等价物, 它会生成特殊的处理器内存指令,这些指令在 他们自己,最重要的是诱导记忆 优化周边代码的栅栏限制。

本文是关于shared_ptr 的实现,我不确定他的评论是否仅适用于此或一般情况下。根据他的表述,我认为它是一般

但是当我想到它时,我只能在if(counter==0) 紧随其后时想到“更昂贵的减量”——shared_ptr 可能就是这种情况。

因此我想知道 atomic 操作 ++counter 是否(通常)总是比 --counter或者只是因为它是使用if(--counter==0)...shared_ptr

【问题讨论】:

  • “从他的表述来看,我认为它通常是” - 我认为相反。
  • 您可能还想查看 --counter == 0 所需的 WriteRead 屏障,它通常是最昂贵的屏障,也是唯一需要在写入数据后,必须重新读取它以保证内核之间的同步。对于 counter++,您只需编写即可,无需立即获取此值或其他数据的同步状态。

标签: c++ performance atomic reference-counting memory-fences


【解决方案1】:

他在某处更详细地讨论了这一点,我想在他的atomic<> weapons 演示文稿中。基本上,这都是关于在 shared_ptr 用例中需要内存围栏的地方,而不是原子增量与减量的任何内在属性。

原因是你并不真正关心使用 ref 计数的智能指针的增量的确切顺序,只要你不遗漏任何东西,但是对于减量,你有适当的内存屏障是至关重要的,这样触发删除的最终递减量在释放内存后,您不可能从另一个线程对智能指针拥有的对象进行先前的内存访问。

【讨论】:

  • Herb 的说法简直是天方夜谭!谢谢! +1
  • 然而,“只要你不错过任何一个”是重要的部分。那,以及关于递减的发生之前的保证(但这是由原子递减处理的)。他明确地说“正常增量”,这在并发场景中错过一些增量。也许他打算说些不同的话,但无论如何他都是这么说的。
【解决方案2】:

我相信它指的是增量可以“隐藏”,其中“减量和检查”必须作为一个操作完成。

我不知道--counter(或counter--,假设我们谈论的是int、char等简单数据类型)比++countercounter++慢的任何架构。

【讨论】:

  • “隐藏”是什么意思?
  • 这与他所说的完全相反。他明确地说“普通增量”。这不是事后不检查价值或任何此类事情。这是关于使用普通增量(读取缓存线、修改、写入缓存线)在存在并发时丢失引用。因此,我强烈倾向于说他所说的完全是错误的。萨特也会说错话,为什么不呢。
【解决方案3】:

Sutter 谈到的问题是引用计数增量不需要任何后续操作来确保正确性。您将一个非零引用计数转换为另一个非零计数,因此不需要进一步的操作。但是,减量需要采取后续行动以确保正确性。递减将非零引用计数变为非零或零引用计数,如果您的引用计数递减为零,您需要执行一个操作 --- 具体来说,释放引用的对象。这种递减和执行动态需要更高的一致性,无论是在栅栏级别(因此释放不会与 CPU 的内存/缓存管理逻辑重新排序的另一个内核上的其他读/写一起重新排序)和在编译器级别(因此编译器不会围绕可能导致读/写围绕潜在释放重新排序的递减重新排序操作)。

因此,对于 Sutter 所描述的场景,增量和减量之间的成本差异不在于基本操作本身,而在于对减量的实际使用施加的一致性约束(具体而言,作用于减量本身)不适用于增量。

【讨论】:

    猜你喜欢
    • 2011-01-07
    • 2012-07-18
    • 2011-05-09
    • 1970-01-01
    • 2021-01-10
    • 2010-10-12
    • 1970-01-01
    • 1970-01-01
    • 2013-01-27
    相关资源
    最近更新 更多