计算化学公社

 找回密码 Forget password
 注册 Register
Views: 9071|回复 Reply: 8

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

[复制链接 Copy URL]

220

帖子

8

威望

3030

eV
积分
3410

Level 5 (御坂)

发表于 Post on 2021-6-17 06:28:33 | 显示全部楼层 Show all |阅读模式 Reading model
本帖最后由 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

1852

帖子

1

威望

4098

eV
积分
5970

Level 6 (一方通行)

发表于 Post on 2021-6-17 08:46:00 | 显示全部楼层 Show all
对应的,在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
为您专属定制的高性能计算解决方案

更多讯息,请访问:
App,  https://gitee.com/hpc4you/linux
hpc4you toolkit,  https://gitee.com/hpc4you/hpc
电邮hpc4you@163.com VX: hpc4you

1188

帖子

5

威望

2758

eV
积分
4046

Level 6 (一方通行)

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

220

帖子

8

威望

3030

eV
积分
3410

Level 5 (御坂)

 楼主 Author| 发表于 Post on 2021-6-17 09:46:45 | 显示全部楼层 Show all
snljty 发表于 2021-6-17 08:53
这个feature是否出现似乎还和编译编译器时候的选项、环境等有关,刚试了几个版本,某个版本下加-ffast-math ...

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

220

帖子

8

威望

3030

eV
积分
3410

Level 5 (御坂)

 楼主 Author| 发表于 Post on 2021-6-17 09:53:52 | 显示全部楼层 Show all
abin 发表于 2021-6-17 08:46
对应的,在intel编译器套件中,
如果使用-O3来优化,
也需要做类似的处理。在intel手册中有提及。

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

1852

帖子

1

威望

4098

eV
积分
5970

Level 6 (一方通行)

发表于 Post on 2021-6-17 10:20:19 | 显示全部楼层 Show all
本帖最后由 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
为您专属定制的高性能计算解决方案

更多讯息,请访问:
App,  https://gitee.com/hpc4you/linux
hpc4you toolkit,  https://gitee.com/hpc4you/hpc
电邮hpc4you@163.com VX: hpc4you

4万

帖子

99

威望

4万

eV
积分
89888

管理员

公社社长+计算化学玩家

发表于 Post on 2021-6-17 12:09:42 | 显示全部楼层 Show all
-O3我都不用,比起-O2提升很有限或者几乎没任何提升(取决于代码),但却会带来很多危险

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

北京科音自然科学研究中心http://www.keinsci.com)致力于计算化学的发展和传播,长期开办最高质量的各种计算化学类培训:初级量子化学培训班基础(中级)量子化学培训班分子动力学与GROMACS培训班量子化学波函数分析与Multiwfn程序培训班,内容介绍以及往届资料购买请点击链接查看。这些培训是计算化学从零快速入门以及进一步全面系统性提升研究水平的最佳途径。培训各种常见问题见《北京科音办的培训班FAQ》
欢迎加入“北京科音”微信公众号获取培训最新消息、避免错过网上最有价值的计算化学文章!
欢迎加入人气最高、水准最高的综合性理论与计算化学交流QQ群“思想家公社QQ群”:1号:18616395,2号:466017436,3号:764390338,搜索群号能搜到哪个说明目前哪个能加,合计9000人,讨论范畴相同
思想家公社的门口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!

220

帖子

1

威望

1765

eV
积分
2005

Level 5 (御坂)

发表于 Post on 2021-6-17 13:14:12 | 显示全部楼层 Show all
本帖最后由 lyj714 于 2021-6-17 13:28 编辑

mark

220

帖子

8

威望

3030

eV
积分
3410

Level 5 (御坂)

 楼主 Author| 发表于 Post on 2021-6-18 10:17:24 | 显示全部楼层 Show all
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

本版积分规则 Credits rule

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

GMT+8, 2023-2-2 23:29 , Processed in 0.188244 second(s), 22 queries .

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