Seite 65 von 254

Re: Jammer-Thread

Verfasst: 22.05.2012, 18:06
von eXile
Was ist das für eine räudige Scheiße! Die XMMath-Library vom nächsten Platform-SDK beitet keine operator=(…) volatile! Dabei ist es von der MSDN sogar vorgegeben, volatile zu benutzen, wenn man nicht im Optimierungsmodus /Ot (Favor Fast Code) ist (halt in jedem Debug-Build):
http://msdn.microsoft.com/en-us/library/windows/desktop/ff476457 hat geschrieben:Don't read from a subresource mapped for writing
When you pass D3D11_MAP_WRITE, D3D11_MAP_WRITE_DISCARD, or D3D11_MAP_WRITE_NO_OVERWRITE to the MapType parameter, you must ensure that your app does not read the subresource data to which the pData member of D3D11_MAPPED_SUBRESOURCE points because doing so can cause a significant performance penalty. […]

Note
Even the following C++ code can read from memory and trigger the performance penalty because the code can expand to the following x86 assembly code.

C++ code:

Code: Alles auswählen

*((int*)MappedResource.pData) = 0;
x86 assembly code:

Code: Alles auswählen

xor [eax],0
Use the appropriate optimization settings and language constructs to help avoid this performance penalty. For example, you can avoid the xor optimization by using a volatile pointer or by optimizing for code speed instead of code size.
Jetzt musste ich mir für alle Typen der XMMath-Library minimale Wrapper bauen, nur um den operator=(...) volatile zur Verfügung zu stellen. Benutzen die ihre eigene Doku nicht?! Ist das ein Fall für Microsoft Connect, solange die noch nichts released haben?

Re: Jammer-Thread

Verfasst: 22.05.2012, 18:43
von Krishty
Nein, das musst du persönlich behandeln.

Für SSE-Schreiboperationen ist, im Gegensatz zu x86-nativen Datentypen, keine strenge Ordnung beim Schreiben vorgeschrieben:
http://msdn.microsoft.com/en-us/library/windows/desktop/ee418650 hat geschrieben:String operations (MOVS and STOS) and 16-byte SSE reads can be internally reordered
Das bedeutet, dass die CPU die einzelnen Schreiboperationen – samt deren Sichtbarkeit in den Caches – für unbestimmte Zeit vertagen und dann in anderer Reihenfolge realisieren darf.

Konkret bedeutet das, dass du vor dem Freigeben der Map eine Schreibblockade einfügen musst, damit die Schreiboperationen wirksam werden, bevor der Speicher wieder zur GPU geschickt wird.

Für einen selbstgeschriebenen Zuweisungsoperator, auf dem die linke Seite volatile ist, bedeutet das: Auch er muss diese Schreibblockade aufweisen. Schon aus Prinzip, weil der Speicher ja volatile deklariert ist und deshalb um sofortige Realisierung aller Zugriffe bettelt (sonst viel Spaß, falls du den Operator mal in einem nebenläufigen Programm einsetzt, wofür volatile ja eigentlich da ist).

In letzter Konsequenz: Deine for-Schleife, die die Map mit Vektordaten befüllen soll, ruiniert den Schreibdurchsatz und die Caches, weil nach jedem einzelnen Vektor eine Schreibblockade folgt.

Behandel das also lieber vor Ort Sonderfall.

Re: Jammer-Thread

Verfasst: 22.05.2012, 19:09
von eXile
Krishty hat geschrieben:Für einen selbstgeschriebenen Zuweisungsoperator, auf dem die linke Seite volatile ist, bedeutet das: Auch er muss diese Schreibblockade aufweisen.
So wie ich das gerade sehe, kann man gar nicht volatile __m128 einander zuweisen:

Code: Alles auswählen

volatile __m128 a;
volatile __m128 b;
__m128 c;

a = b; // C2678
a = c; // C2678
error C2678: binary '=' : no operator found which takes a left-hand operand of type 'volatile __m128' (or there is no acceptable conversion)
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\xmmintrin.h(70): could be '__m128 &__m128::operator =(const __m128 &)'
while trying to match the argument list '(volatile __m128, volatile __m128)'

error C2678: binary '=' : no operator found which takes a left-hand operand of type 'volatile __m128' (or there is no acceptable conversion)
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\xmmintrin.h(70): could be '__m128 &__m128::operator =(const __m128 &)'
while trying to match the argument list '(volatile __m128, __m128)'
Und so wie ich das sehe, kann man dem __m128 auch keine Operatoren hinzufügen; zumindest bin ich immer mit ähnlichen Dingen wie hier gescheitert.

Da die XMMath aber für alle Datentypen unions auf float-Arrays anbietet, kopiere ich im Augenblick die float-Arrays. Das ist Mist, aber es funktioniert; wahrscheinlich kommst du aber gleich zu einer besseren Lösung.

Re: Jammer-Thread

Verfasst: 22.05.2012, 19:17
von Krishty
eXile hat geschrieben:Da die XMMath aber für alle Datentypen unions auf float-Arrays anbietet, kopiere ich im Augenblick die float-Arrays. Das ist Mist, aber es funktioniert; wahrscheinlich kommst du aber gleich zu einer besseren Lösung.
Achso – ich dachte, die Datentypen seien immernoch alle __m128-Wrapper. Mit einzelnen floats müsste es tatsächlich funktionieren. Dann wird der Grund schlicht und einfach sein, dass keine Möglichkeit einer atomaren Zuweisung besteht, und volatile ganz einfach unbrauchbar ist.

Re: Jammer-Thread

Verfasst: 22.05.2012, 19:37
von eXile
Halt, Kommando zurück! Ich habe mich verwirren lassen, weil die noch zwei weitere, vollkommen unabhängige Vektortypen deklarieren, die sie aber gar nicht für ihre Mathe-Funktionen verwenden.

Für XMVECTOR, der __m128-Wrapper:

Code: Alles auswählen

struct __vector4
{
    union
    {
        float       vector4_f32[4];
        uint32_t    vector4_u32[4];
    };
};

// Vector intrinsic: Four 32 bit floating point components aligned on a 16 byte 
// boundary and mapped to hardware vector registers
#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
typedef __m128 XMVECTOR;
#elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
typedef __n128 XMVECTOR;
#else
typedef __vector4 XMVECTOR;
#endif
Für XMMATRIX, der Standard-Matrixklasse:

Code: Alles auswählen

#if (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM)) && defined(_XM_NO_INTRINSICS_)
struct XMMATRIX
#else
__declspec(align(16)) struct XMMATRIX
#endif
{
    union
    {
        XMVECTOR r[4];
        struct
        {
            float _11, _12, _13, _14;
            float _21, _22, _23, _24;
            float _31, _32, _33, _34;
            float _41, _42, _43, _44;
        };
        float m[4][4];
    };

    XMMATRIX() {}
    XMMATRIX(FXMVECTOR R0, FXMVECTOR R1, FXMVECTOR R2, GXMVECTOR R3) { r[0] = R0; r[1] = R1; r[2] = R2; r[3] = R3; }
    XMMATRIX(float m00, float m01, float m02, float m03,
             float m10, float m11, float m12, float m13,
             float m20, float m21, float m22, float m23,
             float m30, float m31, float m32, float m33);
    explicit XMMATRIX(/*_In_reads_(16)*/ const float *pArray);

    float       operator() (size_t Row, size_t Column) const { return m[Row][Column]; }
    float&      operator() (size_t Row, size_t Column) { return m[Row][Column]; }

    XMMATRIX&   operator= (const XMMATRIX& M) { r[0] = M.r[0]; r[1] = M.r[1]; r[2] = M.r[2]; r[3] = M.r[3]; return *this; }

    XMMATRIX    operator+ () const { return *this; }
    XMMATRIX    operator- () const;

    XMMATRIX&   operator+= (CXMMATRIX M);
    XMMATRIX&   operator-= (CXMMATRIX M);
    XMMATRIX&   operator*= (CXMMATRIX M);
    XMMATRIX&   operator*= (float S);
    XMMATRIX&   operator/= (float S);

    XMMATRIX    operator+ (CXMMATRIX M) const;
    XMMATRIX    operator- (CXMMATRIX M) const;
    XMMATRIX    operator* (CXMMATRIX M) const;
    XMMATRIX    operator* (float S) const;
    XMMATRIX    operator/ (float S) const;

    friend XMMATRIX operator* (float S, CXMMATRIX M);
};
In allen Mathe-Funktionen gibt's keine Überraschungen: Falls keine Intrinsics benutzt werden sollen, wird XMVECTOR.vector4_f32 und XMMATRIX.m benutzt, ansonsten XMVECTOR als __m128 und XMMATRIX.r als __m128.

