Frage

Gibt es einen Unterschied zwischen logischen SSE -Intrinsiken für verschiedene Typen? Zum Beispiel, wenn wir nehmen oder operieren, gibt es drei Intrinsics: _mm_or_ps, _mm_or_pd und _mm_or_si128, die alle dasselbe tun: Berechnen bitweise Oder von ihren Operanden. Meine Fragen:

  1. Gibt es einen Unterschied zwischen der Verwendung des einen oder anderen Intrinsischen (mit geeignetem Typguss)? Wird es nicht versteckte Kosten wie eine längere Ausführung in einer bestimmten Situation geben?

  2. Diese Intrinsics karten auf drei verschiedene X86 -Anweisungen (POR, ORPS, ORPD). Hat jemand irgendwelche Ideen, warum Intel wertvolle Opcode -Raum für mehrere Anweisungen verschwendet, die dasselbe tun?

War es hilfreich?

Lösung

Ich denke, alle drei sind effektiv gleich, dh 128 -Bit -Bitgewise -Operationen. Der Grund, warum verschiedene Formen existieren, ist wahrscheinlich historisch, aber ich bin nicht sicher. Ich denke es ist möglich Dass es in den schwimmenden Punktversionen zusätzliches Verhalten gibt, z. B. wenn es NANS gibt, aber dies ist reines Vermutung. Für normale Eingaben scheinen die Anweisungen austauschbar zu sein, z. B.

#include <stdio.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include <xmmintrin.h>

int main(void)
{
    __m128i a = _mm_set1_epi32(1);
    __m128i b = _mm_set1_epi32(2);
    __m128i c = _mm_or_si128(a, b);

    __m128 x = _mm_set1_ps(1.25f);
    __m128 y = _mm_set1_ps(1.5f);
    __m128 z = _mm_or_ps(x, y);

    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);

    c = (__m128i)_mm_or_ps((__m128)a, (__m128)b);
    z = (__m128)_mm_or_si128((__m128i)x, (__m128i)y);

    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);

    return 0;
}

$ gcc -Wall -msse3 por.c -o por

$ ./por

a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000
a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000

Andere Tipps

  1. Gibt es einen Unterschied zwischen der Verwendung des einen oder anderen Intrinsischen (mit geeignetem Typguss)? Wird es nicht versteckte Kosten wie eine längere Ausführung in einer bestimmten Situation geben?

Ja, es kann Leistungsgründe geben, eine gegen das andere auszuwählen.

1: Manchmal gibt es ein oder zwei zusätzliche Latenzzyklus (Weiterleitungsverzögerung), wenn die Ausgabe einer Ganzzahlausführungseinheit an die Eingabe einer FP -Ausführungseinheit geleitet werden muss oder umgekehrt. Es braucht viele Drähte, um 128B Daten auf viele mögliche Ziele zu verschieben, sodass CPU -Designer Kompromisse eingehen müssen, z.

Sehen Diese Antwort, oder Microarchitecture Doc von Agner Fog Für Bypass-Delays. Suche nach "Datenbypass -Verzögerungen auf Nehalem" in Agners DOC; Es hat einige gute praktische Beispiele und Diskussionen. Er hat einen Abschnitt für jeden Mikroarch, den er analysiert hat.

Die Verzögerungen für das Übergeben von Daten zwischen den verschiedenen Domänen oder verschiedenen Arten von Registern sind jedoch auf der Sandbrücke und der Ivy -Brücke kleiner als auf dem Nehalem und oft Null. - Micro Arch Doc von Agner Fog

Denken Sie daran, dass die Latenz keine Rolle spielt, ob sie sich nicht auf dem kritischen Weg Ihres Code befindet. Verwendung pshufd Anstatt von movaps + shufps Kann ein Sieg sein, wenn der UOP -Durchsatz Ihr Engpass ist und nicht die Latenz Ihres kritischen Weges.

2: Das ...ps Die Version nimmt 1 weniger Code als die anderen beiden. Dies richtet sich die folgenden Anweisungen anders aus, was für die Decoder- und/oder UOP -Cache -Linien von Bedeutung ist.

3: Die jüngste Intel -CPUs können die FP -Versionen nur auf Port5 ausführen.

  • Merom (Core2) und Penryn: orps kann auf p0/p1/p5 laufen, aber nur integer-domain. Vermutlich sind alle 3 Versionen in genau das gleiche UOP dekodiert. So tritt die Verspätung durch die Domänen weiter. (AMD -CPUs tun dies auch: FP -Bitgewise -Anweisungen in der IVEC -Domäne ausgeführt.)

  • Nehalem / Sandybridge / IVB / Haswell / Broadwell: por kann auf p0/p1/p5 laufen, aber orps kann nur auf Port5 ausgeführt. P5 wird auch von Mischungen benötigt, aber die Einheiten von FMA, FP und FP MUL befinden sich auf den Ports 0/1.

  • Skylake: por und orps Beide haben einen 3-pro-Zyklus-Durchsatz. Informationen über die Weiterleitung von Verzögerungen sind noch nicht verfügbar.

Beachten vpor ymm, ymm Benötigt AVX2. Dies war wahrscheinlich nicht der Grund für die Veränderung, da Nehalem dies tat.

Wie man mit Bedacht auswählt:

Wenn der logische OP -Durchsatz auf Port5 ein Engpass sein könnte, verwenden Sie die Ganzzahlversionen auch für FP -Daten. Dies gilt insbesondere dann, wenn Sie Integer-Mischungen oder andere Datenbewegungsanweisungen verwenden möchten.

AMD-CPUs verwenden immer die Integer-Domäne für Logische. Wenn Sie also mehrere Integer-Domain-Dinge zu tun haben, machen Sie sie alle gleichzeitig, um die Roundreise zwischen Domänen zu minimieren. Kürzere Latenzen werden die Dinge schneller aus dem Nachbestellpuffer abbauen, auch wenn eine DEP -Kette nicht der Engpass für Ihren Code ist.

Wenn Sie nur ein bisschen in FP -Vektoren zwischen FP -Add und MUL -Anweisungen einstellen/löschen/klären möchten, verwenden Sie die ...ps Logische, selbst bei doppelten Präzisionsdaten, da Einzel- und Doppel-FP bei jeder existierenden CPU die gleiche Domäne sind und die ...ps Versionen sind ein Byte kürzer.

Es gibt praktische Gründe für die Verwendung der Verwendung des Menschen-Faktors ...pd Versionen, die oft das Speichern von 1 Byte des Codes überwiegen. Die Lesbarkeit Ihres Codes durch andere Menschen ist ein Faktor: Sie werden sich fragen, warum Sie Ihre Daten als Singles behandeln, wenn sie tatsächlich verdoppelt werden. Esp. Mit C/C ++ - Intrinsics, die Ihren Code mit Abgüssen zwischeneinander übertragen __mm256 und __mm256d ist es nicht wert. Wenn das Stimmen auf der Ebene der Inn -Ausrichtung wichtig ist, schreiben Sie direkt in ASM, nicht in Intrinsics! (Wenn Sie den Anweisungen länger haben, kann die Dinge für die Dichte der UOP -Cache -Linien und/oder Decoder besser ausgerichtet sein.)

Verwenden Sie für Ganzzahldaten die Ganzzahlversionen. Das Speichern eines Anweisungsbytes ist das Bypass-Delay nicht wert, und Integer-Code hält Port5 häufig vollständig mit Mischungen beschäftigt. Für Haswell wurden viele Anweisungen zum Einfügen / Einfügen / Extrakt / Pack / Pack / Auspacken nur P5 anstelle von P1 / P5 für SNB / IVB.

  1. Diese Intrinsics -Karten auf drei verschiedene x86 -Anweisungen (por, orps, orpd). Hat jemand irgendwelche Ideen, warum Intel wertvolle Opcode -Raum für mehrere Anweisungen verschwendet, die dasselbe tun?

Wenn Sie sich die Geschichte dieser Anweisungssätze ansehen, können Sie sehen, wie wir hierher gekommen sind.

por  (MMX):     0F EB /r
orps (SSE):     0F 56 /r
orpd (SSE2): 66 0F 56 /r
por  (SSE2): 66 0F EB /r

MMX existierte vor SSE, also sieht es aus wie Opcodes für SSE (...ps) Anweisungen wurden aus demselben ausgewählt 0F xx Platz. Dann für sse2 die ...pd Version hinzugefügt a 66 operandgröße Präfix an die ...ps Opcode und die Ganzzahlversion fügte a hinzu 66 Präfix für die MMX -Version.

Sie könnte habe weggelassen orpd und/oder por, aber sie haben es nicht getan. Vielleicht dachten sie, dass zukünftige CPU -Designs möglicherweise längere Weiterleitungspfade zwischen verschiedenen Domänen haben, und daher wäre es ein größeres Geschäft, die Übereinstimmung für Ihre Daten zu verwenden. Obwohl es separate Opcodes gibt, behandelten AMD und frühe Intel sie alle gleich wie int-vektor.

Gemäß den Richtlinien für Intel und AMD -Optimierung erzeugen das Mischen von OP -Typen mit Datentypen eine Leistung, die als CPU intern gedreht wird. Die interne Tags 64 -Bit -Hälften des Registers für einen bestimmten Datentyp. Dies scheint größtenteils die Rohrauskleidung zu beeinflussen, da die Anweisung dekodiert ist und die UOPS geplant sind. Funktionell erzeugen sie das gleiche Ergebnis. Die neueren Versionen für die Ganzzahl -Datentypen haben eine größere Codierung und nehmen im Codesegment mehr Platz ein. Wenn also die Codegröße ein Problem ist, verwenden Sie die alten OPs, da diese kleinere Codierung aufweisen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top