我有一些不可变的数据结构,我想使用引用计数来管理它们,并在 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 上运行,因此欢迎任何特定于处理器的信息。我已经了解 GCC 原子内置函数。至于不变性,引用计数是 仅有的 随着对象的持续时间而变化的字段。

有帮助吗?

解决方案

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

“ ...锁定的操作序列化所有出色的负载和存储操作(也就是说,等待它们完成)。” ...“锁定操作相对于所有其他内存操作和所有外部可见事件都是原子的。只有取指令和页表访问才能通过锁定指令。锁定指令可用于同步一个处理器写入的数据和另一处理器读取的数据。”- 英特尔® 64 和 IA-32 架构软件开发人员手册, ,第 8.1.2 章。

内存屏障实际上是​​作为虚拟实现的 LOCK OR 或者 LOCK AND 同时 互联网JAVA JIT 在 x86/x64 上。
因此,无论您是否喜欢,您都可以在 x86 上拥有完整的围栏作为额外的好处。:)

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

总而言之,显式内存屏障对于原子计数器的正常工作来说并不是必需的。

其他提示

有原子访问之间进行区分是很重要的(这保证读/修改/写的值执行作为一个原子单元的)和存储器的重排序。

记忆障碍妨碍重新排序的读取和写入和重新排序是完全正交的原子性。例如,在PowerPC如果您实现最有效的原子增量可能,那么它不会阻止重新排序。如果要防止然后重新排序则需要一个lwsync或同步指令,或一些相当的高电平(C ++ 11?)存储器屏障。

声称有“无编译器重新排序的东西在有问题的方式的可能性”似乎幼稚作为一般的语句,因为编译器优化可以是相当令人惊讶的和因为CPU的(的PowerPC / ARM /α/特别MIPS)积极地重新排序的存储器操作

有一个连贯的高速缓存不保存你要么。请参见 http://preshing.com/ 怎么看存储器记录确实有效。

在这种情况下,然而,相信答案是不需要的障碍。这是因为,对于这种特殊情况下(引用计数)就没有必要对引用计数和在对象中的其它值之间的关系。唯一的例外是当引用计数降为零。在这一点上是很重要的,以确保从其他线程的所有更新对于当前线程可见,以便读获取屏障是必要的。

您打算实现自己的atomic_dec或者你只是想知道一个系统提供的功能是否将表现为你想要的吗?

作为一般规则,系统提供的原子递增/递减设施将适用任何记忆力障碍需要只是做正确的事情。你一般不必对记忆障碍的担心,除非你正在做的事情的古怪样实现自己的无锁数据结构或STM库。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top