Es ist eher verwunderlich, warum das gerade hier funktioniert. Ist so eine union-Geschichte wie in XMMATRIX überhaupt erlaubt (also ein union mit __m128 und float[4])? Wenn __m128 und volatile-Zuweisungen nicht funktioniere, ist das alles fürs Mappen unbrauchbar?

Re: Jammer-Thread

Verfasst: 22.05.2012, 19:53
von Krishty
XNA Math hatte zwei Vektortypen:
  • einen, der auf __m128 basierte und nach Möglichkeit 1:1 auf ein Register abgebildet wurde – also nur als lokale Variable oder rvalue existieren dürfte; und
  • einen, der auf float[4] basierte und für Speicherung ausgelegt war (z.B. als Attribut oder in Datenstrukturen).
Konvertiert wurde zwischen den beiden mit Lade- und Speicherprozeduren; ebenso durfte man nicht aus dem Registertyp einfach eine float auslesen, sondern musste das mit XMVectorGetX() (oder so) machen. Der Sinn war, dass für die eigentlichen Berechnungen der Registertyp genutzt wurden, damit sich dort keine Gedanken über Speicherausrichtung u.ä. gemacht werden mussten, und sie ausschließlich in Registern blieben und nicht für so blöde Sachen wie Zugriffe auf einzelne Skalare auf den Stapel geschrieben und für spätere Berechnungen zurückgeladen werden mussten. Die float[4]-Hälfte der unions war afaik nur für die nicht-Intrinsic-Version, und (geraten) für harte Algorithmen, die eh nicht vektorisierbar sind.

Für Matrizen gab es keine solchen Typen, und das sieht bei XMMath genauso aus. Als Grund mutmaße ich: 4×4-Matrizen sind so groß, dass sie eh nicht in Registern übergeben oder als Rvalues benutzt werden können (eine einzige Kopie würde alle x86-SSE-Register vollständig aufgefüllt haben, dann wäre nicht einmal mehr Platz für einen Operanden). Darum haben sie schlicht drauf geschissen und es mit den unions so bequem wie möglich gemacht. Da Matrizen nur durch deren Funktionen / K’toren manipuliert werden und nicht von dir selber, ist das in den meisten Fällen sicher.

Für’s Mappen ist das alles sehr wohl brauchbar – aber nur als Hack. Beim Mappen sagt das volatile dem Compiler einfach, dass er nicht optimieren soll (spezieller: dass ein impliziter Zugriff auf den so deklarierten Speicher nicht stattfinden darf, weil er damit implizite Acquire-Semantik verursachen würde**). Die Semantik, die volatile sonst für Methoden und Operatoren hat (Acquire und Release bei Lesen und Schreiben zu garantieren) musst du in dem Augenblick einfach ignorieren, weil SSE damit nicht wirklich kompatibel ist (es sei denn, du baust Speicherbarrieren und Mutexe in alle volatile-deklarierten Methodenaufrufe ein).

** ist schon ein Bisschen her, dass ich damit gearbeitet habe; bitte korrigieren, falls ich A & R durcheinanderschmeiße.

Re: Jammer-Thread

Verfasst: 22.05.2012, 20:26
von eXile
Krishty hat geschrieben:Für’s Mappen ist das alles sehr wohl brauchbar – aber nur als Hack.
Sorry, dass ich so nachhake, aber ich versuche es zu verstehen. Die Aquire&Release-Semantik beim Lesen&Schreiben ist ja eigentlich gut, denn das verhindert die von der MSDN angesprochenen ungewollten Lesezugriffe.

