交换值 C++ 的最有效方法

most efficient way of swapping values c++

本文关键字:有效 方法 C++ 交换      更新时间:2023-10-16

我想知道在 c++ 中交换整数最有效的操作方式是什么,为什么?是这样的:

int a =..., b = ...;
a = a + b;
b = a - b;
a = a - b;

比使用临时的更有效率?还有其他更有效的方法吗?(不要求只是交换整数的其他方法(为什么它们会更有效率?

分配值总是比执行算术运算更快。

C++ std::swap 的实现是

template<typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1); // or T temp(std::move(t1));
    t1 = std::move(t2);
    t2 = std::move(temp);
}

因此,使用临时变量比做算术技巧要好。
使用std::swap甚至更好,因为在编程中重新发明轮子从来都不是一个好主意

最好的方法是信任编译器并使用C++标准库函数。它们是为彼此设计的。

std::swap会赢。

您可以将 XOR 交换用于int(不需要临时(,但如今它的性能仍然不如 std::swap .

就我而言,std::swap比以下慢 5%(两者都使用 O3 优化(。通常,std::swap(( 函数调用复制构造函数可能总是比仅复制部分内存慢。

#include <cstring>
size_t objectSize = sizeof(Object);
char temp[objectSize];
loop {
    loop {
        memcpy(temp, a, objectSize);
        memcpy(a, b, objectSize);
        memcpy(b, temp, objectSize);
    }
}

编辑:使用堆栈而不是堆内存分配。

最有效的方法是不要尝试自己做。这真的取决于你为什么/是否要这样做。试图变得聪明并用C++编写晦涩的代码只会减少编译器正确优化它的机会。

假设我们使用您编写的±方式:首先,必须从内存中加载值 a 和 b。然后,您正在执行 3 个算术运算来"交换"它们的内容。最后,这 2 个值必须再次存储在内存中。(不打算使用实际的汇编代码,因为我不精通它,而且这个伪汇编更容易理解这个概念(

load a into register rA
load b into register rB
add rB to rA and store in rA
subtract rB from rA and stor in rB
subtract rB from rA and store in rA
store register rA to memory b
store register rB to memory a

如果编译器完全按照您想要的方式做(他可能会忽略它并使其更好(,那就是:2 个加载,3 个简单的数学功能,2 个商店 - 7 个操作。

它也可以做得稍微好一点,因为加法/减法可以用内存中的 1 个值来完成。

load 'a' into register rA
add b to rA and store in rA
subtract b from rA and store in rB
subtract rB from rA and store in rA
store rA to a
store rB to b

如果我们使用一个额外的 tmp 变量:

int a =..., b = ...;
int tmp = a;
a = b;
b = tmp;

编译器可能会认识到"tmp"只是一个临时变量,仅用于交换 2 个值,因此它不会为其分配仅使用寄存器的内存位置 btu。在这种情况下,它将做的是类似于以下内容:

load a into register rA
load b into register rB
store register rA to memory b
store register rB to memory a

只有 4 个操作 - 基本上它是最快的,因为您需要加载 2 个值,并且您需要存储 2 个值,没有别的。(对于 Moder nx86_64 处理器,没有命令可以只交换内存中的 2 个值 - 其他架构可能有它,在这种情况下甚至更快(。

执行这些算术运算(或异或技巧(是一个很好的练习,但在除了最基本的编译器之外的所有现代x86 CPU上,它不会以任何形式"更有效率"。它将使用同样多的寄存器,相同的变量内存量,但需要更多的指令来完成相同的工作。通常,除非您检查了代码,对其进行了测试和基准测试,并发现生成的程序集没有达到应有的水平,否则您不应该尝试超越编译器。

但几乎不需要达到这个水平进行优化,您的时间最好花在查看更大的图景上。

#include <iostream>
using namespace std;
void swap(int &a, int &b){
    b = (a+b) - (a=b);
}
int main() {
    int a=1,b=6;
    swap(a,b);
    cout<<a<<b;
    return 0;
}