代码之家  ›  专栏  ›  技术社区  ›  Dietrich Epp

原子参考计数共享不变数据是否需要内存屏障?

  •  12
  • Dietrich Epp  · 技术社区  · 15 年前

    我有一些不可变的数据结构,我想使用引用计数管理它们,在SMP系统上跨线程共享它们。

    发布代码如下所示:

    void avocado_release(struct avocado *p)
    {
        if (atomic_dec(p->refcount) == 0) {
            free(p->pit);
            free(p->juicy_innards);
            free(p);
        }
    }
    

    atomic_dec 需要一个记忆屏障吗?如果是这样,什么样的记忆障碍?

    附加说明:应用程序必须在PowerPC和x86上运行,因此欢迎使用任何特定于处理器的信息。我已经知道海湾合作委员会的原子内建。至于不可变性,refcount是 只有 在对象持续时间内更改的字段。

    3 回复  |  直到 10 年前
        1
  •  11
  •   Community CDub    8 年前

    在x86上,它将变成以锁为前缀的程序集指令,例如 LOCK XADD
    作为一条指令,它是不可中断的。作为一个附加的“功能”,锁前缀导致一个完整的内存屏障:

    “..locked operations序列化所有未完成的加载和存储操作(即等待它们完成)。..locked operations是所有其他内存操作和所有外部可见事件的原子操作。只有指令获取和页表访问才能传递锁定的指令。锁定指令可用于同步一个处理器写入的数据和另一个处理器读取的数据。“”- Intel® 64 and IA-32 Architectures Software Developer’s Manual ,第8.1.2章。

    内存屏障实际上是作为一个虚拟对象实现的。 LOCK OR LOCK AND 两者都有 the .NET the JAVA JIT 在x86/x64上。
    所以不管你喜欢与否,你都有一个完整的x86防护作为额外的奖励。:)

    在PPC上,它是不同的。安 LL/SC 对- lwarx & stwcx -使用内部减法,可以将内存操作数加载到寄存器中,减去一个,然后在没有其他存储的情况下将其写回目标位置,或者在没有其他存储的情况下重试整个循环。LL/SC可以中断。
    它也不意味着一个全自动围栏。
    然而,这不会以任何方式损害计数器的原子性。
    这仅仅意味着在x86的情况下,你也会得到一个“免费”的围栏。
    在PPC上,可以通过发出 (lw)sync instruction .

    总而言之,原子计数器正常工作不需要显式内存屏障。

        2
  •  4
  •   Bruce Dawson    10 年前

    区分原子访问(保证值的读/修改/写作为一个原子单元执行)和内存重新排序是很重要的。

    内存屏障阻止读写的重新排序,并且重新排序与原子性完全正交。例如,在PowerPC上,如果您实现尽可能高效的原子增量,那么它将不会阻止重新排序。如果你想防止重新排序,那么你需要一个LWSYNC或同步指令,或者一些等价的高级(C++ 11?)记忆障碍。

    声称“编译器不可能以有问题的方式对事物重新排序”作为一般性语句似乎很幼稚,因为编译器优化可能会非常令人惊讶,而且因为CPU(特别是PowerPC/ARM/Alpha/MIPS)会积极地重新排序内存操作。

    一个一致的缓存也不能保存您。见 http://preshing.com/ 看看内存重新排序是如何工作的。

    然而,在这种情况下,我认为答案是不需要障碍物。这是因为对于这个特定的情况(引用计数),不需要引用计数和对象中的其他值之间的关系。唯一的例外是当引用计数为零时。此时,确保其他线程的所有更新对当前线程可见是很重要的,因此读取获取屏障 可以 是必要的。

        3
  •  2
  •   Marcelo Cantos    15 年前

    你打算实施你自己的 atomic_dec 或者您只是想知道系统提供的函数是否会按您想要的方式工作?

    作为一般规则,系统提供的原子增量/减量工具将应用所需的任何内存屏障,以便正确地执行操作。通常,您不必担心内存障碍,除非您正在做一些奇怪的事情,比如实现自己的无锁数据结构或STM库。