Performance von Test gegen Null mit kleinem Epsilon für floats
-
FelixManke schrieb:
Ob das schneller ist als fabs?
return (f > -ZERO) && (f < ZERO);musst du wohl selbst testen/profilen. Evtl. koenntest du auch ueberlegen, welcher der beiden Terme oefter false zurueckgibt und den voran stellen.
-
die referenz duerfte nichts bringen.
ob fabs schneller ist als ein zweiter compare, kommt drauf an, ob fabs als intrinsic ausgefuehrt wird - falls nicht passieren dabei gruselige dinge.
der compiler sollte ungefaehr sowas produzieren:inline static bool IsZero(float f) { bool result; _asm { xor bl,bl fld f fabs fcomp ZERO fstsw ax fwait sahf adc bl,0 mov result,bl }; return result; };
-
Hilfe :-)!
Aber huch! Wo ist denn mein letztes Posting hin? Naja, ich habe das mal geprofiled mit folgendem kleinen Test:
bool IsZero1(float f) { return fabs(f) <= CUtils::ZERO; }; bool IsZero2(float f) { return (f >= -CUtils::ZERO) && (f <= CUtils::ZERO); }; void Test() { for(uint32 i = 0; i < 10000000; i++) { float f = CUtils::Rand() - 0.5f; // Rand() liefert float in [0,1] bool b1 = IsZero1(f); bool b2 = IsZero2(f); } }Dabei ist herausgekommen, dass IsZero2() nur ca. 40% der Zeit benötigt. Daher habe ich das dahingehend geändert.
@hellihjb
Ich muss gestehen, dass ich Assembler nicht wirklich lesen kann, da ich nur die Grundlegenden Befehle kenne. Ist das ne Implementierung, die du mir vorschlägst? Funktioniert die auf verschiedenen Palttformen (Win / Linux)? Und was macht dabei z.B. xor bl,bl? Wo kommt 'bl' her?!
-
Assembler hat mit dem Betriebssystem nichts zu tun sondern mit der CPU. So lange du eine x86 (kompatible) CPU verwendest wirst du keine Probleme haben. Du verwendest wohl den GCC (wegen der Linux/Windows Frage), dort wird nicht die Intel-Syntax verwendet sondern die AT&T Syntax, da schreibt man von dir Register ein % vor die Register und der Quelloperand kommt vor dem Zieloperand.
Inline-ASM im GCC sieht auch etwas anders aus.bl ist das low Register von BX (=16Bit Register => bl die unteren 8Bit).
-
Ich entwickel für Windows (mit Visual Studio .NET) und für Linux (keine Ahnung, welcher Compiler, hab meinen Code bisher noch nicht auf Linux compilieren lassen). x86 Maschinen werden das schon alles sein...
-
Erklärung zum asm-code:
xor bl,bl ; löscht das bl register ( => bl ist 0) fld f ; schiebt dein float in die fpu (dort gibts nen stack wo die gespeichert werden) fabs ;fabs halt, denke das ist klar fcomp ZERO ; sollte auch klar sein (vergleich) fstsw ax ; kopiert das fpu status registers nach ax fwait ; wartet, da fpu und cpu synchronisieren müssen zum datenaustausch sahf ; kopiert das ah register in die cpu flags (jetzt haben wir das fpu ergebnis in der cpu) adc bl,0 ; add with carry addiert das carry flag darauf wenn es gesetzt ist, da zusätzlich 0 addiert wird hast du 0+1+0=1 wenn das carryflag gesetzt ist (=> das ergebnis ist null) und 0+0+0=0 wenn das carryflag nicht gesetzt ist (=> das ergebnis ist ungleich null) mov result,bl ;speicher das ergebnis in result (eigentlich quatsch, klüger wäre ein mov eax,ebx)
-
Wow. Danke netter Mensch

-
Ist das ne Implementierung, die du mir vorschlägst?
Funktioniert die auf verschiedenen Palttformen (Win / Linux)?du koenntest den code zb zum vergleichen beim profilen einsetzen um herauszufinden, ob dein compiler sinnvollen code erzeugt.
oder einfach mal mit dem debugger durch das disassembly-steppen und anzahl der instruktionen zaehlen.
bzgl des codes muss man vielleicht noch kommentieren, dass die position des fpu-compare-flag im ah-register mit der position des carry-flags uebereinstimmt und man deswegen das resultat des vergleichs durch einfaches addieren des carry-flags erhaelt.
-
Mal ein bisschen OT die Frage: Womit profilest du deinen Code? Ich würde sowas auch gern mal mit meinen Code machen, aber das ist das erste mal das ich sowas hier im Forum lese.
-
-
Such einfach im Internet nach Profiler C++ oder so. Hier ist der Link zum Wikipediaartikel:
http://de.wikipedia.org/wiki/Profiler_(Programmierung)
-
Und was mache ich wenn ich ein CPU von Intel habe. Kann ich dann auch dieses Programm benutzen oder brauche ich eins für Intel Prozessoren?
-
Ich benutze AQtime. Ist aber kommerziell. Ansonsten hätte ich jetzt auch den AMD CodeAnalyst vorgeschlagen, der ist kostenlos. Habe ich zwar noch nicht benutzt, aber für zu Hause bereits runtergeladen.
Wenn es auch um Performance geht, sind die Dinger wirklich ein Muss. Bei IsZero() hätte ich zum Beispiel ansonsten NIE nachgesehen, was sich machen lässt. Und bei 19% der Gesamtzeit kann man da ja durchaus etwas rumschrauben.
-
oder Intel VTune - laeuft nur auf intel-cpus, ist aber sehr maechtig und kostet geld.
der code analyst laeuft ueberall.
-
Also ich wollte eigentlich kein Geld dafür ausgeben, ich lade mir jetzt erstmal dieses Tool von Amd runter. Die große Frage ist nur ob das auf Intel CPUs funktioniert/Probleme macht.
-
Um nochmal auf meinen Test zurückzukommen :). Hab nochmal nen Test gemacht mit der Assembler-Version:
bool IsZero1(float f) { return fabs(f) <= CUtils::ZERO; }; bool IsZero1Asm(float f) { bool result; _asm { xor bl,bl fld f fabs fcomp CUtils::ZERO fstsw ax fwait sahf adc bl,0 mov result,bl }; return result; }; bool IsZero2(float f) { return (f >= -CUtils::ZERO) && (f <= CUtils::ZERO); };Ergebnisse (100000000 Durchläufe):
IsZero1(): 11,70925581
IsZero1Asm(): 5,12243133
IsZero2(): 4,59284843Fazit: Der Unterschied zwischen selbst kontrolliertem fabs (IsZero1Asm) und Variante zwei ist ziemlich gering. Die Implementierung von Microsoft fabs() macht offenbar interessante Sachen...
Gib's noch ne Assembler-Variante zu IsZero2()?? Und wie ist der Code für gcc? Und Inkompatibilitäten habe ich nicht zu befüchten, sofern ich für nen x86er kompiliere? Gibt's da nen Compiler-Define, was man abfragen kann? Dann könnte man sowas machen wie
#ifdef _X86 && _GCC asm mit '%' #elif _X86 && _MSC_VER asm ohne '%' #else normal #endif
-
das waere vielleicht noch einen test wert:
float EPS= 0.0001f; inline bool zero(float a) { _asm { mov ecx,a xor eax,eax and ecx,0x7fffffff sub ecx,EPS adc eax,0 }; }voraussetzung ist, dass "EPS" positiv ist.
da (positive) wachsende floating-point-zahlen in ihrer binaer-repraesentation streng monoton steigend sind, kann man sich das auslesen der fpu-flags sparen...bzgl gcc gibt's irgendwelche flags die auch inline-assembler mit intel-syntax erlauben, hab's aber auch nicht mehr im kopf - muesste sich per suche finden lassen.
-
hellihjb schrieb:
das waere vielleicht noch einen test wert:
float EPS= 0.0001f; inline bool zero(float a) { _asm { mov ecx,a xor eax,eax and ecx,0x7fffffff sub ecx,EPS adc eax,0 }; }voraussetzung ist, dass "EPS" positiv ist.
Äh, fehlt da nicht noch ein bool result; über und nen mov result,eax im asm block? Und EPS ist doch positiv. Ist doch oben definiert. Wobei ich den code so gar nicht verstehe :)...
-
Der "Trick" ist hier, dass eax das Register ist in dem der Return-Wert transportiert wird. Brauchst also eax nicht nochmal nach eax kopieren

-
das können wir sogar ohne inline-assembler machen:
inline bool zero(const float& a) { static const float eps = 0.0001f; return (reinterpret_cast<const unsigned&>(a)&(~0u>>1))<reinterpret_cast<const unsigned&>(eps); }der erzeugte code ist ggf. sogar noch effizienter. das ist mal ein seltener fall, wo es sich lohnt, builtins als referenz-auf-const zu übergeben, da die funktion eigentlich gar nicht mit dem argument in form eine floats arbeitet - jedenfalls verbleibt mit vc2005ee bei einfacher übergabe als float selbst bei höchster optimierung noch eine fld/fstp kombination, wenn das argument per value übergeben wird.