关于c ++:哪个可以编译为更快的代码:“ n * 3”或“ n +(n * 2)”?

关于c ++:哪个可以编译为更快的代码:“ n * 3”或“ n +(n * 2)”?

Which compiles to faster code: “n * 3” or “n+(n*2)”?

哪个编译为更快的代码:" ans = n * 3"或" ans = n +(n * 2)"?

假设n是一个int或long,并且它正在现代的Win32 Intel机器上运行。

如果涉及一些取消引用,这会有所不同吗,也就是说,哪些引用会更快?

1
2
3
4
5
6
7
long    a;
long    *pn;
long     ans;

...
*pn = some_number;
ans = *pn * 3;

要么

1
ans = *pn+(*pn*2);

还是不必担心,因为在任何情况下优化编译器都可能会解决这一问题?


除非您使用某些特殊的编译器,否则IMO无需进行此类微优化。我将可读性放在首位。


没关系现代处理器可以在一个时钟周期或更短的时钟周期内执行整数MUL指令,这与较旧的处理器不同,后者需要执行一系列移位并在内部进行加法以执行MUL,从而使用多个周期。我敢打赌

1
MUL EAX,3

执行速度比

1
2
3
MOV EBX,EAX
SHL EAX,1
ADD EAX,EBX

使用这种优化的最后一个处理器可能是486。(是的,这偏向于Intel处理器,但也可能代表其他体系结构)。

无论如何,任何合理的编译器都应该能够生成最小/最快的代码。因此,始终首先要具有可读性。


由于自己测量很容易,为什么不这样做呢? (使用来自cygwin的gcctime)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* 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)。用以下代码编写一个简单的程序:

1
2
3
int i = 45, j, k;
j = i * 3;
k = i + (i * 2);

在中间行放置一个断点,然后使用调试器运行代码。触发断点后,右键单击源文件,然后选择"转到反汇编"。现在,您将拥有一个窗口,其中包含CPU正在执行的代码。在这种情况下,您会注意到最后两行产生的指令完全相同,即" lea eax,[ebx + ebx * 2]"(在这种特殊情况下,不进行移位和加法运算)。在现代的IA32 CPU上,由于CPU的流水线特性,如果直接使用修改后的值会过快,则直接执行MUL而不是进行位移位可能会更有效。

这证明了aku在谈论什么,即编译器足够聪明,可以为您的代码选择最佳指令。


这将取决于编译器,其配置和周围的代码。

如果不进行测量,您不应尝试猜测事物是否"更快"。

总的来说,您现在不必担心这种纳米级的优化工作-它几乎总是完全无关紧要的,并且,如果您真正在一个重要的领域工作,那么您已经在使用探查器并查看汇编语言的输出。编译器。


只要您使用的是不错的优化编译器,只需编写易于编译器理解的代码即可。这使编译器更容易执行聪明的优化。

您询问此问题表示优化编译器比您更了解优化。因此,请信任编译器。使用n * 3

也看一下这个答案。


不在乎。我认为还有更多重要的事情需要优化。您花了多少时间思考和编写该问题,而不是自己进行编码和测试?

:-)


大多数编译器足够聪明,可以将整数乘法分解为一系列的移位和加法运算。我对Windows编译器一无所知,但是至少使用gcc可以使它吐出汇编器,如果您看一下,就两种编写方式而言,您可能会看到相同的汇编器。


它确实取决于您实际使用的编译器,但是很可能它们会转换为相同的代码。

您可以通过创建一个小的测试程序并检查其拆卸情况来自己检查它。


相信您的编译器可以优化诸如此类的小段代码。在代码级别,可读性更为重要。真正的优化应该更高层次。


编译器擅长优化您的代码。任何现代编译器在这两种情况下都会产生相同的代码,并用左移代替* 2


推荐阅读