x86 放松订购性能

x86 relaxed ordering performance?

本文关键字:性能 x86      更新时间:2023-10-16

既然英特尔提供了强大的硬件内存模型,那么在C++11程序中使用"memory_order_relaxed"有什么好处吗?或者只是将其保留为默认值"顺序一致",因为它没有区别?

像计算机科学中的大多数答案一样,答案是"这取决于"。

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

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

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

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

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

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

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

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

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

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

不多也不少。

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

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