std::atomic
等store
和load
等函数采用std::memory_order
参数。可以在运行时确定参数,就像任何其他函数参数一样。但是,实际值可能会影响编译期间代码的优化。请考虑以下事项:
std::atomic<int> ai1, ai2;
int value = whatever;
void foo() {
std::memory_order memOrd = getMemoryOrder();
register int v = value; // load value from memory
ai1.store(v, memOrd); // dependency on v's value
ai2.store(1, memOrd); // no dependency. could this be move up?
}
如果memOrd
恰好是memory_order_relaxed
,则可以安全地将第二家商店移到第一家商店前面。这将在加载value
和使用它之间添加一些额外的工作,这可能会阻止其他需要的停顿。但是,如果memOrd
为memory_order_seq_cst
,则不应允许切换商店,因为如果ai1
某些其他帖子可能指望value
已设置为ai2
设置为1。
我想知道为什么将内存顺序定义为运行时参数而不是编译时间。在决定最佳内存操作语义之前,有人有理由在运行时检查环境吗?
答案 0 :(得分:6)
原因这是作为运行时参数而不是编译时参数实现的,是为了启用合成。
假设您正在编写一个函数,该函数使用提供的原子操作来执行等效的加载操作,但是在更高级别的构造上操作。通过将存储器顺序指定为运行时参数,较高级别的负载然后可以将用户提供的存储器顺序参数传递给提供排序所需的低级原子操作,而不需要更高级别的操作必须是模板。
通常,原子指令将是内联的,并且编译器将在它实际上是编译时常量的情况下消除对内存顺序参数的测试。
答案 1 :(得分:4)
这只是一个接口规范,允许在运行时指定memory_order
。它不要求实施使用该限额。
例如,在x86硬件上memory_order_seq_cst
可能就是你得到的,无论你指定什么。由于硬件缓存一致性协议,memory_order_relaxed
无法使用。
在其他硬件上,您可以优化编译时已知顺序,实现可能会提供利用默认参数的额外重载。
答案 2 :(得分:0)
C ++的编写者可能已经将memory_order实现为编译时功能,而不是运行时功能。但是,他们不会为此获得任何收益。任何能够理解内存顺序的编译器都可以轻松地优化x.load(memory_order_acq)等明显的情况,因此它们不会从编译时功能中受益。
同时,使用运行时功能意味着他们不必为内存顺序引入任何新的表示法。它们只是函数参数。这意味着它们获得了与编译时版本相同的好处,而且复杂性更低。
同时,对于更简单的编译器来说非常方便,因为它们可以将原子实现为普通类,而不必专门处理它。
T atomic<T>::compare_exchange_strong(T& compare, T exchange, memory_order order)
{
lockBeforeUsing(order); // handle the acquire part of the memory order
if (mValue == compare) {
compare = mValue;
mValue = compare;
} else {
compare = mValue;
}
lockAfterUsing(order); // handle the release part of the memory order
}