【问题标题】:gcc flags to disable arithmetic optimisations禁用算术优化的 gcc 标志
【发布时间】:2020-04-02 00:16:07
【问题描述】:

gcc/g++ 是否具有启用或禁用算术优化的标志,例如当a 是整数时,a+a+...+an*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

-O1 已将 sum += a; 移到循环之外,并用一个乘法替换它。使用-O2,它也会移除死循环。

我只是出于兴趣而询问一些基本的整数运算,并注意到编译器优化了我的循环,我找不到任何标志来禁用它。

【问题讨论】:

  • 我正在寻找一些可能会强制执行您所说的行为的编译指示,但我只发现 stackoverflow.com/questions/2219829/… 听起来不错......但所有答案都解释了如何在本地设置 -O0,并且我们知道这对你来说还不够。
  • 大多没有这样的标志。您可以修改代码(添加 volatile,将操作放在单独的语句中)。对于有符号类型,未定义的清理程序也可能会阻止一些优化。
  • 为什么要对在真实代码中从未发生过的事情进行计时?
  • @n.'pronouns'm。纯粹是为了时间addimulidiv 操作等,我知道网上有一些很好的手册,比如agner.org/optimize/instruction_tables.pdf,但在本地复制总是很好:)
  • 如果你想对某些汇编指令计时,请将这些准确的汇编指令写在汇编中。

标签: c gcc x86 compiler-optimization microbenchmark


【解决方案1】:

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

也许您可以尝试使用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 是的,我同意这一点。
  • 请注意clang 不需要volatile 来禁用-O0 处的乘法运算,但仍将edi 存储到内存[ebp-4] 以执行加法运算。在-O1,它优化了没有imul 指令的加法,有2 个lea 和一个addgcc-O1-O2 生成的代码带有volatile 是可怕的。
  • @user1059432 当您尝试对输出二进制文件进行悲观时,您为什么担心值没有存储在寄存器中?
  • @user1059432 在这种情况下,这是关于微基准测试,而不是关于编译器优化。汇编或内联汇编是解决方案
【解决方案2】:

在没有任何编译器标志的情况下,我只看到两个选项可以强制执行add

  • 编写一系列编译器无法优化的更复杂的添加,例如Fibbonaci series(虽然这会很快溢出):
$ 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-30
    • 2011-07-30
    • 1970-01-01
    • 2010-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    相关资源
    最近更新 更多