Wenn ich SSE3 oder AVX benutzen kann, sind dann ältere SSE-Versionen als SSE2 oder MMX verfügbar -
oder muss ich noch separat nach ihnen suchen?Sind ältere SIMD-Versionen verfügbar, wenn neuere verwendet werden?
Antwort
Im Allgemeinen waren diese additiv, aber bedenken Sie, dass es im Laufe der Jahre Unterschiede zwischen Intel und AMD Support gibt.
Wenn Sie AVX haben, können Sie auch SSE, SSE2, SSE3, SSSE3, SSE4.1 und SSE 4.2 annehmen. Denken Sie daran, dass zur Verwendung von AVX das OSXSAVE-CPUID-Bit auch aktiviert sein muss, um sicherzustellen, dass das von Ihnen verwendete Betriebssystem auch das Speichern der AVX-Register unterstützt.
sollten Sie noch explizit prüfen für alle CPUID Unterstützung, die Sie für Robustheit in Ihrem Code verwenden (sagen für AVX Überprüfung, OSXSAVE, SSE4, SSE3, SSSE3 zugleich Ihre AVX codepaths zu schützen).
#include <intrin.h>
inline bool IsAVXSupported()
{
#if defined(_M_IX86) || defined(_M_X64)
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0);
if (CPUInfo[0] < 1 )
return false;
__cpuid(CPUInfo, 1);
int ecx = 0x10000000 // AVX
| 0x8000000 // OSXSAVE
| 0x100000 // SSE 4.2
| 0x80000 // SSE 4.1
| 0x200 // SSSE3
| 0x1; // SSE3
if ((CPUInfo[2] & ecx) != ecx)
return false;
return true;
#else
return false;
#endif
}
SSE und SSE2 werden für alle Prozessoren, die x64 erforderlich nativen, also sind sie gute Basis Annahmen für den gesamten Code. Windows 8.0, Windows 8.1 und Windows 10 erfordern ausdrücklich SSE- und SSE2-Unterstützung, selbst für x86-Architekturen, so dass diese Befehlssätze ziemlich allgegenwärtig sind. Mit anderen Worten: Wenn Sie eine Prüfung auf SSE oder SSE2 nicht bestehen, beenden Sie die App einfach mit einem schwerwiegenden Fehler.
#include <windows.h>
inline bool IsSSESupported()
{
#if defined(_M_IX86) || defined(_M_X64)
return (IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0 && IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE) != 0);
#else
return false;
#endif
}
-oder
#include <intrin.h>
inline bool IsSSESupported()
{
#if defined(_M_IX86) || defined(_M_X64)
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0);
if (CPUInfo[0] < 1 )
return false;
__cpuid(CPUInfo, 1);
int edx = 0x4000000 // SSE2
| 0x2000000; // SSE
if ((CPUInfo[3] & edx) != edx)
return false;
return true;
#else
return false;
#endif
}
Auch bedenken Sie, dass MMX, x87 FPU und AMD 3DNow! * sind alle veralteten Befehlssätze für x64 nativen, so dass man sie nicht mehr aktiv verwenden sollten in neueren Code. Eine gute Faustregel ist die Vermeidung der Verwendung eines Intrinsic, der einen __m64
oder einen __m64
Datentyp zurückgibt.
Sie können diese DirectXMath blog series mit Notizen zu vielen dieser Anweisungssätze und den entsprechenden Prozessor-Support-Anforderungen überprüfen.
Hinweis (*) - Alle AMD 3DNow! Anweisungen sind veraltet, mit Ausnahme von PREFETCH
und PREFETCHW
, die übertragen wurden. Intel64-Prozessoren der ersten Generation fehlten die Unterstützung für diese Anweisungen, aber sie wurden später hinzugefügt, da sie als Teil des Kern-X64-Befehlssatzes betrachtet werden. Windows 8.1 und Windows 10 x64 erfordern insbesondere PREFETCHW
, obwohl der Test ein wenig seltsam ist. Die meisten Intel-CPUs vor Broadwell melden keine Unterstützung für PREFETCHW
über CPUID, aber sie behandeln den Opcode als eine Nicht-Op-Operation, anstatt eine 'illegale Befehls'-Ausnahme auszulösen. Daher ist der Test hier (a) ist es von CPUID unterstützt, und (b) wenn nicht, wirft PREFETCHW
zumindest keine Ausnahme.
Hier ist ein Testcode für Visual Studio, der den PREFETCHW
Test sowie viele andere CPUID-Bits für die x86- und x64-Plattformen demonstriert.
#include <intrin.h>
#include <stdio.h>
#include <windows.h>
#include <excpt.h>
void main()
{
unsigned int x = _mm_getcsr();
printf("%08X\n", x);
bool prefetchw = false;
// See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0);
if (CPUInfo[0] > 0)
{
__cpuid(CPUInfo, 1);
// EAX
{
int stepping = (CPUInfo[0] & 0xf);
int basemodel = (CPUInfo[0] >> 4) & 0xf;
int basefamily = (CPUInfo[0] >> 8) & 0xf;
int xmodel = (CPUInfo[0] >> 16) & 0xf;
int xfamily = (CPUInfo[0] >> 20) & 0xff;
int family = basefamily + xfamily;
int model = (xmodel << 4) | basemodel;
printf("Family %02X, Model %02X, Stepping %u\n", family, model, stepping);
}
// ECX
if (CPUInfo[2] & 0x20000000) // bit 29
printf("F16C\n");
if (CPUInfo[2] & 0x10000000) // bit 28
printf("AVX\n");
if (CPUInfo[2] & 0x8000000) // bit 27
printf("OSXSAVE\n");
if (CPUInfo[2] & 0x400000) // bit 22
printf("MOVBE\n");
if (CPUInfo[2] & 0x100000) // bit 20
printf("SSE4.2\n");
if (CPUInfo[2] & 0x80000) // bit 19
printf("SSE4.1\n");
if (CPUInfo[2] & 0x2000) // bit 13
printf("CMPXCHANG16B\n");
if (CPUInfo[2] & 0x1000) // bit 12
printf("FMA3\n");
if (CPUInfo[2] & 0x200) // bit 9
printf("SSSE3\n");
if (CPUInfo[2] & 0x1) // bit 0
printf("SSE3\n");
// EDX
if (CPUInfo[3] & 0x4000000) // bit 26
printf("SSE2\n");
if (CPUInfo[3] & 0x2000000) // bit 25
printf("SSE\n");
if (CPUInfo[3] & 0x800000) // bit 23
printf("MMX\n");
}
else
printf("CPU doesn't support Feature Identifiers\n");
if (CPUInfo[0] >= 7)
{
__cpuidex(CPUInfo, 7, 0);
// EBX
if (CPUInfo[1] & 0x100) // bit 8
printf("BMI2\n");
if (CPUInfo[1] & 0x20) // bit 5
printf("AVX2\n");
if (CPUInfo[1] & 0x8) // bit 3
printf("BMI\n");
}
else
printf("CPU doesn't support Structured Extended Feature Flags\n");
// Extended features
__cpuid(CPUInfo, 0x80000000);
if (CPUInfo[0] > 0x80000000)
{
__cpuid(CPUInfo, 0x80000001);
// ECX
if (CPUInfo[2] & 0x10000) // bit 16
printf("FMA4\n");
if (CPUInfo[2] & 0x800) // bit 11
printf("XOP\n");
if (CPUInfo[2] & 0x100) // bit 8
{
printf("PREFETCHW\n");
prefetchw = true;
}
if (CPUInfo[2] & 0x80) // bit 7
printf("Misalign SSE\n");
if (CPUInfo[2] & 0x40) // bit 6
printf("SSE4A\n");
if (CPUInfo[2] & 0x1) // bit 0
printf("LAHF/SAHF\n");
// EDX
if (CPUInfo[3] & 0x80000000) // bit 31
printf("3DNow!\n");
if (CPUInfo[3] & 0x40000000) // bit 30
printf("3DNowExt!\n");
if (CPUInfo[3] & 0x20000000) // bit 29
printf("x64\n");
if (CPUInfo[3] & 0x100000) // bit 20
printf("NX\n");
}
else
printf("CPU doesn't support Extended Feature Identifiers\n");
if (!prefetchw)
{
bool illegal = false;
__try
{
static const unsigned int s_data = 0xabcd0123;
_m_prefetchw(&s_data);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
illegal = true;
}
if (illegal)
{
printf("PREFETCHW is an invalid instruction on this processor\n");
}
}
}
UPDATE: Die grundlegende Herausforderung, natürlich, ist, wie Sie Systeme behandeln, die Unterstützung für AVX fehlt? Während der Befehlssatz nützlich ist, ist der größte Vorteil eines AVX-fähigen Prozessor mit der Fähigkeit, die /arch:AVX
Build-Schalter zu verwenden, die die globale Nutzung des VEX prefix für eine bessere SSE/SSE2-Code-gen ermöglicht. Das einzige Problem ist der resultierende Code DLL/EXE ist nicht kompatibel mit Systemen, die AVX-Unterstützung haben.
Daher sollten Sie idealerweise für Windows eine EXE für Nicht-AVX-Systeme erstellen (unter der Annahme, dass SSE/SSE2 nur /arch:SSE2
anstelle von x86-Code verwenden; diese Einstellung ist implizit für x64-Code), eine andere EXE, die optimiert ist für AVX (mit /arch:AVX
), und dann CPU-Erkennung verwenden, um zu bestimmen, welche EXE für ein bestimmtes System verwendet werden soll.
Zum Glück mit Xbox One können wir nur immer mit /arch::AVX
bauen, da es sich um eine feste Plattform ist ...
Ich denke nicht, dass es eine gute Idee ist, separate ausführbare Dateien für verschiedene Befehlssätze zu erstellen . Eine ausführbare Datei mit einem [CPU Dispatcher] (https://stackoverflow.com/questions/23676426/disable-avx2-functions-on-non-haswell-processors/23677889#23677889) ist meiner Meinung nach idealer. –
Der Schalter ''/arch: AVX'' bezieht sich auf ein ganzes Modul, nicht nur auf eine Funktion, aber theoretisch könnten Sie für jede Funktion eigene CPP-Dateien erstellen und mit verschiedenen Build-Einstellungen kompilieren. –
Das Hauptproblem ist, dass die Verwendung von virtuellen Funktionen (oder Funktionszeigern) zusätzlichen Aufwand verursacht, so dass es wirklich davon abhängt, wie viel Arbeit Sie in den "versandten" Funktionen tun. Dieser Entwurf wurde von der ursprünglichen D3DXMath-Bibliothek verwendet. Es lässt sich für bestimmte CPUs leicht optimieren und zur Laufzeit erkennen, aber das Ergebnis verliert bei kleinen Vorgängen viel an Leistung. Aus diesem Grund verwendet DirectXMath für Windows nur SSE und SSE2, sodass es inline aggressiv sein kann und keine "überwachten Pfade" oder "virtuellen Funktionen" verwendet werden muss. –
Als allgemeine Regel - nicht mischen verschiedene Generationen von SSE/AVX es sei denn, Sie zu haben. Wenn Sie dies tun, vergewissern Sie sich, dass Sie vzeroupper oder ähnliche Anweisungen zum Löschen des Zustands verwenden. Andernfalls können Sie Teilwerte ziehen und unwissentlich falsche Abhängigkeiten erzeugen, da die meisten Register zwischen den Modi gemeinsam genutzt werden. Selbst beim Löschen kann das Umschalten zwischen den Modi zu Strafen führen auf die genaue Mikroarchitektur.
Literaturhinweise - https://software.intel.com/sites/default/files/m/d/4/1/d/8/11MC12_Avoiding_2BAVX-SSE_2BTransition_2BPenalties_2Brh_2Bfinal.pdf
Um fair zu sein, tritt dieses Mischen Problem nur auf, wenn Sie nicht-VEX mischen Code mit VEX mit Code (dh AVX oder AVX2 mit früheren 2 Operanden SSE-Anweisungen mischen). Ansonsten ist es gut und notwendig, zwischen den Generationen zu mischen - notwendig, weil jede Erweiterung keine eigenständige USA ist, sondern auf der letzten aufbaut. – BeeOnRope
@BeeOnRope, Es ist in Ordnung zu mischen, aber Sie müssen sich vor dem Problem schützen, über das ich sprach. Siehe - http: // stackoverflow.com/questions/7839925/using-avx-cpu-anweisungen-poor-performance-ohne-archavx – Leeor
Ja genau, aber selbst diese Frage ist ein wenig unklar, da es nicht darauf hinweist, dass fast alle interessanten SSE-Anweisungen eine VEX haben. kodierte Version - einschließlich 128-Bit-Varianten. Viele Leute nennen diese SSE-Anweisungen immer noch locker. Zum Beispiel hören Sie, dass 'pshufb' eine 'SSE3'-Anweisung ist. In der Tat erzeugt ein Aufruf wie 'phsufb xmm0, xmm1' eine Nicht-VEX-Kodierung, die das von Ihnen erwähnte Leistungsproblem verursachen würde. Ändert man das einfach in das identisch benannte 'pshufb xmm0, xmm0, xmm1', ändert es jedoch in VEX-Codierung und vermeidet das Problem. – BeeOnRope
See Chucks Antwort für gute Ratschläge, was Sie tun sollten. Sehen Sie diese Antwort für eine wörtliche Antwort auf die gestellte Frage, falls Sie neugierig sind.
AVX-Unterstützung garantiert absolut Unterstützung für alle Intel SSE * Befehlssätze, da es enthält VEX-codierte Versionen von allen von ihnen. Wie Chuck darauf hinweist, kannst du gleichzeitig mit einer Bitmaske nach früheren suchen, ohne deinen Code aufzublähen, aber verschwinde nicht.
Beachten Sie, dass POPCNT, TZCNT und solche Sachen nicht Teil von SSE-irgendetwas sind. POPCNT hat ein eigenes Feature-Bit. LZCNT hat auch ein eigenes Feature-Bit, da AMD es separat von BMI1 eingeführt hat. TZCNT ist jedoch nur ein Teil von BMI1. Da einige BMI1-Befehle VEX-Codierungen verwenden, haben auch Pentium/Celeron-CPUs der neuesten Generation (wie Skylake Pentium) keinen BMI1. :(Ich denke, Intel wollte nur AVX/AVX2 wegzulassen, wahrscheinlich so konnten sie CPUs mit fehlerhaften oberen Bahnen der Ausführungseinheiten als Pentiums verkaufen, und sie tun dies in den Decodern VEX Unterstützung durch die Deaktivierung.
Intel Die SSE-Unterstützung war in allen bisher veröffentlichten CPUs inkrementell, SSE4.1 beinhaltet SSSE3, SSE3, SSE2 und SSE und SSE4.2 impliziert alle vorhergehenden. Ich bin mir nicht sicher, ob eine offizielle x86-Dokumentation die Möglichkeit einer CPU mit SSE4.1-Unterstützung, aber nicht SSSE3 (dh PSHUFB weglassen, was möglicherweise teuer zu implementieren ist.) In der Praxis ist es jedoch äußerst unwahrscheinlich, da dies die Annahmen vieler Menschen verletzen würde. Wie ich schon sagte, könnte es sogar so sein offiziell verboten, aber ich habe nicht sorgfältig geprüft
AVX enthält keine AMD SSE4a oder AMD XOP. AMD-Erweiterungen müssen extra geprüft werden. Beachten Sie auch, dass die neuesten AMD-CPUs XOP-Unterstützung verlieren. (Intel hat es nie übernommen, daher schreiben die meisten Leute keinen Code, um es auszunutzen. Für AMD sind diese Transistoren also meist verschwendet. Es hat einige nette Sachen, wie eine 2-Quell-Byte-Permutation, die eine Byte-LUT zweimal erlaubt Breit wie PSHUFB, ohne die In-Lane-Begrenzung von AVX2 VPSHUFB ymm).
SSE2 ist Grundlage für die x86-64-Architektur. Sie müssen nicht nach SSE- oder SSE2-Unterstützung in 64-Bit-Builds suchen. Ich vergesse, wenn MMX auch Baseline ist. Fast sicher.
Der SSE-Befehlssatz enthält einige Anweisungen für MMX-Register. (z. B. PMAXSW mm1, mm2/m64
war neu mit SSE. Die XMM-Version ist Teil von SSE2.) Selbst eine 32-Bit-CPU, die SSE unterstützt, benötigt MMX-Register. Es wäre Wahnsinn, MMX-Register zu haben, aber nur die SSE-Befehle zu unterstützen, die diese verwenden, nicht die ursprünglichen MMX-Befehle (z. B. movq mm0, [mem]
). Ich habe jedoch nichts Definitives gefunden, das die Möglichkeit einer x86-basierten Deathstation 9000 mit SSE-, aber nicht MMX-CPUID-Feature-Bits ausschließt, aber ich habe mich nicht in Intels offizielle x86-Handbücher vertieft. (Siehe das x86 Tag Wiki für Links).
Verwenden Sie MMX sowieso nicht, es ist im Allgemeinen langsamer, auch wenn Sie nur 64 Bits gleichzeitig bearbeiten müssen, in der unteren Hälfte eines XMM-Registers. Die neuesten CPUs (wie Intel Skylake) haben einen geringeren Durchsatz für die MMX-Versionen einiger Befehle als für die XMM-Version. In manchen Fällen sogar noch schlechtere Latenzzeiten. Zum Beispiel, nach Agner Fog's testing, PACKSSWB mm0, mm1
ist 3 Ups, mit 2c Latenz, auf Skylake. Die 128-Bit- und 256-Bit-XMM/YMM-Versionen sind 1 Up mit 1c Latenz.
In der Regel sollten Sie vor der Verwendung nach einer Funktion suchen. Die CPUID-Anweisungen, die bestimmen, ob Sie SSE3 oder AVX haben, bestimmen jedoch auch, ob Sie SSE2 oder MMX haben. Wenn Sie die Ausgaben dieser CPUID-Anweisungen nur in den entsprechenden Variablen speichern, können Sie einen einzelnen Bit-Test durchführen, wenn Sie eine bestimmte Anweisung verwenden möchten. –
Das ist auf SO schon mal aufgekommen, aber ich finde das Duplikat zur Zeit nicht ... –
Intel CPUs sind immer abwärtskompatibel. Wenn es also einen Befehlssatz unterstützt, unterstützt es alle älteren Versionen –