为啥 GCC 为 C++ <cmath> 实现 isnan() 比 C <math.h> 更有效?
Posted
技术标签:
【中文标题】为啥 GCC 为 C++ <cmath> 实现 isnan() 比 C <math.h> 更有效?【英文标题】:Why does GCC implement isnan() more efficiently for C++ <cmath> than C <math.h>?为什么 GCC 为 C++ <cmath> 实现 isnan() 比 C <math.h> 更有效? 【发布时间】:2014-11-21 01:21:12 【问题描述】:这是我的代码:
int f(double x)
return isnan(x);
如果我 #include <cmath>
我得到这个程序集:
xorl %eax, %eax
ucomisd %xmm0, %xmm0
setp %al
这相当聪明:ucomisd 如果 x 与自身的比较是无序的,则设置奇偶校验标志,这意味着 x 是 NAN。然后setp 将奇偶校验标志复制到结果中(只有一个字节,因此%eax
的初始清除)。
但如果我 #include <math.h>
我得到这个程序集:
jmp __isnan
现在代码不是内联的,__isnan
函数肯定没有ucomisd
指令快,所以我们进行了一次跳转,没有任何好处。如果我将代码编译为 C,我会得到同样的结果。
现在,如果我将isnan()
调用更改为__builtin_isnan()
,无论我包含哪个标头,我都会得到简单的ucomisd
指令指令,并且它也适用于C。同样,如果我只是 return x != x
.
所以我的问题是,为什么 C <math.h>
标头提供的 isnan()
实现效率低于 C++ <cmath>
标头?人们真的希望使用__builtin_isnan()
,如果是,为什么?
我在 x86-64 上使用 -O2
和 -O3
优化测试了 GCC 4.7.2 和 4.9.0。
【问题讨论】:
这是我的推测:c99之前,c中没有内联函数。没有内联函数意味着函数必须由 jmp/call(或某种分支)调用。 __builtin_isnan 不是 c 的一部分。它可能是特定于平台的内在因素。 但像<math.h>
这样的系统头文件肯定可以使用特定于平台的内置插件。
我很确定isnan
会尽可能使用__builtin_isnan
。我认为您没有理由必须手动调用它。
也许当 C99 出现时,没有人想过要回去更新 isnan
sourceware.org/bugzilla/show_bug.cgi?id=15367
【参考方案1】:
查看<cmath>
与 gcc 4.9 一起提供的 libstdc++,你会得到:
constexpr bool
isnan(double __x)
return __builtin_isnan(__x);
constexpr
函数可以被积极内联,当然,该函数只是将工作委托给__builtin_isnan
。
<math.h>
标头不使用__builtin_isnan
,而是使用__isnan
实现,在此处粘贴有点长,但它在我的机器™ 上是math.h
的第430 行。由于 C99 标准要求为 isnan
等人使用宏(C99 标准的第 7.12 节),因此“函数”定义如下:
#define isnan(x) (sizeof (x) == sizeof (float) ? __isnanf (x) \
: sizeof (x) == sizeof (double) ? __isnan (x) \
: __isnanl (x))
但是,我看不出它为什么不能使用__builtin_isnan
而不是__isnan
,所以我怀疑这是一个疏忽。正如 Marc Glisse 在 cmets 中指出的那样,有一个类似问题的 relevant bug report 使用 isinf
而不是 isnan
。
【讨论】:
其实这个bug是关于isinf的。这是一个不同功能的类似问题,但严格来说不是同一个问题。 不要忘记包含this,表示标准要求它们是宏。 您认为将<math.h>
改为简单地说#define isnan(x) __builtin_isnan(x)
是否合法?
@JohnZwinck 是的。我想不出它不应该有效的任何理由。
对于 gcc,它是有效的。 __builtin_isnan 并非在每个编译器上都有。以上是关于为啥 GCC 为 C++ <cmath> 实现 isnan() 比 C <math.h> 更有效?的主要内容,如果未能解决你的问题,请参考以下文章