簡體   English   中英

-ffast-math 的奇怪 LTO 行為

[英]Weird LTO behavior with -ffast-math

概括

最近我遇到了一個關於 LTO 和-ffast-math的奇怪問題,我的“pow”(在cmath中)調用的結果不一致,具體取決於是否使用了-flto

環境:

$ g++ --version
g++ (GCC) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ll /lib64/libc.so.6
lrwxrwxrwx 1 root root 12 Sep  3  2019 /lib64/libc.so.6 -> libc-2.17.so

$ ll /lib64/libm.so.6
lrwxrwxrwx 1 root root 12 Sep  3  2019 /lib64/libm.so.6 -> libm-2.17.so

$ cat /etc/redhat-release 
CentOS Linux release 7.5.1804 (Core) 

最小的例子

代碼

  • fixed.hxx
#include <cstdint>
double Power10f(const int16_t power);
  • fixed.cxx
#include "fixed.hxx"
#include <cmath>

double Power10f(const int16_t power)
{
    return pow(10.0, (double) power);
}
  • test.cxx
#include <iostream>
#include <cmath>
#include <iomanip>
#include <cstdint>
#include "fixed.hxx"

int main(int argc, char** argv)
{
    if (argc >= 3) {
        int64_t value = (int64_t)atoi(argv[1]);
        int16_t power = (int16_t)atoi(argv[2]);
        double x = Power10f(power);
        std::cout.precision(17);
        std::cout << std::scientific << x << std::endl;
        std::cout << std::scientific << (double)value * x << std::endl;
        return 0;   
    }
    return 1;
}

編譯並運行

使用-ffast-math編譯它,使用/不使用-flto會給出不同的結果

  • 使用-flto最終將調用__pow_finite版本並給出“准確”的結果:
$ g++ -O3 -DNDEBUG -ffast-math -std=c++17 -flto  -o fixed.cxx.o -c fixed.cxx
$ g++ -O3 -DNDEBUG   -o fdtest fixed.cxx.o test.cxx
$ ./fdtest 81 20
1.00000000000000000e+20
8.10000000000000000e+21
$ objdump -DC fdtest > fdtest.dump
$ cat fdtest.dump
...
0000000000400930 <Power10f(short)>:
  400930:       0f bf ff                movswl %di,%edi
  400933:       66 0f ef c9             pxor   %xmm1,%xmm1
  400937:       f2 0f 10 05 99 00 00    movsd  0x99(%rip),%xmm0        # 4009d8 <_IO_stdin_used+0x8>
  40093e:       00 
  40093f:       f2 0f 2a cf             cvtsi2sd %edi,%xmm1
  400943:       e9 d8 fd ff ff          jmpq   400720 <__pow_finite@plt>
  400948:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40094f:       00
...
  • 沒有-flto最終會調用__exp_finite (如果我猜對了,作為-ffast-math啟用的優化),並給出“不准確”的結果。
$ g++ -O3 -DNDEBUG -ffast-math -std=c++17  -o fixed.cxx.o -c fixed.cxx
$ g++ -O3 -DNDEBUG   -o fdtest fixed.cxx.o test.cxx
$ ./fdtest 81 20
1.00000000000000786e+20
8.10000000000006396e+21
$ objdump -DC fdtest > fdtest.dump
$ cat fdtest.dump
...
0000000000400930 <Power10f(short)>:
  400930:       0f bf ff                movswl %di,%edi
  400933:       66 0f ef c0             pxor   %xmm0,%xmm0
  400937:       f2 0f 2a c7             cvtsi2sd %edi,%xmm0
  40093b:       f2 0f 59 05 95 00 00    mulsd  0x95(%rip),%xmm0        # 4009d8 <_IO_stdin_used+0x8>
  400942:       00 
  400943:       e9 88 fd ff ff          jmpq   4006d0 <__exp_finite@plt>
  400948:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40094f:       00
...

問題

上述示例是預期行為還是我的代碼有問題導致了這種意外行為?

更新

在其他一些平台上也可以觀察到相同的結果(例如,帶有 g++ 12.1 和 glibc 2.35 的 ArchLinux)。

男人 gcc:

要使用鏈接時優化器,應在編譯時和最終鏈接期間指定-flto和優化選項。 建議您使用相同的選項編譯參與同一鏈接的所有文件,並在鏈接時指定這些選項。 例如:

 gcc -c -O2 -flto foo.c gcc -c -O2 -flto bar.c gcc -o myprog -flto -O2 foo.o bar.o

-ffast-math允許編譯器因任何原因而不一致。 由於選擇了不同的優化策略,即使在函數中修改概念上不相關的代碼也很容易導致pow返回不同的結果。 並且-flto在如何/何時完成優化方面發生了相當大的變化,因此有很大的空間可以實現。

如果您關心數值精度、數值一致性或一般數值,請不要使用-ffast-math 它執行的轉換通常可供您作為程序員使用,如果您自己進行,則可以依賴它們的一致性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM