编译为更快的代码:“n * 3”还是“n+(n*2)”?
-
09-06-2019 - |
题
编译为更快的代码:“ans = n * 3”或“ans = n+(n*2)”?
假设 n 是 int 或 long,并且它运行在现代 Win32 Intel 机器上。
如果涉及一些解除引用,这会有所不同吗?也就是说,哪一个会更快?
long a; long *pn; long ans; ... *pn = some_number; ans = *pn * 3;
或者
ans = *pn+(*pn*2);
或者,这是不需要担心的事情,因为优化编译器在任何情况下都可能会考虑到这一点?
解决方案
在我看来,除非您使用某种特殊的编译器,否则这种微优化是没有必要的。我会把可读性放在第一位。
其他提示
没关系。现代处理器可以在一个时钟周期或更短的时间内执行整数 MUL 指令,这与旧处理器不同,旧处理器需要在内部执行一系列移位和加法才能执行 MUL,从而使用多个周期。我敢打赌
MUL EAX,3
执行速度快于
MOV EBX,EAX
SHL EAX,1
ADD EAX,EBX
最后一种可能有用这种优化的处理器可能是 486。(是的,这偏向于英特尔处理器,但也可能代表其他架构)。
无论如何,任何合理的编译器都应该能够生成最小/最快的代码。因此,始终首先考虑可读性。
既然自己测量很容易,为什么不这样做呢?(使用 gcc
和 time
来自赛格温)
/* test1.c */
int main()
{
int result = 0;
int times = 1000000000;
while (--times)
result = result * 3;
return result;
}
machine:~$ gcc -O2 test1.c -o test1
machine:~$ time ./test1.exe
real 0m0.673s
user 0m0.608s
sys 0m0.000s
进行几次测试,然后对其他情况重复该测试。
如果你想查看汇编代码, gcc -S -O2 test1.c
这取决于编译器、其配置和周围的代码。
您不应该在不进行测量的情况下尝试猜测事物是否“更快”。
一般来说 现在你不应该担心这种纳米级优化的东西 - 它几乎总是完全无关的,如果你真正在一个重要的领域工作,你已经在使用探查器并查看编译器的汇编语言输出。
找出编译器对代码执行的操作并不困难(我在这里使用 DevStudio 2005)。使用以下代码编写一个简单的程序:
int i = 45, j, k;
j = i * 3;
k = i + (i * 2);
在中间行放置一个断点并使用调试器运行代码。当断点被触发时,右键单击源文件并选择“Go To Disassemble”。现在您将看到一个窗口,其中包含 CPU 正在执行的代码。在这种情况下,您会注意到最后两行产生完全相同的指令,即“lea eax,[ebx+ebx*2]”(在这种特殊情况下不是位移和加法)。在现代 IA32 CPU 上,由于 CPU 的流水线性质,执行直接 MUL 可能比位移更有效,过早使用修改值会带来损失。
这证明了 aku 正在谈论的内容,即编译器足够聪明,可以为您的代码选择最佳指令。
它确实取决于您实际使用的编译器,但它们很可能会翻译为相同的代码。
您可以通过创建一个小测试程序并检查其反汇编来自行检查。
大多数编译器都足够聪明,可以将整数乘法分解为一系列位移和加法。我不了解Windows编译器,但至少使用gcc你可以让它吐出汇编器,如果你看一下它,你可能会看到两种编写方式的相同汇编器。
它不在乎。我认为还有更重要的事情需要优化。您投入了多少时间思考和编写该问题,而不是自己编码和测试?
:-)
只要你使用一个像样的优化编译器,就可以 编写易于编译器理解的代码. 。这使得编译器更容易执行巧妙的优化。
您问这个问题表明优化编译器比您更了解优化。所以相信编译器。使用 n * 3
.
看一下 这个答案 以及。
编译器擅长优化像您这样的代码。任何现代编译器都会为这两种情况生成相同的代码,并另外替换 * 2
通过左移。
相信你的编译器会优化这样的小代码。可读性在代码级别更为重要。真正的优化应该出现在更高的水平上。