Warum implementiert GCC isnan() effizienter für C++ <cmath> als für C <math.h>?

 C Programming >> C-Programmierung >  >> Tags >> GCC
Warum implementiert GCC isnan() effizienter für C++ <cmath> als für C <math.h>?


Hier ist mein Code:


int f(double x)
{
return isnan(x);
}

Wenn ich #include <cmath> Ich bekomme diese Assembly:


xorl    %eax, %eax
ucomisd %xmm0, %xmm0
setp %al

Das ist ziemlich schlau:ucomisd setzt das Paritäts-Flag, wenn der Vergleich von x mit sich selbst ungeordnet ist, was bedeutet, dass x NAN ist. Dann kopiert setp das Parity-Flag in das Ergebnis (nur ein einzelnes Byte, daher das anfängliche Löschen von %eax ).


Aber wenn ich #include <math.h> Ich bekomme diese Assembly:


jmp     __isnan

Jetzt ist der Code nicht inline und der __isnan Funktion ist sicherlich nicht schneller als die ucomisd Anweisung, also haben wir einen Sprung ohne Nutzen angefallen. Ich bekomme dasselbe, wenn ich den Code als C kompiliere.


Wenn ich jetzt den isnan() ändere Aufruf an __builtin_isnan() , bekomme ich das einfache ucomisd Anweisung Anweisung unabhängig davon, welchen Header ich einfüge, und es funktioniert auch in C. Ebenso, wenn ich nur return x != x .


Meine Frage ist also, warum der C <math.h> Header bieten eine weniger effiziente Implementierung von isnan() als der C++ <cmath> Header? Wird wirklich erwartet, dass die Leute __builtin_isnan() verwenden , und wenn ja, warum?


Ich habe GCC 4.7.2 und 4.9.0 auf x86-64 mit -O2 getestet und -O3 Optimierung.


Antworten:


Betrachten wir <cmath> für libstdc++, das mit gcc 4.9 ausgeliefert wird, erhalten Sie Folgendes:


  constexpr bool
isnan(double __x)
{ return __builtin_isnan(__x); }

Ein constexpr Die Funktion könnte aggressiv inliniert werden, und natürlich delegiert die Funktion die Arbeit einfach an __builtin_isnan .


Die <math.h> Kopfzeile verwendet __builtin_isnan nicht , sondern verwendet einen __isnan Implementierung, die etwas lang ist, um sie hier einzufügen, aber es sind die Zeilen 430 von math.h auf meiner Maschine™. Da der C99-Standard die Verwendung eines Makros für isnan erfordert et al (Abschnitt 7.12 des C99-Standards) wird die „Funktion“ wie folgt definiert:


#define isnan(x) (sizeof (x) == sizeof (float) ? __isnanf (x)   \
: sizeof (x) == sizeof (double) ? __isnan (x) \
: __isnanl (x))

Ich sehe jedoch keinen Grund, warum es nicht __builtin_isnan verwenden kann statt __isnan daher vermute ich ein Versehen. Wie Marc Glisse in den Kommentaren anmerkt, gibt es einen relevanten Fehlerbericht für ein ähnliches Problem mit isinf statt isnan .