Die Optionen die jetzt so dastehen:
  1. Auf volatile verzichten (so wie wohl 99 % aller Direct3D-Benutzer) und zügig mittels des eingebauten

    Code: Alles auswählen

    XMMATRIX& operator= (const XMMATRIX& M)
    {
    	r[0] = M.r[0]; r[1] = M.r[1]; r[2] = M.r[2]; r[3] = M.r[3];
    	return *this;
    }
    als __m128 kopieren und hoffen, dass alles gut geht? (Eventuell noch mittels _mm_sfence von außen absichern.)
  2. Die MSDN beachten, volatile einsetzen und mittels des selbstgebastelten

    Code: Alles auswählen

    VXMMATRIX volatile & operator=(const XMMATRIX& M) volatile
    {
    	_11 = M._11; _12 = M._12; _13 = M._13; _14 = M._14;
    	_21 = M._21; _22 = M._22; _23 = M._23; _24 = M._24;
    	_31 = M._31; _32 = M._32; _33 = M._33; _34 = M._34;
    	_41 = M._41; _42 = M._42; _43 = M._43; _44 = M._44;
    
    	return *this;
    }
    kopieren? (Jaja, Vergleiche von &M == this gab's auch in deren Code nirgendwo, und memcpy könnte man wohl auch benutzen.)
  3. Irgendwas anderes? Was wäre die un-hack-igste Möglichkeit, dieses Problem (Benutzung von __m128 und gleichzeitig Beachtung der MSDN) zu lösen?

Re: Jammer-Thread

Verfasst: 22.05.2012, 20:45
von Krishty
eXile hat geschrieben:
Krishty hat geschrieben:Für’s Mappen ist das alles sehr wohl brauchbar – aber nur als Hack.
Sorry, dass ich so nachhake, aber ich versuche es zu verstehen. Die Aquire&Release-Semantik beim Lesen&Schreiben ist ja eigentlich gut, denn das verhindert die von der MSDN angesprochenen ungewollten Lesezugriffe.
Richtig; allerdings möchtest du mit volatile nicht nur die Zugriffe deines Threads auf dieses Objekt ordnen, sondern alle Zugriffe.

Daher: Deklarierst du VXMMATRIX volatile & operator=(const XMMATRIX& M) volatile, möchtest du damit garantieren, dass global jeder andere Zugriff auf *this abgeschlossen ist, bevor die Zuweisung Wirkung entfaltet. Du möchtest, einfach gesagt, eine atomare Zuweisung garantieren:

    matrixMutex.lock(); // irgendwie realisiert; nur darf diese Matrix ab jetzt nicht mehr von woanders verändert werden
    _11 = M._11; _12 = M._12; _13 = M._13; _14 = M._14;
    _21 = M._21; _22 = M._22; _23 = M._23; _24 = M._24;
    _31 = M._31; _32 = M._32; _33 = M._33; _34 = M._34;
    _41 = M._41; _42 = M._42; _43 = M._43; _44 = M._44;
    // Falls die Zuweisung via SSE-MOVs geschähe, müsste hier außerdem ein _mm_sfence() hin
    matrixMutex.unlock() // Wirkung durchgesetzt; der nächste Zugriff darf kommen


Dass das Leistungsverschwendung ist, falls du einfach nur ein paar Matrizen in einen Puffer kopieren möchtest, sollte klar sein (ein Lock pro Objekt – Java lässt grüßen ;) ) – in diesem Fall weißt du ja, dass niemand anders in den Speicher schreibt. Das volatile ist ja hier bloß dafür da, dass der Compiler nicht auf die Idee kommt, implizit aus dem Speicher zu lesen; nicht, um Zugriffe auf Matrizen tatsächlich atomar zu machen.

Du möchtest an dieser Stelle also durchaus Aquire-Semantik – allerdings keine Methode, die überall funktioniert, wo du jemals A&R für eine VXMMATRIX benötigst (z.B. in einem nebenläufigen Programm, wo zwei Threads auf dieselbe Matrix zugreifen), weil die zu schwer wäre. Anstatt also einen operator = (…) volatile zu deklarieren, schreibst du eine lokale Funktion, die nur für den einen Zweck eingesetzt wird, jetzt die Daten ohne Lesezugriff in diesen Puffer zu kopieren, indem sie jedem volatile float ein entsprechendes float zuweist.

Eben weil ein einmal volatile deklarierter Operator überall Ordnung garantieren muss – und nicht nur in dem Spezialfall einer Kopie von RAM zu VRAM – und das bedeuten würde, alle Matrizen jederzeit thread-safe zu machen, hat Microsoft keinen beigelegt. Stattdessen bleibt es dir überlassen, eine Lösung für deinen Spezialfall auszutüfteln.

(Alles ohne Gewähr. Ich hasse Multithreading und meide es wie die Pest; darum kenne ich volatile-Correctness nur aus Sicht der Compilertheorie ab C++0x und kaum aus tatsächlicher Anwendung. Ich bin trotzdem von mir überzeugt und glaube, dass von den Leuten, die das Schlüsselwort einsetzen, nur ein Bruchteil die tatsächliche Bedeutung verstanden hat, sondern stattdessen noch ausschließlich an das sich ständig aktualisierende 80er-Jahre-C-Sensorregister glaubt. Das Wort hätte es vor C++0x niemals in den Standard schaffen dürfen.)

Re: Jammer-Thread

Verfasst: 22.05.2012, 21:10
von eXile
Krishty hat geschrieben:Ich bin trotzdem von mir überzeugt und glaube, dass von den Leuten, die das Schlüsselwort einsetzen, nur ein Bruchteil die tatsächliche Bedeutung verstanden hat, sondern stattdessen noch ausschließlich an das sich ständig aktualisierende 80er-Jahre-C-Sensorregister glaubt. Das Wort hätte es vor C++0x niemals in den Standard schaffen dürfen.
Und bis vor wenigen Minuten gehörte ich auch zu diesen Leuten. Danke Dir!

Da meine Daten für den Constant-Buffer eh nur POD sind, werde ich die einfach zwischen Map und Unmap in den volatile Speicher memcpy'en. Hat auch noch den Vorteil, das die Zeit des Mappens kleiner gehalten wird. ;)

Re: Jammer-Thread

Verfasst: 23.05.2012, 00:28
von Jörg
Side note: Wenn man in einen via API gemappten Speicher kopiert, ist es Aufgabe der API dafuer zu sorgen, dass waehrend des unmap-Aufrufes alle offenen Schreiboperationen abgeschlossen werden, genauso wie sie währen des Map-Aufrufes sicherstellen muss, dass ein Lesezugriff die gewuenschten Daten liefert.
Es gibt ja noch die Caches, um die man sich u.U. auf Treiberseite kuemmern muss, und die willst Du jetzt nicht auch von Hand kontrollieren? Oder Dir Gedanken darueber machen müssen, mit welchen Speicherattributen dein Mapping denn angelegt wurde?

Es wird nur kritisch, wenn der Compiler aus welchen Gruenden auch immer annehmen sollte, ein Map oder Unmap-Aufruf waere seiteneffektfrei und deshalb Lade/Schreiboperationen "ueber" die Funktionsaufrufe zieht. Und das sollte wirklich nicht passieren.

Fuer Thread-Thread Datenaustausch via regulärem Speicher (Heap, evtl. Stack) gelten Deine Anmerkungen.

Zum "Don't read from a subresource mapped for writing" eine Anmerkung. Das liegt einfach daran, dass das Mapping im meisten Fall ein Uncached, Write-Through Mapping ist. D.h. jeder Lesezugriff geht direkt ins RAM, ohne irgendwie vom Cache abgefangen zu werden. Daher die Performance-Einbußen. Vorteil: Wenn man nur schreibt, dann wird dadurch kein kostbarer Platz im Cache reserviert, der u.U. Daten rauswirft, die man braucht, nur um solche zu halten, die man nicht mehr haben will...
Also: memcpy tut es, SSE stores auch. Ohne Probleme.

Re: Jammer-Thread

Verfasst: 23.05.2012, 08:22
von Lynxeye
Side Note zur Side Note:
Jörg hat geschrieben: Zum "Don't read from a subresource mapped for writing" eine Anmerkung. Das liegt einfach daran, dass das Mapping im meisten Fall ein Uncached, Write-Through Mapping ist. D.h. jeder Lesezugriff geht direkt ins RAM, ohne irgendwie vom Cache abgefangen zu werden. Daher die Performance-Einbußen.
Das Mapping wird in der Regel Uncached,Write-Combined sein, das heißt deine Schreiboperationen werden in einem WC Puffer gehalten und in größeren Paketen geschrieben. Dieser Puffer wird entweder durch die API geflusht, sobald du ein unmap() durchführst oder von der Hardware selbst sobald du eine Leseoperation auf den betreffenden Speicherbereich durchführst, das ist der größere Performancekiller.

Der Fall, den die MSDN nennt, ist aber ein ziemlicher Sonderfall, da der MS Compiler anscheinend immer noch glaubt es ist eine gute Idee eine 0 per xor zu erzeugen, statt sie als Konstante zu setzen. Das war auf alten Pentium Prozessoren der Fall, bei modernen CPUs ist das aber nicht mehr so. Du solltest also auch ohne volatile auskommen, wenn du einfach für den Fall auf ein memset() ausweichst.

Re: Jammer-Thread

Verfasst: 23.05.2012, 10:23
von Jörg
Lynxeye hat geschrieben:Das Mapping wird in der Regel Uncached,Write-Combined sein
Ja natuerlich, fuer reine Datenpuffer mit irrelevanter individueller Schreibreihenfolge ist das besser geeignet.

Re: Jammer-Thread

Verfasst: 23.05.2012, 12:41
von CodingCat
Visual Studio 11: Kostenlose Express-Version unterstützt nur Metro-Apps - Visual Studio Express ist tot. Wenn ich die Bilder schon sehe. Eigentlich will ich nur den neuen Compiler - ob man sich den dann irgendwie in VS10 einbauen kann?

Re: Jammer-Thread

Verfasst: 23.05.2012, 13:06
von dot
Das wird wohl spätestens mit der nächsten Version wieder besser hoff ich. Im Moment wollen sie eben einfach nur Metro pushen. War ja mit der ersten Express Version auch nicht anders, damals wollte man eben .NET pushen...

Re: Jammer-Thread

Verfasst: 23.05.2012, 16:20
von Jonathan
Also ich bin ja überaus gespannt auf Clang + Eclipse. Sobald das gut läuft, könnte es sehr gut sein, dass ich mich von Visual Studio verabschiede. Jedenfalls wenn dann Eclipse für C++ so gut ist, wie für Java (immerhin ist Eclipse der Grund, aus dem Java erst erträglich geworden ist).

Re: Jammer-Thread

Verfasst: 23.05.2012, 18:12
von kaiserludi
Jonathan hat geschrieben:Also ich bin ja überaus gespannt auf Clang + Eclipse. Sobald das gut läuft, könnte es sehr gut sein, dass ich mich von Visual Studio verabschiede. Jedenfalls wenn dann Eclipse für C++ so gut ist, wie für Java (immerhin ist Eclipse der Grund, aus dem Java erst erträglich geworden ist).
Eclipse ist doch so ziemlich die mieseste IDE auf dem Markt.

Re: Jammer-Thread

Verfasst: 23.05.2012, 18:40
von Krishty
Jörg hat geschrieben:Es wird nur kritisch, wenn der Compiler aus welchen Gruenden auch immer annehmen sollte, ein Map oder Unmap-Aufruf waere seiteneffektfrei und deshalb Lade/Schreiboperationen "ueber" die Funktionsaufrufe zieht. Und das sollte wirklich nicht passieren.
Stimmt, das hatte ich vergessen – vor Aufrufen unbekannter Wirkung muss der Compiler sowieso einen konstanten Zustand herstellen.
Lynxeye hat geschrieben:da der MS Compiler anscheinend immer noch glaubt es ist eine gute Idee eine 0 per xor zu erzeugen, statt sie als Konstante zu setzen. Das war auf alten Pentium Prozessoren der Fall, bei modernen CPUs ist das aber nicht mehr so.
Sicher? Zumindest von Intel-CPUs weiß ich, dass sie es tatsächlich als Nullen mit Aufbrechen von Abhängigkeiten implementieren und nicht als wirkliches Lesen; XOR; Schreiben. Die xor-Version ist dabei kürzer als die Konstante; es passt also bei gleicher Wirkung mehr Maschinentext in den Cache.

Re: Jammer-Thread

Verfasst: 23.05.2012, 18:51
von dot
Lynxeye hat geschrieben:Der Fall, den die MSDN nennt, ist aber ein ziemlicher Sonderfall, da der MS Compiler anscheinend immer noch glaubt es ist eine gute Idee eine 0 per xor zu erzeugen, statt sie als Konstante zu setzen. Das war auf alten Pentium Prozessoren der Fall, bei modernen CPUs ist das aber nicht mehr so.
Das bildet sich der MS Compiler nicht einfach nur ein. Wie Krishty schon gesagt hat, ist das ganz besonders bei modernen CPUs genau so der Fall, nicht nur weil die Instruction kleiner ist, sondern auch was Instruction Level Parallelism angeht. Siehe dazu das Intel Software Optimization Manual ;)

Re: Jammer-Thread

Verfasst: 23.05.2012, 19:12
von CodingCat
Soll das ein Witz sein? Ich kann die Anzahl mittels Stream Output geschriebener Primitive nur per SO Query zurück zur CPU lesen, sie aber nicht einfach auf der GPU in einen (Constant) Buffer schreiben? Wie zur Hölle soll ich jetzt Stream Output Buffers im Compute Shader vararbeiten?!?

Re: Jammer-Thread

Verfasst: 23.05.2012, 19:20
von Krishty
dot hat geschrieben:sondern auch was Instruction Level Parallelism angeht.
Wo hat denn das Laden einer Konstante (natürlich angenommen, sie füllt das gesamte Register aus!) Nachteile bezüglich Nebenläufigkeit gegenüber einem XOR?

Re: Jammer-Thread

Verfasst: 23.05.2012, 19:37
von dot
mov wird von der CPU nicht als dependency breaking erkannt wie du schon gesagt hast, xor reg, reg schon...
Intel Software Optimization Manual hat geschrieben:Some micro-ops can execute to completion during rename and are removed from the
pipeline at that point, effectively costing no execution bandwidth. These include:
  • Zero idioms (dependency breaking idioms)
  • NOP
  • VZEROUPPER
  • FXCHG
[...]

Instruction parallelism can be improved by using common instructions to clear
register contents to zero. The renamer can detect them on the zero evaluation of the
destination register.

Use one of these dependency breaking idioms to clear a register when possible.
  • XOR REG,REG
  • SUB REG,REG
  • PXOR/VPXOR XMMREG,XMMREG
  • PSUBB/W/D/Q XMMREG,XMMREG
  • VPSUBB/W/D/Q XMMREG,XMMREG
  • XORPS/PD XMMREG,XMMREG
  • VXORPS/PD YMMREG, YMMREG
Since zero idioms are detected and removed by the renamer, they have no execution
latency.

Re: Jammer-Thread

Verfasst: 23.05.2012, 19:49
von Krishty
Uff; da muss ich nachforschen. Ich meine gelesen zu haben, dass ein mov reg, mem in ein gesamtes Register sehr wohl die Abhängigkeiten bricht … zumindest für SSE war ich mir absolut sicher. Es erscheint mir auch zu einfach, als dass sie das nicht eingebaut haben (wo ja sonst jede noch so absurde Nischenoptimierung drin ist). Mal suchen gehen.

Nachtrag: Du hast absolut recht.
Furthermore, they do not consume an issue port or an execution unit. So using zero idioms are preferable than moving 0’s into the register.

Re: Jammer-Thread

Verfasst: 23.05.2012, 19:57
von dot
Ich wollte gerade den gleichen Satz posten :D

Re: Jammer-Thread

Verfasst: 23.05.2012, 23:56
von Lynxeye
Ok, da hab ich wirklich falsch gelegen. Auch AMD meint das selbe:
Software Optimization Guide for AMD Family 15h Processors hat geschrieben: Rationale
AMD Family 15h processors are able to avoid the false read dependency on the XOR instruction.

Acceptable
mov reg, 0

Preferred
xor reg, reg
Ändert aber nichts an der Ursprungsaussage, dass der angeführte =0 Fall der MSDN eher die Ausnahme denn die Regel ist.

Warum diskutieren wir eigtl. die solche Sachen immer im Jammerthread?

Re: Jammer-Thread

Verfasst: 24.05.2012, 00:06
von dot
Wär auch blöd wenn AMD es genau anders rum machen würd wie Intel :D
Lynxeye hat geschrieben:Warum diskutieren wir eigtl. die solche Sachen immer im Jammerthread?
Gute Frage. Ist wohl so dass die wirklich interessanten Themen beim Jammern hochkommen ;)

Re: Jammer-Thread

Verfasst: 24.05.2012, 19:52
von Krishty
Ich persönlich poste ja kaum noch woanders und kann dementsprechend schwer woanders darüber diskutieren – das erklärt zumindest mir diesen Zufall ;)

Re: Jammer-Thread

Verfasst: 24.05.2012, 20:18
von eXile
Warum lesen sich eigentlich OpenGL-Präsentationen genauso wie damals im Jahre 2003? Die Präsentationen sehen in ihrer Essenz vollkommen gleich aus. Irgendwie hört man immer die gleichen Aussagen, frei nach dem Motto „hurrrr wenn wir weiter so viele Versionen veröffentlichen sind wir bald™ \($10^{100}$\)-mal schneller als alles dagewesene“; jedes Jahr natürlich. Ich glaub' keinem mehr von irgendeiner Seite irgendwas.

(Wobei bindless Texturen eigentlich schon schön wären. Kommt vermutlich erst in Direc3D vNext.)

Re: Jammer-Thread

Verfasst: 24.05.2012, 21:31
von CodingCat
Jedes Mal, wenn ich von Dozenten an der Uni vom Java GC vorgeschwärmt bekomme (und dabei beiläufig C++ runtergeputzt wird), wünschte ich mir, ein gewisses Forenmitglied hätte bereits so viel informatischen Ruhm erlangt, dass ich mit Autorität zitieren könnte:
Ein Garbage Collector ist ein Designfehler.
Traurig, dass C++ mit all seinem Balast und Macken noch immer die einzig mir bekannte Sprache ist, die Ressourcenverwaltung (und damit einhergehend Fehlerbehandlung) sinnvoll ermöglicht.

Re: Jammer-Thread

Verfasst: 24.05.2012, 22:06
von eXile
CodingCat hat geschrieben:Jedes Mal, wenn ich von Dozenten an der Uni vom Java GC vorgeschwärmt bekomme
Meinen Dozenten war es nach dem dritten Semester egal, in welchen Programmiersprachen wir die Probleme gelöst haben. In manchen Fällen (Ray-Tracer schreiben) fällt die Wahl ganz natürlich auf C++; welches wir natürlich im Studium nie behandelt haben.

Re: Jammer-Thread

Verfasst: 24.05.2012, 22:10
von CodingCat
Es geht nicht um irgendwelche Aufgaben, es geht um die vermittelte Idealvorstellung und die fehlenden Konzepte für strukturierte und verlässliche Programmierung. Dass Java mit seiner verkorksten Ausnahmebehandlung, und damit einhergehend der misslungenen Ressourcenverwaltung durch den GC, gegenüber C++ als Ideal dargestellt wird, tut einfach in der Seele weh.