Is it just me, or …
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Hmmm, interessantes Feature … kannst du mir genauer erklären, wie das funktioniert?
Was zum Beispiel passiert, wenn ich libpng einbinde um PNGs zu laden? Dann müsste doch ein Drittel der dortigen Symbole (nämlich die, die nur zum Schreiben da sind) als unreferenziert angezeigt werden … dann nochmal die Hälfte von zlib (alles, was nur zum Packen statt zum Entpacken da ist) inklusive endloser Listen von Lookup-Tables für Datenverarbeitung und Fehlernachrichten … und 90 % der Symbole der Laufzeitbibliothek sowieso?
Und, naja, wenn der Compiler (bzw. Linker) warnt, dass ein Symbol nicht genutzt wird, dann wird er ja auch in der Lage sein, es wegzulassen. Das bedeutet im Umkehrschluss, dass bei den Symbolen, die wegen dem Unvermögen des Compilers unnötig in der Executable landen, auch keine Warnung kommen würde.
Achja, noch was am Rande: Es scheint dem Optimizer auch einen Unterschied zu machen, ob meine Exception-Klasse einen Zuweisungsoperator hat oder nicht … Programmierung ist echt unberechenbar.
Was zum Beispiel passiert, wenn ich libpng einbinde um PNGs zu laden? Dann müsste doch ein Drittel der dortigen Symbole (nämlich die, die nur zum Schreiben da sind) als unreferenziert angezeigt werden … dann nochmal die Hälfte von zlib (alles, was nur zum Packen statt zum Entpacken da ist) inklusive endloser Listen von Lookup-Tables für Datenverarbeitung und Fehlernachrichten … und 90 % der Symbole der Laufzeitbibliothek sowieso?
Und, naja, wenn der Compiler (bzw. Linker) warnt, dass ein Symbol nicht genutzt wird, dann wird er ja auch in der Lage sein, es wegzulassen. Das bedeutet im Umkehrschluss, dass bei den Symbolen, die wegen dem Unvermögen des Compilers unnötig in der Executable landen, auch keine Warnung kommen würde.
Achja, noch was am Rande: Es scheint dem Optimizer auch einen Unterschied zu machen, ob meine Exception-Klasse einen Zuweisungsoperator hat oder nicht … Programmierung ist echt unberechenbar.
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Paralleles Thema: Gibt es eine Möglichkeit, den vector constructor iterator wegzuoptimieren?
Ich habe ein fixed-size-Array ([100]) eines User-Typs, dessen K’tor nichts anderes macht, als drei Member-Pointer auf 0 zu setzen. Anstatt nun 3×4×100 Bytes zu nullen, wird der vector constructor iterator aufgerufen und ruft den Konstruktor 100 Mal indirekt auf … erscheint mir irgendwie suboptimal :/
Ich habe ein fixed-size-Array ([100]) eines User-Typs, dessen K’tor nichts anderes macht, als drei Member-Pointer auf 0 zu setzen. Anstatt nun 3×4×100 Bytes zu nullen, wird der vector constructor iterator aufgerufen und ruft den Konstruktor 100 Mal indirekt auf … erscheint mir irgendwie suboptimal :/
- TGGC
- Establishment
- Beiträge: 569
- Registriert: 15.05.2009, 18:14
- Benutzertext: Ich _bin_ es.
- Alter Benutzername: TGGC
- Echter Name: Ich _bin_ es.
- Wohnort: Mainz
- Kontaktdaten:
Re: Is it just me, or …
Konstruktor schreiben, der nichts macht und dann den den Speicherblock mit memset fuellen. f'`8kKrishty hat geschrieben:Paralleles Thema: Gibt es eine Möglichkeit, den vector constructor iterator wegzuoptimieren?
Ich habe ein fixed-size-Array ([100]) eines User-Typs, dessen K’tor nichts anderes macht, als drei Member-Pointer auf 0 zu setzen. Anstatt nun 3×4×100 Bytes zu nullen, wird der vector constructor iterator aufgerufen und ruft den Konstruktor 100 Mal indirekt auf … erscheint mir irgendwie suboptimal :/
Gruß, TGGC (der kostenlose DMC Download)
-
Alexander Kornrumpf
- Moderator
- Beiträge: 2198
- Registriert: 25.02.2009, 13:37
Re: Is it just me, or …
Ich weiß nicht ob Kimmi die "Symbol defined but never used" Warnung meinte aber ich habe die immer so verstanden, dass der Gedanke dahinter ist dass der Programmierer sich vermutlich etwas dabei gedacht hat das Symbol zu definieren und es somit wahrscheinlich ein Irrtum des Programmieres ist dieses dann nicht zu benutzen. Der Charakter der Warnung war für mich immer "Du hast vergessen das Symbol zu benutzen" und nicht "Du hast eine Definition zuviel".Krishty hat geschrieben: Und, naja, wenn der Compiler (bzw. Linker) warnt, dass ein Symbol nicht genutzt wird, dann wird er ja auch in der Lage sein, es wegzulassen. Das bedeutet im Umkehrschluss, dass bei den Symbolen, die wegen dem Unvermögen des Compilers unnötig in der Executable landen, auch keine Warnung kommen würde.
- kimmi
- Moderator
- Beiträge: 1416
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: Is it just me, or …
Teilweise kann es sogar unmöglich für den Compiler sein, nicht referenzierte Symbole wegzulassen: wenn über einen Header solche Dinge durch irgendwelche Kunstgriffe unbenutzte Symbole hinzukommen, die dann aber für andere Dlls wichtig sind. Und ja, das ist mir schon passiert :). Das setzt natürlich voraus, daß diese Symbole external linked sind. Bei Dlls hat das das selber ja in der Hand, bei Libs nur bedingt.
Mir ist es lieber, wenn der Compiler eine Warnung ausspuckt.
Gruß Kimmi
Mir ist es lieber, wenn der Compiler eine Warnung ausspuckt.
Gruß Kimmi
- Aramis
- Moderator
- Beiträge: 1458
- Registriert: 25.02.2009, 19:50
- Echter Name: Alexander Gessler
- Wohnort: 2016
- Kontaktdaten:
Re: Is it just me, or …
Man sollte einfach nicht vergessen dass ein Optimizer nichts ist, auf dass man sich 100% verlassen kann. Wenn absolut keine unreferenzierten Symbole drin sein duerfen, gibt es immer noch den Praeprozessor.
Fuer die allermeisten User spielt zudem Geschwindigkeit eine groessere Rolle als Groesse, daher duerfte die Entwicklung des MS-Linkers auch daraufhin ausgelegt sein.
Fuer die allermeisten User spielt zudem Geschwindigkeit eine groessere Rolle als Groesse, daher duerfte die Entwicklung des MS-Linkers auch daraufhin ausgelegt sein.
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Leider keine Option, da es kein PoD-Typ ist. Aber selbst, wenn ich es täte (ich habe es aus Verzweiflung gerade probiert) würde es nicht wegoptimiert – es würde in Schleife ein K’tor aufgerufen, der aus nichts als mov eax, ecx; ret bestünde.TGGC hat geschrieben:]Konstruktor schreiben, der nichts macht und dann den den Speicherblock mit memset fuellen.
Ich muss mich übrigens korrigieren, der Compiler benutzt den eh vector constructor iterator – und das wird auch der Knackpunkt sein: Ich vermute, dass der Compiler das tut, weil das Objekt unwindable ist (kein leerer Destruktor). Wahrscheinlich hat da irgendjemand angenommen, dass Objekte, sobald unwindable, auch ausnamesicher initialisiert werden müssen. Da der K’tor hier aber überhaupt keine Ausnahme auslösen kann (und der Compiler das auch korrekt erkennt, weil er an anderen Stellen entsprechenden Code weglässt) ist es hier auch überflüssig, ausnahmesicher zu initialisieren. Oder sehe ich das falsch?
Kann es sein, dass diese Warnung nur für den Quellcode der aktuellen Executable getriggert wird und nicht für alles, was zu ihr dazugelinkt wird?Alexander Kornrumpf hat geschrieben:Ich weiß nicht ob Kimmi die "Symbol defined but never used" Warnung meinte aber ich habe die immer so verstanden, dass der Gedanke dahinter ist dass der Programmierer sich vermutlich etwas dabei gedacht hat das Symbol zu definieren und es somit wahrscheinlich ein Irrtum des Programmieres ist dieses dann nicht zu benutzen. Der Charakter der Warnung war für mich immer "Du hast vergessen das Symbol zu benutzen" und nicht "Du hast eine Definition zuviel".
Ja, eine Warnung wäre mir auch lieb, aber der Compiler hält einfach nur die Klappe und packt die Symbole (wahllos!) mit rein. Achja, was wären das für Kunstgriffe?kimmi hat geschrieben:Teilweise kann es sogar unmöglich für den Compiler sein, nicht referenzierte Symbole wegzulassen: wenn über einen Header solche Dinge durch irgendwelche Kunstgriffe unbenutzte Symbole hinzukommen, die dann aber für andere Dlls wichtig sind. Und ja, das ist mir schon passiert :). Das setzt natürlich voraus, daß diese Symbole external linked sind. Bei Dlls hat das das selber ja in der Hand, bei Libs nur bedingt.
Mir ist es lieber, wenn der Compiler eine Warnung ausspuckt.
Wenn ich ihn geschrieben hätte, wäre das wohl anders. Aber vielleicht ändert es sich schon, wenn ich das Team weiterhin täglich mit Bug-Reports bombardiere … :)Aramis hat geschrieben:Man sollte einfach nicht vergessen dass ein Optimizer nichts ist, auf dass man sich 100% verlassen kann.
Es macht aber schon einen Unterschied, ob ein Linker einfach nur die Hälfte der Symbole weglässt, bei der er am schnellsten feststellen kann, dass sie unbenutzt sind oder ob er absolut gleichberechtigte Symbole ohne jedes nachvollziehbare Muster behält oder nicht. Das eine ist eine Optimierung, das andere imo ein Bug oder zumindest Nachlässigkeit.Aramis hat geschrieben:Fuer die allermeisten User spielt zudem Geschwindigkeit eine groessere Rolle als Groesse, daher duerfte die Entwicklung des MS-Linkers auch daraufhin ausgelegt sein.
- kimmi
- Moderator
- Beiträge: 1416
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: Is it just me, or …
Zum Beispiel einen Int als volatile markieren, damit eine Variable nicht wegoptimiert wird. Oder man zaubert sich mit dem Präprozessor Symbole weg / Dummysymbole hin, um Linkprobleme in den Griff zu kriegen ( für Dummy-Wrapper beliebt, link mal F77, F90, C und C++ Libs zusammen, da kommt Freude auf ). Und ich hatte erst letztens ein Problem mit einem auf dem Heap generierten Objekt, daß unresolved symbols Fehler erzeugt hatte. Auf dem Stack kreiert waren die weg ( flgl heißt die Lib ). Ich hab den Fehler aus Zeitmangel nicht nachvollzogen.
Und wenn ich beim 2008'er das Warninglevel auf 4 stelle, kriege ich zumindest in meinem Code Warnungen für definierte, aber nicht referenzierte Symbole.
Gruß Kimmi
Und wenn ich beim 2008'er das Warninglevel auf 4 stelle, kriege ich zumindest in meinem Code Warnungen für definierte, aber nicht referenzierte Symbole.
Gruß Kimmi
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Wtf, ich kompiliere seit Jahren nur mit Warning Level 4. Ich bekomme Warnungen bei unreferenzierten Parametern, unreferenzierten lokalen Objekten – aber noch nie bei unreferenzierten Funktionen oder globalen Objekten. Jetzt muss ich aber mal einen Blick in die Compiler-Einstellungen werfen …kimmi hat geschrieben:Und wenn ich beim 2008'er das Warninglevel auf 4 stelle, kriege ich zumindest in meinem Code Warnungen für definierte, aber nicht referenzierte Symbole.
- kimmi
- Moderator
- Beiträge: 1416
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: Is it just me, or …
Ops, sorry! Die Warnungen über nicht referenzierte Funktionen kriege ich von Lint! Ich hab die durch den Tüddel bekommen!
Gruß Kimmi
Gruß Kimmi
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Ah, okay! Microsofts statische Code-Analyse schmeißt vergleichbare Warnungen leider nicht. Dafür habe ich jetzt das Warning-Level WAll entdeckt, boah, da geht jedem Pedanten das Herz auf.
Es hat wohl einen Grund, warum diese Warnungen per default ausgeblendet werden.
Edit: DA!
Warning | 3081 | warning C4514: '_chgsignl' : unreferenced inline function has been removed | X:\Tools\VS2010\VC\include\math.h | 380
und direkt wieder herp derp:
Code: Alles auswählen
struct X {
enum {
A,
B,
NumValues,
}; // warning C4820: 'X' : '3' bytes padding added after data member 'NumValues'
int Members[NumValues];
};Code: Alles auswählen
__assume(x <= 3); // warning C4557: '__assume' contains effect '='Edit: DA!
Warning | 3081 | warning C4514: '_chgsignl' : unreferenced inline function has been removed | X:\Tools\VS2010\VC\include\math.h | 380
und direkt wieder herp derp:
Code: Alles auswählen
__declspec(noinline) void Foo(); // warning C4514: 'Foo' : unreferenced inline function has been removed- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Code: Alles auswählen
inline int Round(double const & Value) {
return ::_mm_cvtsd_si32(::_mm_load_sd(&Value));
}
…
int x = Round(1.0);Code: Alles auswählen
movsd xmm0,mmword ptr [__real@3ff0000000000000]
movsd mmword ptr [SomeLocalAddress],xmm0
movsd xmm1,mmword ptr [SomeLocalAddress]
cvtsd2si edx,xmm1 - kimmi
- Moderator
- Beiträge: 1416
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: Is it just me, or …
Wieso nimmst du da nicht einfach floor, der soll gemäß MS-Doku für alle Architekturen ein Intrinsic sein:
Das sieht dann auch etwas lesbarer aus und ist definitiv portabler. Keine Ahnung, was da an Assemblercode rausfällt. Außerdem gibst du einen int für einen Double zurück, wahrscheinlich für den absoluten Anteil des Doubles. Eventuell liegt da das Problem, weil double auf Int gecastet werden muß? Ich weiß nicht, wie das auf 64-Bit-Plattformen aussieht. KA!
Gruß Kimmi
Code: Alles auswählen
inline long floor(double val )
{
return (long)floor( val + 0.5 );
}
Gruß Kimmi
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Weil es nur für positive Zahlen funktioniert und um den Faktor 10 langsamer ist (floor ist zwar ein Intrinsic, aber nicht nur ein einziger Assembler-Befehl wie es cvtsd2si ist).kimmi hat geschrieben:Wieso nimmst du da nicht einfach floor
- kimmi
- Moderator
- Beiträge: 1416
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: Is it just me, or …
Das mit dem Langsamer mag sein, aber floor gibt es für float und double. In Kombination mit dem ?-Operator und ceil klappts auch für negative :). Aber da bleibt natürlich das Performance-Problem.
Gruß Kimmi
Gruß Kimmi
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Einen entsprechenden SSE-Befehl gibt es auch für float. Und naja, es ist wirklich so, dass floor() und ceil() je einen impliziten Funktionsaufruf mit etwa 30 Befehlen bewirken … da läuft ein einziger SSE-Befehl außer Konkurrenz … auch, wenn der Compiler seinen Aufruf so selten schlecht optimiert. Ist natürlich auch sauber gekapselt und für nicht-MS-Architekturen liegen Intrinsic-lose, langsame, platzverschwendende Alternativen bereit.
(Übrigens optimiert der Compiler jeden Cast zu int sowieso durch den verwandten cvttsd2si-Befehl, der abschneidet, statt zu runden – jede manuelle Methode wäre also zumindest in diesem Punkt redundant)
(Übrigens optimiert der Compiler jeden Cast zu int sowieso durch den verwandten cvttsd2si-Befehl, der abschneidet, statt zu runden – jede manuelle Methode wäre also zumindest in diesem Punkt redundant)
- kimmi
- Moderator
- Beiträge: 1416
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: Is it just me, or …
Hm, interessant. Ich habe mir den generierten Assemblercode nicht angesehen, sondern habe mal aus der Doku von MS gehofft, die entsprechenden Infos zu kriegen und dass das floor = globaler und vor allem optimierter Intrinsic. Wieder was von dir gelernt!
Gruß Kimmi
Gruß Kimmi
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Code: Alles auswählen
if(2.2f == Gamma)Code: Alles auswählen
movss xmm0,dword ptr [esi+4] ; move scalar single-precision floating-point number from stack to xmm0
movsd xmm1,mmword ptr [__real@40019999a0000000] ; load double representation of 2.2 (?) into xmm1
cvtps2pd xmm0,xmm0 ; convert value in xmm0 from single to double
ucomisd xmm1,xmm0 ; compare scalar double-precision floating-point values in xmm0 and xmm1 and set EFLAGS
mov ecx,eax ; save content of eax
lahf ; copy EFLAGS into ah (eax)
test ah,44h ; test if parity and zero flag are set(?)I don't even …
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
… und direkt noch einer hinterher:
Erinnert ihr euch, dass der Compiler manchmal schnelleren Code generieren kann, wenn man den automatischen Zuweisungsoperator benutzt statt einen eigenen zu schreiben? Naja, mit SSE ist es – wie könnte es auch anders sein! – genau umgekehrt. Wenn ihr eine Datenstruktur habt, die einen ausgerichteten 128-Bit-Vektor enthält, schreibt den Zuweisungsoperator bloß selber. Ich habe Vektor, Tupel und XNA-Math-Vektor ineinander verschachtelt und wenn ich die Zuweisung explizit ausschreibe, resultiert es in gerade mal einem Viertel der Befehle – der automatische Zuweisungsoperator kopiert nämlich Wort für Wort, der manuelle auf einen Schlag per movaps.
Und gebt immer by-Reference zurück, falls es möglich ist. By-value erkennt der Compiler nämlich auch kein Alignment.
Erinnert ihr euch, dass der Compiler manchmal schnelleren Code generieren kann, wenn man den automatischen Zuweisungsoperator benutzt statt einen eigenen zu schreiben? Naja, mit SSE ist es – wie könnte es auch anders sein! – genau umgekehrt. Wenn ihr eine Datenstruktur habt, die einen ausgerichteten 128-Bit-Vektor enthält, schreibt den Zuweisungsoperator bloß selber. Ich habe Vektor, Tupel und XNA-Math-Vektor ineinander verschachtelt und wenn ich die Zuweisung explizit ausschreibe, resultiert es in gerade mal einem Viertel der Befehle – der automatische Zuweisungsoperator kopiert nämlich Wort für Wort, der manuelle auf einen Schlag per movaps.
Und gebt immer by-Reference zurück, falls es möglich ist. By-value erkennt der Compiler nämlich auch kein Alignment.
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Noch ein schönes Beispiel dafür, was für unbeabsichtigten Code static erzeugt:
Die letzte Zeile kompiliert – wie gewohnt – zu
Nun könnte man meinen, der Code entfiele, sobald man im K’tor keine Initialisierung mehr vornimmt. Aber Pustekuchen – es fällt nur der Body weg, die Hülle bleibt:Da schwirrt nun ein Herrenloses bool in den globalen Daten herum, dessen Dasein darin besteht, unentwegt abgefragt und beim ersten Mal auf true gesetzt zu werden. Captain Fantastic. Ich hasse static.
Code: Alles auswählen
class CMatrix {
::XMMATRIX MyData;
…
public:
// Default constructor.
CMatrix()
: MyData(::XMMatrixIdentity())
{ }
};
… in function:
static CMatrix Matrix;Code: Alles auswählen
mov eax,dword ptr [1205568h] ; Lade eine Dummy-bool, die der Compiler angelegt hat, um den Initialisierungsstatus zu prüfen
test al,1 ; Schon initialisiert?
jne 11F843Ch ; Dann überspring den Rest
movaps xmm0, { 1., 0., 0., 0. } ; Lade die Einheitsmatrix in die Register …
movaps xmm1, { 0., 1., 0., 0. }
movaps xmm2, { 0., 0., 1., 0. }
movaps xmm3, { 0., 0., 0., 1. }
movaps xmmword ptr [esp+10h], xmm0 ; … und speichere sie in der Matrix-Instanz
movaps xmmword ptr [esp+20h], xmm1
movaps xmmword ptr [esp+30h], xmm2
movaps xmmword ptr [esp+40h], xmm3
or eax,1 ; Setze das Flag auf true, …
mov dword ptr [1205568h],eax ; … und speichere es.Code: Alles auswählen
mov eax,dword ptr [1205432h]
test al,1
jne 11F843Ch
or eax,1
mov dword ptr [1205432h],eax- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Kann jemand reproduzieren, dass Visual C++ strammere Kompilate erzeugt, wenn man sichere Destruktoren mit throw() dekoriert?
Ich habe throw() und __declspec(nothrow) letztes Jahr testweise auf über 400 sichere Funktionen angewandt, und es hatte absolut null Effekt.
Nun dekorierte ich aus Jux zwei Destruktoren, die externe (aber sichere) Funktionen aufrufen damit – und bekam 1 % kleineren Code … überall sonst nützt es aber auch weiterhin nichts … und ich habe auch nicht wirklich Lust, wieder durch die Assembly zu steigen, was genau da optimiert wird …
… ein wenig merkwürdig kommt mir das vor, weil der Compiler mit /EHsc arbeitet, wozu die Doku sagt: „catches C++ exceptions only and tells the compiler to assume that extern C functions never throw a C++ exception.“ Aber wäre ja nicht das erste Mal, dass Destruktoren nachlässig verarbeitet werden …
Ich habe throw() und __declspec(nothrow) letztes Jahr testweise auf über 400 sichere Funktionen angewandt, und es hatte absolut null Effekt.
Nun dekorierte ich aus Jux zwei Destruktoren, die externe (aber sichere) Funktionen aufrufen damit – und bekam 1 % kleineren Code … überall sonst nützt es aber auch weiterhin nichts … und ich habe auch nicht wirklich Lust, wieder durch die Assembly zu steigen, was genau da optimiert wird …
… ein wenig merkwürdig kommt mir das vor, weil der Compiler mit /EHsc arbeitet, wozu die Doku sagt: „catches C++ exceptions only and tells the compiler to assume that extern C functions never throw a C++ exception.“ Aber wäre ja nicht das erste Mal, dass Destruktoren nachlässig verarbeitet werden …
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Eine interessante Optimierung ist es auch, die Anzahl der Funktionsparameter versteckt gering zu halten. Ich hatte beispielsweise so eine Funktion:
void render(
CGPU & GPU,
CMatrix4x4 const & world,
CMatrix4x4 const & view,
CMatrix4x4 const & projection,
float const starLuminanceFactor,
float const fieldOfView,
float const horizontalResolution
) {
updateShaderConstants(
world * view * projection,
starLuminanceFactor * horizontalResolution / (fieldOfView * fieldOfView)
);
// Lots of stuff like GPU.SetPrimitiveTopology(POINTS); here
GPU.Draw(1000);
}
Das Problem ist hier, dass sieben Parameter übergeben werden, die intern zu drei evaluieren. Hier lässt sich viel rausholen, wenn man die Parameterzahl reduziert:
void render(
CGPU & GPU,
CMatrix4x4 const & worldViewProjection,
float const relativeStarLuminanceFactor
) {
updateShaderConstants(
worldViewProjection,
relativeStarLuminanceFactor
);
// Lots of stuff like GPU.SetPrimitiveTopology(POINTS); here
GPU.Draw(1000);
}
Das macht aber die Nutzbarkeit zunichte, weil der Anwender keine Ahnung hat, wie er den relativen Wert berechnen soll. Darum deklariert man die Funktion private und fügt einen öffentlichen Wrapper mit der ursprünglichen, freundlichen Signatur hinzu:
void render(
CGPU & GPU,
CMatrix4x4 const & world,
CMatrix4x4 const & view,
CMatrix4x4 const & projection,
float const starLuminanceFactor,
float const fieldOfView,
float const horizontalResolution
) {
return render(
world * view * projection,
starLuminanceFactor * horizontalResolution / (fieldOfView * fieldOfView)
);
}
Da die Komplexität des Wrappers recht gering ist, wird der Compiler ihn überall inlinen; damit wird die Berechnung der endgültigen Parameter zum Aufrufer der Funktion verschoben. Der Aufrufer erledigt nun also scheinbar mehr Arbeit als zuvor.
Da die Parameterzahl der finalen Funktion aber jetzt mit drei statt sieben relativ niedrig ist, kann der Compiler alle Parameter in Registern übergeben. Vorher bedeutete ein Funktionsaufruf: Parameter auf den Stack schaufeln – Aufruf – Abziehen der Parameter – Rechnen. Nun bedeutet ein Funktionsaufruf: Rechnen – Aufruf. Und da die Werte, mit denen gerechnet wird, mit großer Wahrscheinlichkeit im Kontext zum Aufrufer stehen, kann da nochmal optimiert werden.
Das alles funktioniert nur, wenn globale Optimierungen aktiviert sind (sonst kann der Compiler die Parameterübergabe nicht automatisch bestimmen), die Berechnung der Parameter leicht genug für Inlining und die aufgerufene Funktion zu schwer für Inlining ist, und wenn man die Parameterzahl niedrig genug für eine Übergabe ohne Stack bekommt. Kann sich dann aber lohnen – ich hatte gerade einen Fall, wo mir das beim Aufrufer und in der finalen Funktion zusammen 40 Befehle gespart hat. Und bei Gleitkommazahlen ist der Weg FPU-/SSE-Register -> Stack -> FPU-/SSE-Register ja besonders teuer.
Nur so als Tipp. Und Code-Tags benutze ich nicht, weil ich das Scrollen scheiße finde.
void render(
CGPU & GPU,
CMatrix4x4 const & world,
CMatrix4x4 const & view,
CMatrix4x4 const & projection,
float const starLuminanceFactor,
float const fieldOfView,
float const horizontalResolution
) {
updateShaderConstants(
world * view * projection,
starLuminanceFactor * horizontalResolution / (fieldOfView * fieldOfView)
);
// Lots of stuff like GPU.SetPrimitiveTopology(POINTS); here
GPU.Draw(1000);
}
Das Problem ist hier, dass sieben Parameter übergeben werden, die intern zu drei evaluieren. Hier lässt sich viel rausholen, wenn man die Parameterzahl reduziert:
void render(
CGPU & GPU,
CMatrix4x4 const & worldViewProjection,
float const relativeStarLuminanceFactor
) {
updateShaderConstants(
worldViewProjection,
relativeStarLuminanceFactor
);
// Lots of stuff like GPU.SetPrimitiveTopology(POINTS); here
GPU.Draw(1000);
}
Das macht aber die Nutzbarkeit zunichte, weil der Anwender keine Ahnung hat, wie er den relativen Wert berechnen soll. Darum deklariert man die Funktion private und fügt einen öffentlichen Wrapper mit der ursprünglichen, freundlichen Signatur hinzu:
void render(
CGPU & GPU,
CMatrix4x4 const & world,
CMatrix4x4 const & view,
CMatrix4x4 const & projection,
float const starLuminanceFactor,
float const fieldOfView,
float const horizontalResolution
) {
return render(
world * view * projection,
starLuminanceFactor * horizontalResolution / (fieldOfView * fieldOfView)
);
}
Da die Komplexität des Wrappers recht gering ist, wird der Compiler ihn überall inlinen; damit wird die Berechnung der endgültigen Parameter zum Aufrufer der Funktion verschoben. Der Aufrufer erledigt nun also scheinbar mehr Arbeit als zuvor.
Da die Parameterzahl der finalen Funktion aber jetzt mit drei statt sieben relativ niedrig ist, kann der Compiler alle Parameter in Registern übergeben. Vorher bedeutete ein Funktionsaufruf: Parameter auf den Stack schaufeln – Aufruf – Abziehen der Parameter – Rechnen. Nun bedeutet ein Funktionsaufruf: Rechnen – Aufruf. Und da die Werte, mit denen gerechnet wird, mit großer Wahrscheinlichkeit im Kontext zum Aufrufer stehen, kann da nochmal optimiert werden.
Das alles funktioniert nur, wenn globale Optimierungen aktiviert sind (sonst kann der Compiler die Parameterübergabe nicht automatisch bestimmen), die Berechnung der Parameter leicht genug für Inlining und die aufgerufene Funktion zu schwer für Inlining ist, und wenn man die Parameterzahl niedrig genug für eine Übergabe ohne Stack bekommt. Kann sich dann aber lohnen – ich hatte gerade einen Fall, wo mir das beim Aufrufer und in der finalen Funktion zusammen 40 Befehle gespart hat. Und bei Gleitkommazahlen ist der Weg FPU-/SSE-Register -> Stack -> FPU-/SSE-Register ja besonders teuer.
Nur so als Tipp. Und Code-Tags benutze ich nicht, weil ich das Scrollen scheiße finde.
- CodingCat
- Establishment
- Beiträge: 1857
- Registriert: 02.03.2009, 21:25
- Wohnort: Student @ KIT
- Kontaktdaten:
Re: Is it just me, or …
Ich hab mal einen Code-Ansicht-Erweitern-Link eingebaut.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Ein hübscher versteckter Fallstrick für den Optimizer sind Wirkungen in den Konstruktoren von Return-Values:Bei mir hat der Compiler den K’tor von string geinlinet, und zwar in allen beiden return-Anweisungen. Nachdem ich den Code so modifiziert habe, dass der K’tor nur noch einmal aufgerufen wird:ist eine Menge Konstruktorkram rausgeflogen, der Registerdruck gesunken und es gibt eine Stelle weniger, an der eine Exception geschmissen werden könnte – unter’m Strich ist fast die Hälfte der Funktion weggefallen. Also immer ein Auge offen halten, wenn eine Funktion unterschiedliche Pfade zur Konstruktion eines Rückgabewerts hat.
Code: Alles auswählen
// Gibt "1 KiB, 1 B" für 1025 zurück
string stringFromByteSize(
unsigned long long const size
) {
// Damit für 0 kein leerer String zurückgegeben wird
if(0 == size)
return "0 B";
char temp[sizeof("15 EiB, 1023 PiB, 1023 TiB, 1023 GiB, 1023 MiB, 1023 KiB, 1023 B ")];
// Hier wird temp befüllt
return string(temp);
}Code: Alles auswählen
char temp[sizeof("15 EiB, 1023 PiB, 1023 TiB, 1023 GiB, 1023 MiB, 1023 KiB, 1023 B ")];
if(0 == size) {
strcpy(temp, "0 B");
} else {
// Hier wird temp befüllt
}
return string(temp);- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Kann mir das hier jemand erklären?
Gibt es bei Array-Initialisierung tatsächlich einen Unterschied dazwischen, ob ein Kopierk’tor nicht verfügbar oder nicht automatisch generiert ist? Warum versucht der, das Array per Kopierk’tor zu initialisieren, falls einer existiert?
Code: Alles auswählen
class CNonCopyable {
private:
CNonCopyable(CNonCopyable const &);
public:
explicit CNonCopyable(int) { }
};
CNonCopyable arrayA[] = { CNonCopyable(0) }; // ERROR: copy constructor is inaccessable
struct CIndirectlyNonCopyable
: public CNonCopyable
{
explicit CIndirectlyNonCopyable(int x) : CNonCopyable(x) { }
};
CIndirectlyNonCopyable arrayB[] = { CIndirectlyNonCopyable(0) }; // OKRe: Is it just me, or …
Kann dein „OK“ gerade nicht nachvollziehen: http://codepad.org/kDsz15IB
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Na toll, also wieder eine Microsoft-Erweiterung. Aua.
Wie ruft man bei Array-Initialisierungen K’toren explizit auf, ohne Kopien zu erzwingen?
Wie ruft man bei Array-Initialisierungen K’toren explizit auf, ohne Kopien zu erzwingen?
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
HähWarum ergibt die Negierung von short ein int?
Code: Alles auswählen
short foo;
auto bar = -foo; // decltype(bar) == int- Aramis
- Moderator
- Beiträge: 1458
- Registriert: 25.02.2009, 19:50
- Echter Name: Alexander Gessler
- Wohnort: 2016
- Kontaktdaten:
Re: Is it just me, or …
Weil die unaeren + und --Operatoren immer int zurueckgeben.
Manche Fanatiker preisen das sogar als tolle Sprachfeature an: decltype(+'a')==int
Manche Fanatiker preisen das sogar als tolle Sprachfeature an: decltype(+'a')==int
Zuletzt geändert von Aramis am 01.05.2011, 15:31, insgesamt 1-mal geändert.
- Krishty
- Establishment
- Beiträge: 8421
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Is it just me, or …
Auch bei signed long long?