计算化学公社

 找回密码 Forget password
 注册 Register
Views: 12729|回复 Reply: 8
打印 Print 上一主题 Last thread 下一主题 Next thread

[编译与编译器] 一个与编译器机制有关的bug:NaN,fast-math

[复制链接 Copy URL]

220

帖子

8

威望

3082

eV
积分
3462

Level 5 (御坂)

跳转到指定楼层 Go to specific reply
楼主
本帖最后由 coolrainbow 于 2021-6-17 06:39 编辑

今天本人在写程序时遇上了一个难以发现的bug。经过层层函数的插桩最后发现与NaN以及编译器的机制有关,特此记录以供参考。

(1) 什么是NaN

NaN是IEEE754浮点数运算标准中的一个定义。通俗的说,一个实数范围内无意义的计算被定义为返回一个“非数”,即NaN (Not a number),比如负数开平方,负数取对数,绝对值大于1的数取反三角函数等。NaN有个奇异的规定,即它不等于自己,也就是说NaN != NaN。因此,它可以用来判断一个实数是否是非数。比如一个real(kind=4) 或者double 型的x , x==x为真,则x是个普通的实数;否则,它就是NaN。另外,Fortran和C++中都有个专门的函数 isnan() 来判断是否为NaN。

(2) 代码bug

我遇到的NaN的bug可以简化成如下的Fortran代码(nan.f90):

program main
    real(kind=4) x
        x = -1
        x = x**(0.5)
        write (*, *) "x = ",x
        if (x == x) then
            write (*, *) "x is an actual number."
        else
            write (*, *) "x is not a number."
        endif
end

可以预见,因为x是个负数开平方,所以最后应该得到一个NaN,并且输出“x is not a number.”。 简单编译运行后确实如此:

$ gfortran nan.f90
$ ./a
x =               NaN
x is not a number.

然而,在我的程序中,为了程序的运行效率,添加了一个激进的、不安全的编译选项:fast-math。顾名思义,就是快速的数学运算。如果你编译过NAMD,就会发现fast-math是一个默认的编译选项,可以极大的提升计算速度。在我这里也是如此。然而,用这个选项编译上面的程序,却带来了错误的运行结果:

$ gfortran nan.f90 -ffast-math
$ ./a
x =               NaN
x is an actual number.

x明明已经是NaN了,为什么下面却判断错了?难道是编译器的bug?

当然不是。虽然intel编译器的bug满天飞,但是GNU编译器发展到现在已经无比稳定,一般只有在极其复杂的C++语法中才能发现bug,Fortran编译器发现bug的概率极低。经过查阅资料,这才发现:fast-math为了提升效率,会假定所有的浮点是都是实数,不会出现NaN,Inf之类的情况。所以isnan()函数和x==x这类技巧都会失效。这就是NaN判断失败的原因!

bug原因找到了也就好解决了。编译器手册指出,加上一个选项no-finite-math-only就可以撤销只有实数的假定。

$ gfortran nan.f90 -ffast-math -fno-finite-math-only
$ ./a
x =               NaN
x is not a number.

(3) 总结

这个特征在GNU的gfortran,gcc,g++编译器都存在,解决方法就是在fast-math后面加no-finite-math-only。由此可见,在程序中使用不安全的优化选项时一定要慎重,虽然大多数情况下没什么问题,但一旦有问题就是难以排查的bug。所以在编写复杂程序时,可以先关闭不安全的编译选项进行测试,最后再使用激进优化,以保证程序的安全和正确。



评分 Rate

参与人数
Participants 4
威望 +1 eV +13 收起 理由
Reason
北大-陶豫 + 5 谢谢分享
wangxubo + 5
hebrewsnabla + 3
sobereva + 1

查看全部评分 View all ratings

220

帖子

8

威望

3082

eV
积分
3462

Level 5 (御坂)

9#
 楼主 Author| 发表于 Post on 2021-6-18 10:17:24 | 只看该作者 Only view this author
sobereva 发表于 2021-6-17 12:09
-O3我都不用,比起-O2提升很有限或者几乎没任何提升(取决于代码),但却会带来很多危险

我写程序一般都 ...

根据经验,gnu系列的,gfortran,gcc的O3还是可靠的

intel的O3个别情况下还没O0快,不少时候编译都不过,必须改成O2。 老bug了。

评分 Rate

参与人数
Participants 1
eV +2 收起 理由
Reason
卡开发发 + 2 我很赞同

查看全部评分 View all ratings

306

帖子

2

威望

3251

eV
积分
3597

Level 5 (御坂)

8#
发表于 Post on 2021-6-17 13:14:12 | 只看该作者 Only view this author
本帖最后由 lyj714 于 2021-6-17 13:28 编辑

mark

5万

帖子

99

威望

5万

eV
积分
112354

管理员

公社社长

7#
发表于 Post on 2021-6-17 12:09:42 | 只看该作者 Only view this author
-O3我都不用,比起-O2提升很有限或者几乎没任何提升(取决于代码),但却会带来很多危险

我写程序一般都是先用debug模式跑一遍。有些-O1/-O2状态下不会报错的危险情况在debug情况下都会报错

北京科音自然科学研究中心http://www.keinsci.com)致力于计算化学的发展和传播,长期开办高质量的各种计算化学类培训:初级量子化学培训班中级量子化学培训班高级量子化学培训班量子化学波函数分析与Multiwfn程序培训班分子动力学与GROMACS培训班CP2K第一性原理计算培训班,内容介绍以及往届资料购买请点击相应链接查看。这些培训是计算化学从零快速入门以及进一步全面系统性提升研究水平的高速路!培训各种常见问题见《北京科音办的培训班FAQ》
欢迎加入“北京科音”微信公众号获取北京科音培训的最新消息、避免错过网上有价值的计算化学文章!
欢迎加入人气非常高、专业性特别强的综合性理论与计算化学交流QQ群“思想家公社QQ群”:1号:18616395,2号:466017436,3号:764390338,搜索群号能搜到哪个说明目前哪个能加,合计9000人。北京科音培训班的学员在群中可申请VIP头衔,提问将得到群主Sobereva的最优先解答。
思想家公社的门口Blog:http://sobereva.com(发布大量原创计算化学相关博文)
Multiwfn主页:http://sobereva.com/multiwfn(十分强大的量子化学波函数分析程序)
ResearchGate:https://www.researchgate.net/profile/Tian_Lu
Money and papers are rubbish, get a real life!

2301

帖子

1

威望

5473

eV
积分
7794

Level 6 (一方通行)

6#
发表于 Post on 2021-6-17 10:20:19 | 只看该作者 Only view this author
本帖最后由 abin 于 2021-6-17 10:22 编辑
coolrainbow 发表于 2021-6-17 09:53
数值错误还可以通过O0之类的来排查,有时候gnu能编译的,intel就完全编译不过去:让人一点脾气也没有

是呀,
比如libxc v5.1.4GNU编译没有任何问题。
换到intel,就各种错误。
好不容易搞过去了, 结果cp2k测试算例,一堆错误。

头疼。
High-Performance Computing for You
为您专属定制的高性能计算解决方案

更多讯息,请访问:
https://labitc.top
http://tophpc.top:8080
电邮: ask@hpc4you.top

220

帖子

8

威望

3082

eV
积分
3462

Level 5 (御坂)

5#
 楼主 Author| 发表于 Post on 2021-6-17 09:53:52 | 只看该作者 Only view this author
abin 发表于 2021-6-17 08:46
对应的,在intel编译器套件中,
如果使用-O3来优化,
也需要做类似的处理。在intel手册中有提及。

数值错误还可以通过O0之类的来排查,有时候gnu能编译的,intel就完全编译不过去:让人一点脾气也没有

220

帖子

8

威望

3082

eV
积分
3462

Level 5 (御坂)

4#
 楼主 Author| 发表于 Post on 2021-6-17 09:46:45 | 只看该作者 Only view this author
snljty 发表于 2021-6-17 08:53
这个feature是否出现似乎还和编译编译器时候的选项、环境等有关,刚试了几个版本,某个版本下加-ffast-math ...

是的,在Windows的Mingw C++和Linux下的g++也会表现不同,所以看来跟具体实现有关。总之这种危险的优化一定要多多测试。

1187

帖子

5

威望

2841

eV
积分
4129

Level 6 (一方通行)

3#
发表于 Post on 2021-6-17 08:53:52 | 只看该作者 Only view this author
这个feature是否出现似乎还和编译编译器时候的选项、环境等有关,刚试了几个版本,某个版本下加-ffast-math不加-fno-finite-math-only,C语言的程序,gcc编译,isnan(x)没有问题,x != x结果就不对。换个版本,现象又不一样了。

2301

帖子

1

威望

5473

eV
积分
7794

Level 6 (一方通行)

2#
发表于 Post on 2021-6-17 08:46:00 | 只看该作者 Only view this author
对应的,在intel编译器套件中,
如果使用-O3来优化,
也需要做类似的处理。在intel手册中有提及。

看到很多网友推荐的vasp或者cp2k编译中,仅仅单独使用了-O3,
这是非常危险的。
根据intel手册描述,
“-O3 Enables more aggressive loop and memory access optimizations— such as scalar replacement—loop unrolling, code replication to eliminate branches, loop blocking to allow more efficient use of cache and additional data prefetching. This level of optimization also includes a setting akin to GNU’s -ffast-math and so comes with all the attendant warnings about IEEE floating- point compliance.”

换成白话文就是,英特尔工具链,
仅仅使用-O3,可能带来数值错误。

评分 Rate

参与人数
Participants 1
eV +3 收起 理由
Reason
灰飞的旋律 + 3 我很赞同

查看全部评分 View all ratings

High-Performance Computing for You
为您专属定制的高性能计算解决方案

更多讯息,请访问:
https://labitc.top
http://tophpc.top:8080
电邮: ask@hpc4you.top

手机版 Mobile version|北京科音自然科学研究中心 Beijing Kein Research Center for Natural Sciences|京公网安备 11010502035419号|计算化学公社 — 北京科音旗下高水平计算化学交流论坛 ( 京ICP备14038949号-1 )|网站地图

GMT+8, 2024-11-23 21:58 , Processed in 0.174101 second(s), 23 queries , Gzip On.

快速回复 返回顶部 返回列表 Return to list