使用寄存器交换值
Swapping values using registers
我一直在读关于在不使用临时变量的情况下交换变量内容的文章,除了我发现的关于x86上汇编的XCHG指令的著名xor算法之外。所以我写了这个代码:
void swap(int *left, int *right){
__asm__ __volatile__(
"movl %0, %%eax;"
"movl %1, %%ebx;"
:
: "r" (*left), "r" (*right)
);
__asm__ __volatile__(
"xchg %eax, %ebx;"
);
__asm__ __volatile__(
"movl %%eax, %0;"
"movl %%ebx, %1;"
: "=r" (*left), "=r" (*right)
);}
它确实有效,但后来我意识到XCHG指令根本没有必要。
void swap(int *left, int *right){
__asm__ __volatile__(
"movl %0, %%eax;"
"movl %1, %%ebx;"
:
: "r" (*left), "r" (*right)
);
__asm__ __volatile__(
"movl %%ebx, %0;"
"movl %%eax, %1;"
: "=r" (*left), "=r" (*right)
);}
第二个函数也起作用,但似乎没有人提到使用寄存器交换变量,所以这段代码是否被认为是错误的,而实际上它并没有真正正常工作?我是不是错过了什么?
我知道这只适用于x86,但由于大多数人都有英特尔x86处理器,这段代码可以用于任何现实世界的编程吗?我意识到这可能不会比用一个临时变量进行常规交换更快,但我是从理论的角度提出问题的。如果在一次测试或面试中,有人要求我在不使用临时变量的情况下用C编写一个函数来交换x86机器的值,那么这段代码是有效的还是完全是垃圾?谢谢你。
有效,是。按照我的标准,你是一个没有雇用。
为什么?费用
std::swap可以很好地完成任务,而且可能足够快。您的代码将有更高的维护成本。
当然,出于性能原因,有时会陷入汇编程序
这不是其中之一。
首先,内联程序集在很多方面都被破坏了:
- 滥用
volatile
,这并不意味着你想要什么 - 你不会告诉编译器你破坏了寄存器。(这是可以修复的)
- 编译器可以在内联汇编块之间自由插入代码
对于程序员和编译器来说,内联汇编都很难做到正确。
此外,内联汇编可能会通过非常小心的破解进行优化,但它会以损害优化器能力的方式影响编译器(寄存器分配、重新排序等),这通常会导致整体性能下降。我并不反对内联汇编(或编译器内部),但它需要非常小心的处理,这使得它在大多数情况下都不合理。
std::swap
将编译为比这更高效的asm。
这段代码比编译器发出的代码慢,而且已经损坏。
它在不告诉编译器的情况下攻击EAX和EBX,并且很容易失败,尤其是在启用优化的情况下编译,但即使没有优化也不安全。
请参阅如何编写一小段内联gnu扩展程序集来交换两个整数变量的值?例如,在xchg周围更正asm,以及一个更好的版本,它只使用约束来用零asm指令交换C变量,让编译器来确定它想要在哪个寄存器中使用哪个C变量。asm("" : "=r" (a), "=r" (b) : "1" (a), "0" (b));
即使您使用零asm语句和相反的输入/输出约束的内联asm,您仍然会破坏常量传播和范围分析,从而破坏优化。https://gcc.gnu.org/wiki/DontUseInlineAsm.我对前面链接的答案的编辑包括一个纯C交换,表明恒定传播在那里有效,但不适用于任何内联asm交换。
因此,即使是一个正确编写的版本,对于任何现实世界的使用来说仍然是无用的,除了作为一个练习/示例,为GNUC内联asm使用输入/输出约束,以避免在实际asm块的开始/结束处出现mov
,并尽可能多地由编译器决定。
只有当你做了一些编译器无法做得比你个人更好的事情时,Asm才能赢得性能。(用于测试Collatz猜想的C++代码比手工编写的汇编更快——为什么?)
查看编译器的asm输出以获得正常函数(如何从GCC/clang汇编输出中消除"噪声"?),然后查看
- https://agner.org/optimize/
- 以及在预测现代超标量处理器上的操作延迟时需要考虑哪些因素,以及我如何手动计算它们
了解更多关于高效asm的原因。
- 本质:使用__128寄存器
- 将寄存器设计成可由C和C++访问的外设的最佳实践
- 在模拟器中使用并集来模拟CPU寄存器有多合适
- 使用英特尔 PIN 修改寄存器
- AVX 指令中寄存器和指针之间的客观差异
- 如何确定我的处理器有多少个 AVX 寄存器?
- 除非使用某些寄存器,否则函数挂钩会崩溃
- 寄存器上的管道计算
- 其中关于内存和寄存器的左值和右值
- 有没有办法强制C++编译器将变量存储在寄存器中?
- "变量":函数中函数作用域不允许初始化的自动或寄存器变量'naked'
- Atmel Studio:返回一个包含数组的寄存器
- 使用 googletest 测试嵌入式C++代码时处理外设寄存器的重复符号
- 移位寄存器74HC595输出电流
- 超过255的Modbus寄存器无法访问SimpleModbus
- 如何在程序集函数中将元素数组作为参数传递时转发 ARM 寄存器的地址指针
- xmm 寄存器中的__m128何时?
- 是否可以在 GCC 中使用带有 C++17 的显式寄存器变量?
- 处理器寄存器的大小是多少,有多少个处理器寄存器?
- 使用寄存器交换值