Anstatt eine alternative SSE-Implementierung für Ihren skalaren Code von Hand zu programmieren, empfehle ich Ihnen dringend, einen Blick auf OpenCL . Es handelt sich um ein herstellerneutrales, portables, plattformübergreifendes System für rechenintensive Anwendungen (und es ist in hohem Maße "buzzword-konform"!). Sie können Ihren Algorithmus in einer Teilmenge von C99 schreiben, die für vektorisierte Operationen ausgelegt ist, was viel einfacher ist als SSE von Hand zu programmieren. Und das Beste ist, dass OpenCL zur Laufzeit die beste Implementierung generiert, die dann entweder auf der GPU ou auf der CPU. Sie bekommen also im Grunde den SSE-Code für Sie geschrieben.
In meinem Code gibt es einige Stellen, an denen derselbe Vorgang sehr oft für einen großen Datensatz wiederholt wird. In einigen Fällen dauert es sehr lange, diese zu verarbeiten.
Ihre Anwendung klingt nach genau der Art von Problem, für das OpenCL entwickelt wurde. Das Schreiben von alternativen Funktionen in SSE würde die Ausführungsgeschwindigkeit sicherlich verbessern, ist aber mit viel Arbeit verbunden, um sie zu schreiben und zu debuggen.
Gibt es eine compiler- und betriebssystemunabhängige Möglichkeit, den Code so zu schreiben, dass er die Vorteile der SSE-Befehle nutzt? Ich mag die VC++ intrinsics, die SSE-Operationen enthalten, aber ich habe keine compilerübergreifenden Lösungen gefunden.
Ja. Die SSE-Intrinsics wurden von Intel im Wesentlichen standardisiert, so dass die gleichen Funktionen unter Windows, Linux und Mac (insbesondere mit Visual C++ und GNU g++) gleich funktionieren.
Ich muss noch einige CPUs unterstützen, die entweder keine oder nur begrenzte SSE-Unterstützung haben (z. B. Intel Celeron). Gibt es eine Möglichkeit zu vermeiden, dass ich verschiedene Versionen des Programms erstellen muss, z. B. mit einer Art "Laufzeit-Linker", der entweder den Basis- oder den SSE-optimierten Code einbindet, je nachdem, auf welcher CPU er beim Start des Prozesses läuft?
Sie könnten dies tun (z. B. mit dlopen()
), aber es ist eine sehr komplexe Lösung. Viel einfacher wäre es (in C), eine Funktionsschnittstelle zu definieren und die entsprechende Version der optimierten Funktion über einen Funktionszeiger aufzurufen, oder in C++ verschiedene Implementierungsklassen zu verwenden, je nach der festgestellten CPU.
Bei OpenCL ist dies nicht erforderlich, da der Code zur Laufzeit für die jeweilige Architektur generiert wird.
Wie sieht es mit anderen CPU-Erweiterungen aus? Ein Blick auf die Befehlssätze verschiedener Intel- und AMD-CPUs zeigt, dass es ein paar davon gibt.
Innerhalb des SSE-Befehlssatzes gibt es viele verschiedene Varianten. Es kann recht schwierig sein, denselben Algorithmus in verschiedenen Untergruppen von SSE zu kodieren, wenn bestimmte Anweisungen nicht vorhanden sind. Ich schlage vor (zumindest für den Anfang), dass Sie eine minimal unterstützte Stufe wählen, z. B. SSE2, und auf älteren Maschinen auf die skalare Implementierung zurückgreifen.
Dies ist auch eine ideale Situation für Unit-/Regressionstests, die sehr wichtig sind, um sicherzustellen, dass Ihre verschiedenen Implementierungen die gleichen Ergebnisse liefern. Stellen Sie eine Testreihe mit Eingabedaten und bekannt guten Ausgabedaten zusammen und lassen Sie die gleichen Daten durch beide Versionen der Verarbeitungsfunktion laufen. Möglicherweise müssen Sie einen Präzisionstest durchführen (d. h. die Epsilon-Differenz zwischen dem Ergebnis und der richtigen Antwort liegt unter 1e6
zum Beispiel). Dies wird die Fehlersuche erheblich erleichtern, und wenn Sie in Ihr Test-Framework ein hochauflösendes Timing einbauen, können Sie gleichzeitig die Leistungsverbesserungen vergleichen.