我在多处理器系统上的共享内存x中有一个变量。
void MyFunction(volatile int* x) {
if (*x != 0) {
// do something
}
}
其他进程(可能在不同的处理器上)将使用gcc内置的原子操作(如__sync_bool_compare_and_swap等)写入x。
我认为我遇到了一些缓存并发问题,有时需要一些时间才能最终使用新值更新x。
我想要的是一种atomic_compare(没有交换),如果存在这样的东西?或者是“原子阅读”。最快的方法是什么? (避免使用互斥锁,锁等)。
由于
编辑:
我刚刚意识到一个有点hackish的解决方法是使用__sync_val_compare_and_swap,其值为我知道它永远不会是。这会解决问题吗? (有更清洁的方式吗?)
答案 0 :(得分:4)
新的C标准C11具有_Atomic
个数据类型和操作来处理这个问题。该标准尚未实现,但gcc和clang已接近它,它们已经实现了该功能。事实上,函数__sync_bool_compare_and_swap
是其中的一部分。我把它包装成set of headers in P99,让你可以使用C11接口进行编程。
C11函数可以执行您想要的操作atomic_load
,或者您对连贯性atomic_load_explicit
有特殊要求。毫无疑问,正如您所怀疑的那样,P99会在__sync_val_compare_and_swap(&x, 0, 0)
上映射。然后,如果您查看在大多数体系结构上生成的汇编程序,那么在x
建立int
的情况下,这只会在简单的加载操作中进行转换。但是语言并不能保证这一点,编译器需要知道这些事情并合成保证是原子的指令。
答案 1 :(得分:2)
最快的方法是什么? (避免使用互斥锁,锁等)。
我很确定你不想避免使用互斥锁。 linux的futexes允许你利用比较和交换(大部分时间)的优点,同时保持经典的互斥语义(发生'交换'是互斥体之一,而不是受其保护的代码/数据)。我强烈建议您尝试使用它们并分析解决方案(perf
,oprofile
,VTune等)以查看您的瓶颈是否与锁定机制本身有关,而不是缓存利用率,内存吞吐量等,CPU周期,IO访问,远程节点存储器访问等。
我认为我遇到了一些缓存并发问题,有时需要一些时间才能最终使用新值更新x。
好吧,让我们假设您确实需要在处理器之间进行交互,并且您已经测量了从futex获得的延迟命中率,并且您已经确定它无法满足您的应用程序的需求。因此,如果是这种情况,一个相对理智的方法就是这样:创建一个32位整数数组,填充大于或等于目标缓存行大小的距离。使用当前正在执行的CPU和高速缓存行大小作为此列表中实际值的索引(因此,如果您的高速缓存行为64字节,则将CPU#16缩放以跳过填充)。您应该只从相应的CPU写入这些值,并且可以从任何其他CPU轮询它(可能应该在忙等待的主体中调用CPU的一个“暂停”指令)。这将是一种有效的机制,用于检查不同的执行线程是否已达到/满足给定条件。
我应该补充一点,这几乎肯定会起作用(有效地将CPU效率换成可能更低的延迟)但对于除了一组非常特殊的硬件之外的所有硬件来说仍然是一个非常脆弱的解决方案。
答案 2 :(得分:0)
我想要的是一种atomic_compare(没有交换),如果这样的话 东西存在吗?或者是“原子阅读”。
比较已经是原子的。这是一次阅读。
如果处理器之间的延迟已经很糟糕,那么您的代码似乎可以通过将其解耦而受益。即将依赖关系分开一点,这样你就不会依赖内部循环中的这种通信。