4

由于英特尔提供强大的硬件内存模型,在 C++11 程序中使用“memory_order_relaxed”有什么优势吗?或者只是将其保留为默认的“顺序一致”,因为它没有区别?

4

2 回答 2

2

就像计算机科学中的大多数答案一样,这个问题的答案是“这取决于”。

首先,顺序一致的排序永远不会带来任何惩罚的想法是不正确的。根据您的代码(可能还有编译器),它可以并且将会受到惩罚。

其次,要对内存排序约束做出明智的决策,您需要考虑(并理解)您如何使用相关数据。

memory_order_relaxed 对于需要原子的独立计数器之类的东西很有用,但与其他东西没有直接关系,因此它不需要与任何“其他东西”保持一致。典型的例子是引用计数,例如shared_ptrstd::string. 在这种情况下,我们只需要确保计数器以原子方式递增和递减,并且对它的修改对所有线程都是可见的。但是,特别是,没有任何相关数据需要保持一致,所以我们不太关心它相对于其他任何东西的排序。

顺序一致的排序几乎处于相反的极端。它可能是最容易应用的——您编写代码就像它是单线程的一样,并且实现确保它正常工作(这并不是说您根本不必考虑线程,而是顺序一致的顺序通常需要最少的考虑,但通常也是最慢的模型)。

当您拥有两条或更多条相关信息时,通常会使用获取/发布一致性,并且您需要确保其中一条仅在另一条之前/之后才可见。对于我最近处理的一个示例,假设您正在构建大致类似于内存数据库的东西。您有一些数据,并且有一些元数据(并且您或多或少地分别存储)。

元数据(除其他外)用于搜索数据库。我们希望确保,如果有人找到某些特定数据,他们找到的数据将实际存在于数据库中。

为了确保这一点,我们希望确保数据始终存在于元数据之前,并且至少与元数据一样长。如果有人可以使用元数据搜索数据库并找到它想要使用的一些数据,而这些数据实际上并不存在,那么数据库将是不一致的。

所以在这种情况下,当我们添加一条记录时,我们需要确保我们先添加数据,然后添加元数据——并且编译器不能重新排列这两者。同样,当我们删除一条记录时,我们需要删除元数据(这样没人会找到数据),然后删除数据本身。就数据本身而言,我们可能有一个引用计数来跟踪当前有多少客户端正在访问该数据,以确保我们不会在有人尝试使用它时删除它。

所以在这种情况下,我们可以对元数据和数据使用获取/释放语义,对引用计数使用宽松的排序。或者,如果我们想让我们的代码尽可能简单,我们可以在整个过程中使用顺序一致性——即使它可能(并且可能会)带来至少一些惩罚。

于 2015-01-20T16:19:01.130 回答
2

始终使用使代码正确所需的最低保证。

不多也不少。

这样,您可以避免对实现的任何不必要的依赖,从而降低任何移植成本,并且仍然可以获得最快的程序。

当然,如果您确定自己永远不会关心移植任何代码,那么在您知道在您的平台上无关紧要的地方采取更强有力的保证可能会更容易证明它是正确的。
更难误用、更容易推理或更短也是使用性能较低的构造的完全可接受的原因。

于 2015-01-20T16:22:15.403 回答