整数类型的 std::swap 有多快

How fast is std::swap for integer types?

本文关键字:swap 整数 std 类型      更新时间:2023-10-16

STL 实现了一个通用的 std::swap 函数来交换 2 个值。它可以通过以下方式呈现:

template <class T> void swap (T& a, T& b)
{
  T c(std::move(a));
  a=std::move(b);
  b=std::move(c);
}

但是,有一种异或交换算法可以交换 2 个整数 (http://en.wikipedia.org/wiki/XOR_swap_algorithm(:

void swap_u( size_t& x, size_t& y )
{
   x = x^y;
   y = x^y;
   x = x^y;
}

我的问题:

  1. 现在是优化吗(x86arm(?
  2. C++标准是否支持这种优化?
  3. 在野外是否有任何真正的 STL 实现对整数具有std::swap专用化?

在绝大多数情况下,异或交换不是一种优化。

请参阅此维基条目。

在大多数实际场景中,使用临时寄存器的简单交换算法效率更高。异或交换可能实用的有限情况包括:

  • 在指令集编码允许以较少的字节数编码 XOR 交换的处理器上;
  • 在寄存器
  • 压力较高的区域中,它可能允许寄存器分配器避免溢出寄存器。
  • 在可用RAM非常有限的微控制器中。

由于这些情况很少见,因此大多数优化编译器不会生成异或交换代码。

另请注意,异或交换的实现已损坏。您需要首先检查 x 和 y 是否没有别名。此检查肯定会使异或交换变慢。

我不知道有任何使用异或交换的标准库实现。

请注意,无论标准库实现什么,如果 XOR 交换确实比普通交换快,那么优化编译器将执行窥视孔优化以将其转换为 XOR 交换。这实际上是一个让编译器为您选择的情况。

或交换实际上只是一个噱头,在某些情况下可能会失败(例如,两个变量都是对同一对象的引用(。

异或交换也不是特别有效,因为它具有串行依赖关系,因此始终需要至少三个指令周期。使用带有临时的直接交换具有较少的依赖关系,允许在现代超标量CPU上进行一些并行性 - 在某些CPU上,它甚至可以在一个指令中实现,但即使没有特殊指令,它也可能在两个周期内执行。

在 X86 上,内存位置(不是 CPU 寄存器(之间的三重 XOR 交换与三重副本占用相同的处理器周期。如果临时是寄存器,它们可能会更少。

正如在大多数情况下已经解释的那样,异或位拨弄会更慢。

但它也在很大程度上取决于周围的代码。假设此交换是单独完成的,远离需要这些值的任何其他代码(因此它们不会加载到寄存器中(,我们在这里使用"普通"x86 处理器。

任何交换 2

个值的算法至少需要 2 个操作将值从内存加载到寄存器中,另外需要 2 个操作将这些值再次存储到内存中(x86 没有直接交换 2 个内存位置内容的操作(。

使用这样的临时变量时:

void swap (int& a, int& b)
{
  int temp = a;
  a = b;
  b = temp;
}

基本上,任何编译器都会认识到"temp"仅在本地用于交换,并且不会为其提供内存位置。由于它只保存"a"的值,因此它甚至不会是一个单独的寄存器。

它的汇编代码将如下所示(伪汇编(:

load a to rA
load b to rB
store rA to b
store rB to a

因此,在大多数情况下,就内存访问、指令数和寄存器数而言,这将是最有效的。

只有当编译器无法识别"temp"不用于其他任何内容并将其存储在单独的寄存器中(或被该死的精算内存(时,XOR 变体才能在任何方面更有效率。

但这仍然是理论上的,因为您的交换将被其他代码包围,这在那里将更为重要。如果这些值未被使用,则整个交换将被忽略。如果这些值直接用于其他计算,那么可能只是以下代码交换了 2 个寄存器,因此交换它本身有 0 条指令。而且您将很难找到任何比无所事事更有效的解决方案。

当然,还有其他更晦涩难懂的指令集可能具有直接交换 2 个内存位置内容的指令。