GCC标志禁用算术优化

5

gcc/g++有启用或禁用算术优化的标志吗?例如,当a是整数时,a+a+...+a被替换为n*a。特别地,在使用-O2-O3时,这是否可以禁用?

在下面的示例中,即使使用-O0,加法运算也会被替换为单个乘法运算:

$ cat add1.cpp
unsigned int multiply_by_22(unsigned int a)
{
    return a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a;
}

$ g++ -S -masm=intel -O0 add1.cpp

$ cat add1.s
...
        imul    eax, edi, 22

即使禁用了-O0中使用的所有标志(参见 g++ -c -Q -O0 --help=optimizers | grep enabled),仍会产生imul操作。

当添加循环时,需要使用-O1将重复的加法简化为单个乘法:

$ cat add2.cpp
unsigned int multiply(unsigned int a, unsigned int b)
{
    unsigned int sum=0;
    for(unsigned int i=0; i<b; i++)
        sum += a;
    return sum;
}

$ g++ -S -masm=intel -O1 add2.cpp

$ cat add2.s
...
        mov     eax, 0
.L3:
        add     eax, 1
        cmp     esi, eax
        jne     .L3
        imul    eax, edi
        ret

-O1sum += a; 移到循环外并用单个乘法代替。 使用 -O2 也会删除死循环。

我只是出于兴趣在尝试计时一些基本整数操作时发现编译器优化了我的循环,而我无法找到任何禁用此功能的标志。


我正在寻找一些可能强制执行你所说的行为的编译指示,但我只找到了 https://dev59.com/CXE95IYBdhLWcg3wp_kg 这个看起来不错... 但所有答案都解释了如何在本地设置 -O0,而我们知道这对你来说是不够的。 - Roberto Caboni
大多数情况下没有这样的标志。您可以修改代码(添加volatile,将操作放在单独的语句中)。对于有符号类型,未定义的清洁器也可能防止一些优化。 - Marc Glisse
1
为什么你想要计时一些在真实代码中从未发生的事情? - n. m.
1
我只是为了时间而使用 addimulidiv 等操作,我知道有一些很好的在线手册,比如 https://www.agner.org/optimize/instruction_tables.pdf,但在本地复制总是很不错的 :) - user1059432
1
如果您想计时某些汇编指令,请用汇编语言编写这些确切的汇编指令。 - n. m.
显示剩余3条评论
2个回答

5

我不知道这样的编译器标志。

或许你可以尝试使用volatile作为替代:

unsigned int multiply_by_22(volatile unsigned int a)
{
    return a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a;
}

使用 -O0 选项,你会得到:

push    rbp
mov     rbp, rsp
mov     DWORD PTR [rbp-4], edi
mov     edx, DWORD PTR [rbp-4]
mov     eax, DWORD PTR [rbp-4]
add     edx, eax
mov     eax, DWORD PTR [rbp-4]
add     edx, eax
mov     eax, DWORD PTR [rbp-4]
add     edx, eax
mov     eax, DWORD PTR [rbp-4]
add     edx, eax
mov     eax, DWORD PTR [rbp-4]
add     edx, eax
mov     eax, DWORD PTR [rbp-4]
add     edx, eax
mov     eax, DWORD PTR [rbp-4]
add     edx, eax
mov     eax, DWORD PTR [rbp-4]
add     edx, eax
mov     eax, DWORD PTR [rbp-4]
add     edx, eax
mov     eax, DWORD PTR [rbp-4]
add     edx, eax

etc...

若要查看使用-O2或-O3优化选项生成的代码,请访问:https://godbolt.org/z/Bk2b6Z


volatile 肯定是有效的,但缺点是即使使用 -O2,它也不会被存储在 CPU 寄存器中,总是需要内存访问。 - user1059432
@user1059432 是的,我同意。 - Picaud Vincent
请注意,clang 不需要 volatile-O0 禁用乘法,但仍将 edi 存储到内存 [ebp-4] 中以执行加法。在 -O1,它通过 2 个 lea 和一个 add 优化了加法而不使用 imul 指令。使用 volatile 的情况下,gcc-O1-O2 生成的代码非常糟糕。 - chqrlie
1
@user1059432,为什么你在担心寄存器中没有存储值,当你试图将输出二进制最小化时? - phuclv
@phuclv 因为我对例如 add 的时间和像 mov eax, DWORD PTR [rbp-4] 这样的慢速内存访问的时间感兴趣,这会扭曲结果。 - user1059432
1
在这种情况下,这是关于微基准测试而不是编译器优化。汇编或内联汇编是解决方案。 - phuclv

2

如果没有编译器标志,我只看到两种强制执行add的方法:

  • 编写更复杂的加法序列,编译器无法进行优化,例如斐波那契数列(尽管这会很快超出范围):
$ cat fibonacci.cpp
unsigned int fibonacci(unsigned int ops)
{
    unsigned int a=1;
    unsigned int b=1;
    for(unsigned int i=0; i<ops/2; i++) {
        a+=b;
        b+=a;
    }
    return b;
}

$ g++ -Wall -S -masm=intel -O3 --unroll-loops fibonacci.cpp

$ cat fibonacci.s
...
.L3:
        add     edx, eax
        add     ecx, 8
        add     eax, edx
        add     edx, eax
        add     eax, edx
        add     edx, eax
        add     eax, edx
        add     edx, eax
        add     eax, edx
        add     edx, eax
        add     eax, edx
        add     edx, eax
        add     eax, edx
        add     edx, eax
        add     eax, edx
        add     edx, eax
        add     eax, edx
        cmp     ecx, edi
        jne     .L3
  • 编写一个汇编程序,用于生成add操作:
unsigned int multiply_by_5(unsigned int a)
{
   unsigned int sum = 0;
   asm ( "# start multiply_by_5\n\t"
         "movl %1, %%ebx\n\t"           // ebx = a
         "movl $0, %%eax\n\t"           // eax = 0 (sum = 0)
         "addl %%ebx, %%eax\n\t"        // eax += ebx (sum+=a)
         "addl %%ebx, %%eax\n\t"        // eax += ebx (sum+=a)
         "addl %%ebx, %%eax\n\t"        // eax += ebx (sum+=a)
         "addl %%ebx, %%eax\n\t"        // eax += ebx (sum+=a)
         "addl %%ebx, %%eax\n\t"        // eax += ebx (sum+=a)
         "movl %%eax, %0\n\t"           // sum = eax
         "# end multiply_by_5\n"
         : "=m" (sum) : "m" (a) : "%eax", "%ebx");
   return sum;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接