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: 8248
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: 1405
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: 8248
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: 8248
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: 8248
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: 8248
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: 8248
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: 8248
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: 8248
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: 8248
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: 8248
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: 8248
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
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 »

Nein - gemaess den Regeln der im Hintergrund ablaufenden Integral Promotion bliebe das Resultat in dem Fall ein long long. Ich hab btw. oben editiert, du vermaledeiter Schnellposter :-)
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Wtf is dis shit
Ich sehe gerade, dass sogar ((short)0) - foo zu int evaluiert
Jedes Mal, wenn ich wieder mit dieser Dreckssprache anfange, passiert sowas
Ich habe die Schnauze mal wieder so voll

Ach, und ich poste jetzt nicht mehr
Ich bleibe im Jahr 1977 und höre Hotel California auf Endlosschleife
Fuckin Eagles
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Guckt mal, was ich euch mitgebracht habe – diese Übersetzung meines Quelltexts ist falsch; parameters.distance wird immer auf 0 gesetzt (auskommentiert, was nicht direkt den Wert betrifft):

Code: Alles auswählen

				parameters.distance = UInt2B(round(0.125f * ::XMVectorGetX(
					::XMVector3LengthEst(::XMVector3Transform(::XMVectorSet(modelsXYZ.x, modelsXYZ.y, modelsXYZ.z, 1.0f), view)
				))));
0133250E  movss       xmm3,dword ptr [__real@3f800000 (134EDA0h)]  
01332516  xorps       xmm0,xmm0  
01332519  xorps       xmm1,xmm1  
0133251C  lea         ecx,[eax+eax*2]  
0133251F  movsx       eax,word ptr [edx+ecx*8+700h]  
01332527  cvtsi2ss    xmm0,eax  
0133252B  lea         edi,[edx+ecx*8+6F8h]  
01332532  movsx       ecx,word ptr [edi+0Ah]  
01332536  movsx       edx,word ptr [edi+0Ch]  
0133253A  cvtsi2ss    xmm1,ecx  
0133253E  xorps       xmm2,xmm2  
01332541  cvtsi2ss    xmm2,edx  
01332545  movaps      xmm4,xmm3  
01332548  unpcklps    xmm0,xmm2  
0133254B  unpcklps    xmm1,xmm4  
0133254E  unpcklps    xmm0,xmm1  
01332551  movaps      xmm2,xmm0  
01332554  shufps      xmm2,xmm0,0AAh  
01332558  mulps       xmm2,xmmword ptr [ebp-0F0h]  
0133255F  movaps      xmm1,xmm0  
01332562  shufps      xmm1,xmm0,55h  
01332566  mulps       xmm1,xmmword ptr [ebp-100h]  
0133256D  shufps      xmm0,xmm0,0  
01332571  mulps       xmm0,xmmword ptr [view]  
01332578  addps       xmm1,xmm0  
0133257B  addps       xmm1,xmm2  
0133257E  addps       xmm1,xmmword ptr [ebp-0E0h]  
01332585  mulps       xmm1,xmm1  
01332588  movaps      xmm0,xmm1  
0133258B  shufps      xmm0,xmm1,66h  
0133258F  addss       xmm1,xmm0  
01332593  shufps      xmm0,xmm0,55h  
01332597  addss       xmm0,xmm1  
0133259B  shufps      xmm0,xmm0,0  
0133259F  sqrtps      xmm0,xmm0  
013325A2  movss       dword ptr [ebp-38h],xmm0  
013325A7  fld         dword ptr [ebp-38h]  

;				auto const modelsTranslation(::XMMatrixTranslation(
;					modelsXYZ.x,
;					modelsXYZ.y,
;					modelsXYZ.z
;				));
;013325AA  movaps      xmm4,xmmword ptr [g_XMIdentityR0 (133E1E0h)]  
013325B1  fstp        dword ptr [ebp-158h]  
013325B7  movsd       xmm0,mmword ptr [ebp-158h]  
013325BF  mulsd       xmm0,mmword ptr [__real@3fc0000000000000 (134EE60h)]  
013325C7  cvtpd2ps    xmm0,xmm0  
013325CB  xorps       xmm1,xmm1  
013325CE  movss       xmm1,xmm0  
013325D2  movaps      xmm0,xmm1  
013325D5  cvtss2si    eax,xmm0  
013325D9  mov         word ptr [parameters+10h (1350060h)],ax  
Diese hier ist richtig:

Code: Alles auswählen

				parameters.distance = UInt2B(round(0.125f * ::XMVectorGetX(
					::XMVector3LengthEst(::XMVector3Transform(::XMVectorSet(modelsXYZ.x, modelsXYZ.y, modelsXYZ.z, 1.0f), view)
				))));
0086152E  movss       xmm3,dword ptr [__real@3f800000 (87DDA0h)]  
00861536  xorps       xmm0,xmm0  
00861539  xorps       xmm1,xmm1  
0086153C  lea         ecx,[eax+eax*2]  
0086153F  movsx       eax,word ptr [edx+ecx*8+700h]  
00861547  lea         edi,[edx+ecx*8+6F8h]  
0086154E  movsx       ecx,word ptr [edi+0Ah]  
00861552  movsx       edx,word ptr [edi+0Ch]  
00861556  cvtsi2ss    xmm0,eax  
0086155A  cvtsi2ss    xmm1,ecx  
0086155E  xorps       xmm2,xmm2  
00861561  cvtsi2ss    xmm2,edx  
00861565  movaps      xmm4,xmm3  
00861568  unpcklps    xmm0,xmm2  
0086156B  unpcklps    xmm1,xmm4  
0086156E  unpcklps    xmm0,xmm1  
00861571  movaps      xmm2,xmm0  
00861574  shufps      xmm2,xmm0,0AAh  
00861578  mulps       xmm2,xmmword ptr [ebp-0F0h]  
0086157F  movaps      xmm1,xmm0  
00861582  shufps      xmm1,xmm0,55h  
00861586  mulps       xmm1,xmmword ptr [ebp-100h]  
0086158D  shufps      xmm0,xmm0,0  
00861591  mulps       xmm0,xmmword ptr [view]  
00861598  addps       xmm1,xmm0  
0086159B  addps       xmm1,xmm2  
0086159E  addps       xmm1,xmmword ptr [ebp-0E0h]  
008615A5  mulps       xmm1,xmm1  
008615A8  movaps      xmm0,xmm1  
008615AB  shufps      xmm0,xmm1,66h  
008615AF  addss       xmm1,xmm0  
008615B3  shufps      xmm0,xmm0,55h  
008615B7  addss       xmm0,xmm1  
008615BB  shufps      xmm0,xmm0,0  
008615BF  sqrtps      xmm0,xmm0  
008615C2  movss       dword ptr [ebp-28h],xmm0  
008615C7  fld         dword ptr [ebp-28h]  
008615CA  fmul        qword ptr [__real@3fc0000000000000 (87DE60h)]  
;				auto const modelsTranslation(::XMMatrixTranslation(
;					modelsXYZ.x,
;					modelsXYZ.y,
;					modelsXYZ.z
;				));
;008615D0  movaps      xmm4,xmmword ptr [g_XMIdentityR0 (86D1E0h)]  
;008615D7  movaps      xmmword ptr [modelsTranslation],xmm4  
;008615DE  movaps      xmm4,xmmword ptr [g_XMIdentityR1 (86D1F0h)]  
008615E5  fstp        dword ptr [ebp-28h]  
008615E8  movss       xmm0,dword ptr [ebp-28h]  
008615ED  cvtss2si    eax,xmm0  
008615F1  mov         word ptr [parameters+10h (87F060h)],ax  
Was ich geändert habe? Ich habe
SInt round(
    float const value
) {
    return ::_mm_cvt_ss2si(::_mm_load_ss(&value));
}

zu
SInt round(
    float const & value
) {
    return ::_mm_cvt_ss2si(::_mm_load_ss(&value));
}

(Parameter by-reference statt by-value!) umgeschrieben.

Ist Visual C++ nicht der einzige verbliebene große Compiler, der keine Static Single Assignment Form nutzt? Das ist irgendwie nicht das erste Mal, dass der Probleme hat, temporäre Variablen richtig an geinlinete Funktionen durchzuwinken … hängt das zusammen?

Nachtrag: Die erste Version, die immer nur 0 liefert, sieht imo sogar korrekt aus. Verschluckt die sich an irgendwelchen Latenzen?

Und wtf schreibt der den Wert von xmm0 auf den Stapel, um ihn in die FPU zu laden, die ihn wieder auf den Stapel schreibt, damit er wieder in xmm0 geladen wird?! Und macht solche Spirenzchen wie, xmm0 in xmm1 kopieren und dann sofort wieder xmm1 zurück in xmm0?! Ist nop für Padding zu langweilig geworden?!
Zuletzt geändert von Krishty am 06.07.2011, 22:53, insgesamt 1-mal geändert.
Grund: Die Textblöcke waren vertauscht m[
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Is it just me, or …

Beitrag von dot »

Das Problem ist evtl. dass die Funktion einen einzelnen int returned. Wenn du ne SIMD fähige Vector-Lib bauen willst besser das Ergebnis in nem __m128 returnen. Interessanter Artikel dazu.

Edit: Nächstes mal les ich besser, ich versprechs...
Zuletzt geändert von dot am 05.07.2011, 20:16, insgesamt 1-mal geändert.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Is it just me, or …

Beitrag von dot »

Ja, ist wohl vielleicht ein Compilerbug. Die SSE Codegen vom MSVC soll ja nicht immer unbedingt glänzen was man so hört...
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Soooo. Long story short: Es ist ein Compiler-Bug. Wenn der Wert völlig unnötigerweise von den SSE-Registern in die FPU und wieder zurück wandert, interpretiert ihn der Compiler als double, obwohl es sich um eine float handelt:

Code: Alles auswählen

movss       dword ptr [ebp-34h],xmm0  // 32 Bits aus den SSE-Registern auf den Stapel schreiben
fld         dword ptr [ebp-34h]  // 32 Bits vom Stapel in die FPU laden
fstp        dword ptr [ebp-168h]  // 32 Bits von der FPU auf den Stapel schreiben
movsd       xmm0,mmword ptr [ebp-168h]  // 64 Bits laden – nicht 32, wie es sein sollte
mulsd       xmm0,mmword ptr [__real@3fc0000000000000 (38EE60h)] // 64 Bits, die garkeine sind, mit 64 Bits multiplizieren
Erwartet round() seinen Parameter by Reference statt by Value, wird die Multiplikation auf der FPU durchgeführt und das Missverständnis entsteht nicht. Vereinfache ich den Text, verschwindet der Fehler und auf wundersame Weise taucht eine cvtps2pd (convert packed single to packed double)-Anweisung auf. Wird noch lustig, das für den Bug-Report zu vereinfachen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Ich zweige das hier mal aus dem Jammer-Thread ab:
Krishty hat geschrieben:Ich habe den kompletten Debug-Output rausgenommen (eine globale Kontante statt eines Parameters in die entsprechenden ifs gepackt) und konnte 5 % mehr Leistung verzeichnen. „Cool!“, dachte ich mir – „mache ich halt das komplette System zu einem Template mit einem bool für Debug-Output, und rufe run<true>() auf, wenn ich Logging brauche, und run<false>(), wenn nicht“.

Gesagt, getan.

Nur noch 1 % Leistungssteigerung statt fünf. Wtf?! Lokalität ruiniert klingt für mich nicht plausibel; der Maschinentext ist bloß 6 % größer geworden.
Nun habe ich mal den Maschinentext der beiden Versionen der betroffenen Funktion auseinandergenommen – und er ist identisch. 1:1, von unterschiedlichen Adressen abgesehen. Könnt ihr mir jetzt mal erklären, wo die 4 % Leistung hin sind, wegen denen ich das überhaupt erst gemacht habe?

Wenn mir jetzt jemand erzählt, dass sich Maschinentext-Größe durch Lokalität antiproportional in Leistung umsetzt, mache ich bis morgen aus dem ganzen Projekt eine 96K-Demo …
Mit der Größe hat es nichts zu tun. Ich habe gerade run<true>() rausgenommen und nur run<false>() dringelassen. Größe und Maschinentext sind quasi-identisch zur Version mit manuell auskommentierter Debug-Ausgabe (die Sprungtabellen der switchs sind ein wenig anders und die Adressen der Funktionen auch) – und trotzdem fehlen vier Prozent. Es ist wie verhext. Sobald man es zum Template macht, wird es langsamer.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Hat sich erledigt.

Man darf das Programm nicht von der IDE aus starten.

Fragt mich nicht, was das soll, aber wenn ich die Exe manuell starte sind alle Versionen gleich schnell. Wenn ich sie hingegen von der IDE aus starte, egal ob mit oder ohne angehängten Debugger, ist die Template-Version jene vier Prozent langsamer.

Waren ja wieder mal höchst fruchtbare Stunden, die mir das eingehandelt hat.

Bild
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Ich habe ein Template mit einem bool-Template-Parameter. Ich möchte die Definition(en) aber in eine cpp packen, in einer anderen cpp aufrufen und auf keinen Fall im Header haben, den beide cpps aufrufen. export template wäre perfekt, aber … ist futtsch.

Warum funktioniert das hier jetzt nicht?
// Header
struct Derp {
    template <bool b> void foo();
};

// cpp mit Definition
#include "Header"
template <bool b> void Derp::foo { /* mach was */ }
template <> void Derp::foo<true>();
template <> void Derp::foo<false>();

// cpp mit Aufruf
#include "Header"
Derp derpedy;
derpedy.foo<true>();


Das resultiert in einem unaufgelösten externen Symbol. Auf einem anderen System mit anderem Projekt klappt es. Was mache ich falsch?

Persönlich denke ich, dass die Kompilierreihenfolge wichtig ist. Wird die cpp mit dem Aufruf zuerst kompiliert, verursacht der Aufruf eine Instanzierung des Templates, die ins Leere greift. Wird danach die cpp mit der Definition kompiliert, haben ihre expliziten Instantierungen keine Wirkung mehr, da bereits woanders instanziert wurde, wenn auch ohne Erfolg. Ist aber zum Großteil geraten.


Es funktioniert jetzt. Die Instanzierung geschieht durch template void Derp::foo<true>();, nicht durch template <> void Derp::foo<true>();, welches eine Spezialisierung deklariert … -.- Super Syntax!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Ich brauche mal eine Einschätzung:

enum {
  constant = (2 < sizeof("a")) && ("a"[2])
};


Der Compiler zeigt mir das als Fehler an: kein konstanter Ausdruck. Der Grund ist, dass der Zugriff "a"[2] out-of-bounds ist, da "a" nur zwei Buchstaben lang ist. Allerdings evaluiert 2 < sizeof("a") ja false, und darum darf "a"[2] garnicht ausgewertet werden und der Text müsste kompilieren. Oder?

Btw: Es ist so Zeit für constexpr in Visual C++ …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
waigie
Beiträge: 82
Registriert: 20.05.2009, 19:37

Re: Is it just me, or …

Beitrag von waigie »

Mhh bist du dir ganz sicher das in deinem Code der erste Teil der Expression false ist?
Ich hab grade mal fix das enum von dir genommen und es lässt sich mit VS 2008 und 2010 ohne Probleme kompilieren. In constant steht immer false.
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Duuu … hast vollkommen recht. Als Beweis, dass ich zwar schlafentzogen, nicht aber realitätsverlustet bin:
expr.png
expr.png (4.24 KiB) 3826 mal betrachtet
Die unteren drei funktionieren überhaupt nicht weil bei logischem Oder nicht kurzgeschlossen wird; wie erwartet. Dann habe ich darüber das Und ausprobiert … IntelliSense hat das als Fehler markiert, und darum habe ich garnicht erst zu kompilieren versucht. Ist ja immer wieder toll, wie IntelliSense und der eigentliche Compiler voneinander abweichen m[ Danke für den Hinweis; war eindeutig mein Fehler.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Kann mir jemand sagen, ob sich diese links- und rechts-Shifts zusammenfassen lassen? Falls nein, warum nicht? Ich habe hier nämlich das Gefühl, dass der Compiler suboptimalen Text erzeugt …

Code: Alles auswählen

shld        ecx,eax,0Ch
shl         eax,0Ch
shrd        eax,ecx,17h
shr         eax,0Ch
shr         ecx,17h
mov         dword ptr [esp+0Ch],eax
xor         ecx,ecx
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8248
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Er baut tatsächlich Müll. Der Quelltext sieht so aus (Festkommadivision):

Code: Alles auswählen

UFixedPoint4B const operator / (
	UFixedPoint4B const & dividend, // beliebig
	UFixedPoint4B const & divisor // immer 2048
) {
	auto const scaledDividend = UInt8B(dividend.data) << UFixedPoint4B::fractionBits;
	UFixedPoint4B quotient = { UInt4B(scaledDividend / divisor.data) };
	return quotient;
}
Man beachte scaledDividend! Lasse ich die Variable weg und schreibe den Ausdruck stattdessen direkt hin, kollabiert der Maschinentext zu:

Code: Alles auswählen

shr         eax,0Bh
shr         eax,0Ch
mov         dword ptr [esp+0Ch],eax
Was immernoch eine Anweisung zu viel ist, aber nur die Hälfte von vorher … wegen so einem scheiß Detail. (Es hat btw nicht mit der RVO zu tun.)

… ich hasse das. Die Qualität von Visual C++-erzeugtem Maschinentext ist unterirdisch. Das Schlimme ist, dass sie nicht besser werden wird, sobald ich mal auf Clang umsteige, weil mein Programm durch sowas die ganze Zeit unterschwellig auf Visual C++ optimiert wird. Absolut zum Kotzen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten