Wie bekomme ich Daten aus AVX-Registern?

8

Mit MSVC 2013 und AVX 1 habe ich 8 Floats in einem Register:

%Vor%

Nun möchte ich inline void print(float) {...} für alle 8 Floats aufrufen. Es sieht so aus, als würden die Intel AVX Intrisics das ziemlich kompliziert machen:

%Vor%

aber MSVC hat nicht einmal eine dieser beiden Eigenschaften. Sicher, ich könnte die Werte in den Speicher schreiben und von dort laden, aber ich vermute, dass es auf Assembly-Ebene kein Register verschütten muss.

Bonus F: Ich würde natürlich gerne schreiben

%Vor%

aber MSVC versteht nicht, dass viele intrinsics das Schleifen entrollen müssen. Wie schreibe ich eine Schleife über die 8x32 Floats in __m256 foo ?

    
MSalters 03.06.2016, 10:51
quelle

3 Antworten

3

Vorsicht: _mm256_fmadd_ps ist nicht Teil von AVX1. FMA3 hat ein eigenes Feature-Bit und wurde nur bei Intel mit Haswell eingeführt. AMD führte FMA3 mit Piledriver ein (AVX1 + FMA4 + FMA3, kein AVX2).

Wenn Sie auf der asm-Ebene acht 32-Bit-Elemente in Integer-Registern erhalten möchten, ist es tatsächlich schneller, sie auf dem Stack zu speichern und dann skalare Lasten zu laden. pextrd ist eine 2-Uop-Anweisung für SnB-Familie und Bulldozer-Familie. (und Nehalem und Silvermont, die AVX nicht unterstützen).

Die einzige CPU, wo vextractf128 + 2x movd + 6x pextrd ist nicht schrecklich ist AMD Jaguar. (billig pextrd , und nur ein Ladeport.) (Siehe Agner Fogs Insn Tables )

Ein weit ausgerichteter Speicher kann zu überlappenden schmalen Lasten weiterleiten. (Natürlich können Sie movd verwenden, um das Low-Element zu erhalten, also haben Sie eine Mischung aus Lade-Port und ALU-Port-Ups).

Natürlich scheint Sie float s zu extrahieren, indem Sie einen Integer-Extrakt verwenden und ihn dann wieder in einen Float-Wert konvertieren. Das scheint schrecklich zu sein.

Was Sie eigentlich brauchen, ist jedes float im Low-Element seines eigenen xmm-Registers. vextractf128 ist offensichtlich die Art und Weise zu starten, indem Element 4 an die Unterseite eines neuen xmm regs gebracht wird. Dann kann 6x AVX shufps leicht die anderen drei Elemente jeder Hälfte bekommen. (Oder movshdup und movhlps haben kürzere Codierungen: kein direktes Byte).

7 Shuffle-Ups sind eine Überlegung wert, verglichen mit 1 Store und 7 Load-Ups, aber nicht, wenn Sie den Vektor trotzdem für einen Funktionsaufruf ausgeben würden.

ABI-Überlegungen:

Sie befinden sich unter Windows, wobei xmm6-15 Call-Preserved (nur die low128; die oberen Hälften von ymm6-15 sind Call-clobbered). Dies ist ein weiterer Grund, mit vextractf128 zu beginnen.

In der SysV-ABI sind alle xmm / ymm / zmm-Register call-obsled, so dass jede print() -Funktion einen Überlauf / Neuladen erfordert. Das einzige vernünftige Ding, das dort zu tun ist, ist die Speicherung im Speicher und rufe print mit dem ursprünglichen Vektor auf (d. H. Drucke das niedrige Element, weil es den Rest des Registers ignoriert). Dann movss xmm0, [rsp+4] und% code% auf dem zweiten Element, etc.

Es tut dir nicht gut, alle 8 Floats schön in 8 Vektorregs zu entpacken, denn sie müssten sowieso alle vor dem ersten Funktionsaufruf separat verschüttet werden!

    
Peter Cordes 03.06.2016, 18:17
quelle
3

Angenommen, Sie haben nur AVX (d. h. kein AVX2), dann könnten Sie so etwas tun:

%Vor%

Aber ich denke, ich würde wahrscheinlich nur eine Verbindung verwenden:

%Vor%     
Paul R 03.06.2016 11:28
quelle
1

(Unfertige Antwort. Trotzdem, falls es jemandem hilft, oder falls ich darauf zurückkommen sollte. Wenn Sie mit Skalaren arbeiten müssen, die Sie nicht vektorisieren können, ist es nicht schlecht, einen Vektor zu speichern zu einem lokalen Array, und laden Sie es dann jeweils um ein Element .)

Siehe meine andere Antwort für asm Details. Diese Antwort bezieht sich auf die C ++ - Seite der Dinge.

Mit Agner Fogs Vector Class Library überladen seine Wrapper-Klassen operator[] so, dass sie genauso funktionieren, wie Sie es tun würden erwarte, auch für nicht konstante args. Dies wird oft zu einem Store / Reload kompiliert, aber es macht es einfach, den Code in C ++ zu schreiben. Wenn die Optimierung aktiviert ist, erhalten Sie wahrscheinlich anständige Ergebnisse. (Außer, dass das Element low möglicherweise gespeichert / neu geladen wird, anstatt es nur an Ort und Stelle zu verwenden. Sie müssen also eventuell vec[0] in _mm_cvtss_f32(vec) oder etwas anderes einfügen.)

Siehe auch mein github Repo mit größtenteils nicht getesteten Änderungen an Agners VCL, um besseren Code für einige Funktionen zu generieren.

>

Es gibt ein _MM_EXTRACT_FLOAT -Wrapper-Makro , aber es ist seltsam und nur mit SSE4.1 definiert. Ich denke, dass es mit SSE4.1 extractps gehen soll (das die binäre Darstellung eines Floats in ein Integer-Register extrahieren oder in den Speicher speichern kann). Es gc kompiliert es jedoch zu einem FP shuffle, wenn das Ziel ein float ist. Seien Sie vorsichtig, dass andere Compiler es nicht zu einer tatsächlichen extractps Anweisung kompilieren, wenn Sie das Ergebnis als float haben wollen, weil Das ist nicht das, was extractps tut. (Das ist es, was insertps macht , aber ein einfacherer RP-Shuffle würde weniger Instruktions-Bytes benötigen. ZB shufps mit AVX ist großartig.)

Es ist seltsam, weil es 3 Argumente benötigt: _MM_EXTRACT_FLOAT(dest, src_m128, idx) , also kann man es nicht einmal als Initialisierer für ein float local verwenden.

Schleife über einen Vektor

gcc wird eine Schleife wie diese für Sie auflösen, aber nur mit -O1 oder höher. Bei -O0 erhalten Sie eine Fehlermeldung.

%Vor%     
Peter Cordes 05.06.2016 03:25
quelle

Tags und Links