Is it just me, or …

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

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 :/
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
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 …

Beitrag von TGGC »

Krishty 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 :/
Konstruktor schreiben, der nichts macht und dann den den Speicherblock mit memset fuellen. f'`8k


Gruß, TGGC (der kostenlose DMC Download)
Alexander Kornrumpf
Moderator
Beiträge: 2198
Registriert: 25.02.2009, 13:37

Re: Is it just me, or …

Beitrag von Alexander Kornrumpf »

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.
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".
Benutzeravatar
kimmi
Moderator
Beiträge: 1416
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: Is it just me, or …

Beitrag von kimmi »

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
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Aramis »

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.
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

TGGC hat geschrieben:]Konstruktor schreiben, der nichts macht und dann den den Speicherblock mit memset fuellen.
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.
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?
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".
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?
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.
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?
Aramis hat geschrieben:Man sollte einfach nicht vergessen dass ein Optimizer nichts ist, auf dass man sich 100% verlassen kann.
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:Fuer die allermeisten User spielt zudem Geschwindigkeit eine groessere Rolle als Groesse, daher duerfte die Entwicklung des MS-Linkers auch daraufhin ausgelegt sein.
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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1416
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: Is it just me, or …

Beitrag von kimmi »

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
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

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.
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 …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1416
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: Is it just me, or …

Beitrag von kimmi »

Ops, sorry! Die Warnungen über nicht referenzierte Funktionen kriege ich von Lint! Ich hab die durch den Tüddel bekommen!

Gruß Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

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.

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 '='
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

__declspec(noinline) void Foo(); // warning C4514: 'Foo' : unreferenced inline function has been removed
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

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  
War nicht genau das ein Grund, Intrinsics zu benutzen? (Übrigens ist das ein x64-Build; unter x86 ist der Code noch ineffizienter, weil Round() garnicht erst geinlined wird.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1416
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: Is it just me, or …

Beitrag von kimmi »

Wieso nimmst du da nicht einfach floor, der soll gemäß MS-Doku für alle Architekturen ein Intrinsic sein:

Code: Alles auswählen

inline long floor(double val )
{
  return (long)floor( val + 0.5 );
}
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
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

kimmi hat geschrieben:Wieso nimmst du da nicht einfach floor
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).
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1416
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: Is it just me, or …

Beitrag von kimmi »

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
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

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)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1416
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: Is it just me, or …

Beitrag von kimmi »

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
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Code: Alles auswählen

if(2.2f == Gamma)
Visual C++ 2010 auf höchsten Optimierungseinstellungen:

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(?)
Wtf is this shit
I don't even …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

… 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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Noch ein schönes Beispiel dafür, was für unbeabsichtigten Code static erzeugt:

Code: Alles auswählen

class CMatrix {
    ::XMMATRIX MyData;
    …
public:
    // Default constructor.
    CMatrix()
        : MyData(::XMMatrixIdentity())
    { }
};

… in function:
static CMatrix Matrix;
Die letzte Zeile kompiliert – wie gewohnt – zu

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.
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:

Code: Alles auswählen

mov         eax,dword ptr [1205432h]
test        al,1
jne         11F843Ch
or          eax,1
mov         dword ptr [1205432h],eax
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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

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 …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Is it just me, or …

Beitrag von CodingCat »

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
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Ein hübscher versteckter Fallstrick für den Optimizer sind Wirkungen in den Konstruktoren von Return-Values:

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);
}
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:

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);
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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Kann mir das hier jemand erklären?

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) }; // OK
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?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Is it just me, or …

Beitrag von eXile »

Kann dein „OK“ gerade nicht nachvollziehen: http://codepad.org/kDsz15IB
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Na toll, also wieder eine Microsoft-Erweiterung. Aua.
Wie ruft man bei Array-Initialisierungen K’toren explizit auf, ohne Kopien zu erzwingen?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Häh

Code: Alles auswählen

short foo;
auto bar = -foo; // decltype(bar) == int
Warum ergibt die Negierung von short ein int?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Aramis »

Weil die unaeren + und --Operatoren immer int zurueckgeben.

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.
Benutzeravatar
Krishty
Establishment
Beiträge: 8421
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Auch bei signed long long?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten