3

我在多处理器系统上的共享内存 x 中有一个变量。

void MyFunction(volatile int* x) {
  if (*x != 0) {
     // do something
  }
}

其他进程(可能在不同的处理器上)将使用 gcc 内置的原子操作(例如 __sync_bool_compare_and_swap 等)写入 x。

我想我遇到了一些缓存并发问题,有时在 x 最终用新值更新之前需要一些时间。

我想要的是一种 atomic_compare (没有交换),如果存在这样的事情?或“原子读取”。最快的方法是什么?(避免互斥体、锁等)

谢谢

编辑:

我刚刚意识到一个有点骇人听闻的解决方法是使用 __sync_val_compare_and_swap 的值,我知道它永远不可能。那能解决问题吗?(有没有更清洁的方法?)

4

3 回答 3

4

新的 C 标准 C11 具有_Atomic处理此问题的数据类型和操作。该标准尚未实现,但 gcc 和 clang 已接近它,它们已经实现了该功能。事实上,功能 __sync_bool_compare_and_swap是其中的一部分。我已经将它包装到P99 中的一组标题中,让您已经使用 C11 接口进行编程。

C11 功能可以做您想做的事情,atomic_load或者如果您对连贯性有特殊要求atomic_load_explicit。毫不奇怪,正如您所怀疑的那样,P99 将其映射到__sync_val_compare_and_swap(&x, 0, 0). 然后,如果您查看它在大多数体系结构上生成的汇编程序,这将只转换为一个简单的加载操作,如果xint. 但这不是语言所保证的,由编译器知道这些事情并合成保证是原子的指令。

于 2012-06-30T06:15:18.593 回答
2

最快的方法是什么?(避免互斥体、锁等)

我很确定您不想避免互斥锁。linux 的 futexes 允许您利用比较和交换的优点(大部分时间),同时保持经典的互斥体语义(发生的“交换”是互斥体之一,而不是受其保护的代码/数据)。我强烈建议您尝试并分析解决方案(perf、、oprofileVTune 等),看看您的瓶颈是否真的与锁定机制本身有关,而不是缓存利用率、内存吞吐量、CPU 周期、IO 访问、远程节点等内存访问等

我想我遇到了一些缓存并发问题,有时在 x 最终用新值更新之前需要一些时间。

好吧,假设您确实需要在处理器之间进行交互,并且您已经测量了从 futex 获得的延迟影响,并且您确定它不能满足您的应用程序的需求。因此,如果是这种情况,一个相对健全的方法可能是这样的:创建一个 32 位整数数组,填充的距离大于或等于目标缓存行的大小。使用当前执行的 CPU 和缓存行大小作为此列表中实际值的索引(因此,如果您的缓存行是 64 字节,您可以将 CPU# 缩放 16 以跳过填充)。您应该只从适当的 CPU 写入这些值,并且可以从任何其他 CPU 轮询它(可能应该在忙等待的主体中调用您的 CPU 的“暂停”指令之一)。

我应该补充一点,这几乎肯定会起作用(有效地用 CPU 效率换取可能更低的延迟),但对于除了一组非常特殊的硬件之外的所有硬件来说,它仍然是一个非常脆弱的解决方案。

于 2012-06-30T01:51:08.983 回答
0

我想要的是一种 atomic_compare (没有交换),如果存在这样的事情?或“原子读取”。

比较已经是原子的。是单读。

如果处理器之间的延迟已经很糟糕,那么您的代码似乎会从解耦中受益。即稍微分离出依赖关系,这样您就不会依赖内部循环中的这种通信。

于 2012-06-30T02:02:47.870 回答