5

Ich untersuchte einen Code, der die /fp:precise- und /fp:fast-Flags verwendet.Seltsames/fp Gleitkomma-Modellflaggenverhalten

Nach dem MSDN documentation für /fp:precise:

mit/fp: präzise auf x86-Prozessoren, der Compiler auf Variablen vom Typ float auf die richtige Präzision für Zuweisungen und Abgüsse Abrunden wird zuführen und, wenn Parameter in einer Weitergabe Funktion. Diese Rundung garantiert, dass die Daten keine größere Bedeutung als die Kapazität ihres Typs behalten. Ein mit/fp: precise kompiliertes Programm kann langsamer und größer als eins sein, das ohne/fp: precise kompiliert wurde./fp: präzise deaktiviert intrinsics; Die Routinen der Standardlaufzeitbibliothek werden stattdessen verwendet. Weitere Informationen finden Sie unter/Oi (Intrinsische Funktionen generieren).

Mit Blick auf die Demontage eines Anrufs zu sqrtf (mit /arch:SSE2 genannt, Ziel x86/Win32 Plattform):

0033185D cvtss2sd xmm0,xmm1 
00331861 call  __libm_sse2_sqrt_precise (0333370h) 
00331866 cvtsd2ss xmm0,xmm0 

Von this question I modernen x86/x64-Prozessoren glauben nicht 80-Bit-Register verwenden Sie (oder zumindest ihre Verwendung abschrecken), so dass der Compiler das tut, was ich für das nächstbeste halten würde, und Berechnungen mit 64-Bit-Doubles mache. Und weil intrinsics deaktiviert sind, gibt es einen Aufruf an eine Bibliothek sqrtf-Funktion.

Ok, fairerweise scheint dies zu entsprechen, was die Dokumentation sagt.

Allerdings, wenn ich für die x64 Bogen kompilieren, geschieht etwas Seltsames:

000000013F2B199E movups  xmm0,xmm1 
000000013F2B19A1 sqrtps  xmm1,xmm1 
000000013F2B19A4 movups  xmmword ptr [rcx+rax],xmm1 

Die Berechnungen werden nicht mit 64-Bit-Doppel durchgeführt und Spezifika verwendet werden. Soweit ich das beurteilen kann, sind die Ergebnisse genau so, als ob das /fp:fast Flag verwendet würde.

Warum gibt es eine Diskrepanz zwischen den beiden? Funktioniert /fp:precise einfach nicht mit der x64-Plattform?

Nun, als eine Überprüfung der Gesundheit getestet habe ich den gleichen Code in VS2010 x86 mit /fp:precise und /arch:SSE2 getestet. Überraschenderweise wurde die sqrtpd intrinsische verwendet!

00AF14C7 cvtps2pd xmm0,xmm0 
00AF14CA sqrtsd  xmm0,xmm0 
00AF14CE cvtpd2ps xmm0,xmm0 

Was ist hier los? Warum verwendet VS2010 Intrinsics, während VS2012 eine Systembibliothek aufruft?

Das Testen von VS2010 mit Ausrichtung auf die x64-Plattform hat ähnliche Ergebnisse wie VS2012 (/fp:precise scheint ignoriert zu werden).

Ich habe keinen Zugriff auf ältere Versionen von VS, so dass ich keine Tests auf diesen Plattformen durchführen kann.

Als Referenz teste ich in Windows 7 64-Bit mit einem Intel i5-M430-Prozessor.

+2

Das ist wirklich seltsam. Ich weiß aus der Tatsache, dass '/ fp: precise' manchmal dazu führt, dass der Compiler Intermediates mit höherer Präzision diskret unterstützt. Aber das erklärt nicht die schiere Unstimmigkeit hier. – Mysticial

+0

"Von dieser Frage glaube ich, dass der x86-Arch keine 80-Bit-Register hat" Komm schon wieder? –

+0

Ja, seltsame Formulierung. Aktualisiert, um die allgemeine Empfehlung gegen ihre Verwendung zu klären. – helloworld922

Antwort

3

Zunächst einmal sollten Sie lesen this wirklich guten Blog-Post über Zwischen Gleitkomma-Genauigkeit. Der Artikel behandelt nur Visual Studio generierten Code (aber darum geht es bei Ihrer Frage).Und nun zu den Beispielen:

0033185D cvtss2sd xmm0,xmm1 
00331861 call  __libm_sse2_sqrt_precise (0333370h) 
00331866 cvtsd2ss xmm0,xmm0 

Dieser Assembler-Code wurde mit /fp:precise /arch:SSE2 für die x86-Plattform generiert. Nach der documentation, fördert das genaue Fließkomma-Modell alle Berechnungen zu intern auf der x86-Plattform zu verdoppeln. Es verhindert auch die Verwendung von intrinsics (ich denke, Sie lesen bereits this information). Daher beginnt der Code mit einer Konvertierung von float nach double gefolgt von einem sqrt Aufruf mit doppelter Genauigkeit und schließlich wird das Ergebnis wieder in float umgewandelt.

000000013F2B199E movups  xmm0,xmm1 
000000013F2B19A1 sqrtps  xmm1,xmm1 
000000013F2B19A4 movups  xmmword ptr [rcx+rax],xmm1 

Das zweite Beispiel wurde für x64 (AMD64) Plattform und diese Plattform verhält sich völlig anders zusammengestellt! Entsprechend der Dokumentation:

Aus Leistungsgründen werden Zwischenoperationen mit der größten Genauigkeit eines Operanden und nicht mit der größten verfügbaren Genauigkeit berechnet.

Daher werden die Berechnungen intern mit einfacher Genauigkeit durchgeführt. Ich denke, dass sie auch beschlossen haben, wann immer möglich intrinsics zu verwenden, so ist der Unterschied zwischen /fp:precise und /fp:fast etwas kleiner auf der x64-Plattform. Das neue Verhalten führt zu schnellerem Code und es gibt dem Programmierer mehr Kontrolle darüber, was genau passiert (sie waren in der Lage, die Regeln des Spiels zu ändern, weil Kompatibilitätsprobleme für die neue x64-Plattform keine Rolle spielten). Leider sind diese Änderungen/Unterschiede nicht explizit in der Dokumentation angegeben.

00AF14C7 cvtps2pd xmm0,xmm0 
00AF14CA sqrtsd  xmm0,xmm0 
00AF14CE cvtpd2ps xmm0,xmm0 

Schließlich ist das letzte Beispiel wurde mit dem Visual Studio 2010-Compiler kompiliert und ich denke, dass sie versehentlich eine intrinsische für sqrt verwendet, wenn sie sollten besser nicht haben (zumindest für /fp:precise Modus), aber sie entschieden sich ändern/Behebe dieses Verhalten in Visual Studio 2012 erneut (siehe here).