Frage

Ich habe eine in C++ geschriebene Schleife, die für jedes Element eines großen Integer-Arrays ausgeführt wird.Innerhalb der Schleife maskiere ich einige Bits der Ganzzahl und finde dann die Min- und Max-Werte.Ich habe gehört, dass, wenn ich SSE-Anweisungen für diese Operationen verwende, diese viel schneller ausgeführt werden als eine normale Schleife, die mit bitweisem AND und if-else-Bedingungen geschrieben wird.Meine Frage ist, ob ich mich für diese SSE-Anweisungen entscheiden soll.Was passiert außerdem, wenn mein Code auf einem anderen Prozessor ausgeführt wird?Funktioniert es weiterhin oder sind diese Anweisungen prozessorspezifisch?

War es hilfreich?

Lösung

  1. SSE-Befehle sind prozessorspezifisch. nachschlagen Sie können, welcher Prozessor unterstützt die SSE-Version auf wikipedia.
  2. Wenn Code SSE schneller sein oder von vielen Faktoren abhängt nicht: Die erste ist natürlich, ob das Problem Speicher-Schranke oder CPU-bound. Wenn der Speicher-Bus wird die Engpass SSE nicht viel. Versuchen Sie, Ihre ganze Zahl Berechnungen zu vereinfachen, wenn das der Code schneller macht, ist es wahrscheinlich CPU-gebunden ist, und Sie haben eine gute Chance, es zu beschleunigen.
  3. Beachten Sie, dass SIMD-Code zu schreiben C viel schwieriger ist als das Schreiben ++ - Code, und dass der resultierende Code ist viel schwieriger zu ändern. Halten Sie den C ++ Code auf dem neuesten Stand, können Sie es als Kommentar wollen werden und die Richtigkeit Ihres Assembler-Code zu überprüfen.
  4. Denken Sie über eine Bibliothek wie das IPP, implementiert, dass gemeinsam für verschiedene Prozessoren optimiert SIMD-Operationen auf niedriger Ebene.

Andere Tipps

SIMD, von denen SSE ein Beispiel ist, können Sie den gleichen Vorgang auf mehrere Datenblöcke zu tun. Also, werden Sie keinen Vorteil erhalten, mit SSE als gerade Ersatz für die Integer-Operationen, werden Sie nur Vorteile, wenn man sich einmal die Operationen auf mehrere Datenelemente tun. Dies beinhaltet einige Datenwerte geladen, die im Speicher zusammenhängend sind, dabei die erforderliche Verarbeitung und dann mit dem nächsten Satz von Werten in der Matrix zu treten.

Probleme:

1 Wenn der Codepfad auf den Daten abhängt verarbeitet werden, SIMD wird es sehr viel schwieriger zu implementieren. Zum Beispiel:

a = array [index];
a &= mask;
a >>= shift;
if (a < somevalue)
{
  a += 2;
  array [index] = a;
}
++index;

ist nicht einfach, wie SIMD zu tun:

a1 = array [index] a2 = array [index+1] a3 = array [index+2] a4 = array [index+3]
a1 &= mask         a2 &= mask           a3 &= mask           a4 &= mask
a1 >>= shift       a2 >>= shift         a3 >>= shift         a4 >>= shift
if (a1<somevalue)  if (a2<somevalue)    if (a3<somevalue)    if (a4<somevalue)
  // help! can't conditionally perform this on each column, all columns must do the same thing
index += 4

2 Wenn die Daten dann nicht contigous ist es, die Daten in die SIMD-Befehle Laden umständlich

3 Der Code wird Prozessor spezifisch. SSE ist nur auf IA32 (Intel / AMD) und nicht alle IA32-CPUs Unterstützung SSE.

Sie müssen den Algorithmus und die Daten analysieren, um zu sehen, ob es SSE'd werden kann, und das erfordert zu wissen, wie SSE arbeitet. Es gibt viel Dokumentation auf Intel-Website.

Diese Art von Problem ist ein perfektes Beispiel dafür, wo ein guter Low-Level-Profiler unerlässlich ist.(So ​​etwas wie VTune) Es kann Ihnen eine viel fundiertere Vorstellung davon geben, wo Ihre Hotspots liegen.

Ich gehe davon aus, dass es sich bei Ihrem Hotspot wahrscheinlich um Verzweigungsvorhersagefehler handelt, die aus Min/Max-Berechnungen mit if/else resultieren.Daher sollte die Verwendung von SIMD-Intrinsics die Verwendung der Min/Max-Anweisungen ermöglichen. Es kann sich jedoch lohnen, stattdessen einfach eine verzweigungslose Min/Max-Berechnung zu verwenden.Dadurch könnten die meisten Erfolge mit weniger Schmerzen erzielt werden.

Etwas wie das:

inline int 
minimum(int a, int b)
{
  int mask = (a - b) >> 31;
  return ((a & mask) | (b & ~mask));
}

Wenn Sie SSE-Befehle verwenden, sind Sie offensichtlich auf Prozessoren beschränkt, die diese unterstützen. Das bedeutet, x86, aus dem Pentium 2 oder so (kann nicht genau erinnern, wann sie eingeführt wurden, aber es ist schon lange her)

SSE2, die, soweit ich mich erinnern kann, der ist, der Integer-Operationen bietet, ist etwas neuere (Pentium 3? Obwohl die ersten AMD Athlon Prozessoren diese Zeichen nicht unterstützt)

In jedem Fall haben Sie zwei Möglichkeiten für diese Anweisungen verwenden. Entweder den gesamten Code-Block in der Montage schreiben (wahrscheinlich eine schlechte Idee. Das macht es praktisch unmöglich, den Compiler den Code zu optimieren, und es ist sehr schwer für einen Menschen eines effizienten Assembler zu schreiben).

Alternativ kann die Spezifika mit dem Compiler zur Verfügung verwenden (wenn der Speicher dient, sind sie in der Regel in xmmintrin.h definiert)

Aber auch hier kann die Leistung nicht verbessern. SSE-Code stellt zusätzliche Anforderungen der Daten, die sie verarbeitet. Hauptsächlich ist die, zu beachten, dass die Daten über 128-Bit-Grenzen ausgerichtet werden müssen. Es sollten auch wenige oder keine Abhängigkeiten zwischen den in das gleiche Register geladenen Werte (ein 128-Bit-SSE-Register kann 4 ints halten. Das Hinzufügen der erste und der zweite ist zusammen nicht optimal. Aber das Hinzufügen alle vier ints zu den entsprechenden 4 ints in ein weiteres Register wird schnell sein)

Es kann verlockend sein, um eine Bibliothek zu verwenden, der all Low-Level-Wraps SSE Hantieren, aber das könnte auch einen möglichen Leistungsvorteil zunichte machen.

Ich weiß nicht, wie gut SSE Integer-Operation Unterstützung ist, so dass auch ein Faktor sein kann, die Leistung zu begrenzen. SSE ist vor allem in der Beschleunigung gezielt Gleitkommaoperationen.

Wenn Sie beabsichtigen, Microsoft Visual C ++ zu verwenden, sollten Sie diese Zeilen lesen:

http://www.codeproject.com/KB/recipes/sseintro.aspx

Wir haben einige Bildverarbeitungs Code implementiert, ähnlich dem, was Sie beschreiben, aber auf einem Byte-Array, In SSE. Die Beschleunigungs-zu-C-Code verglichen ist beträchtlich, abhängig von dem genauen Algorithmus mehr als ein Faktor von 4, auch in Bezug auf die Intel-Compiler. wie Sie bereits erwähnt haben Sie jedoch die folgenden Nachteile auf:

  • Portabilität. Der Code wird auf jedem Intel-CPU wie laufen, also auch AMD, aber nicht auf anderen CPUs. Das ist kein Problem für uns, weil wir die Zielhardware zu steuern. Switching-Compiler und sogar zu einem 64-Bit-OS kann auch ein Problem sein.

  • Sie haben eine steile Lernkurve, aber ich fand, dass nach erfassen Sie die Prinzipien Schreiben neue Algorithmen ist nicht so schwer.

  • Wartbarkeit. Die meisten C oder C ++ Programmierer haben keine Kenntnis von Montage / SSE.

Mein Rat an Sie, denn es gehen nur, wenn Sie wirklich die Leistung verbessert werden müssen, und Sie können keine Funktion für Ihr Problem in einer Bibliothek wie der Intel IPP finden, und wenn Sie mit den Portabilität Probleme leben können .

Ich kann von meiner experince sagen, dass SSE eine riesige (4x und höher) Speedup über eine Ebene c Version des Codes bringt (keine Inline-asm, keine intrinsics verwendet), aber handoptimierten Assembler kann Compiler-generierte Assembly, wenn der Beat Compiler kann nicht herausfinden, was der Programmierer gedacht (glauben sie mir, Compiler nicht alle möglichen Codekombinationen abdecken und sie werden nie). Oh und kann der Compiler nicht jedes Mal die Daten-Layout, dass es an der schnellstmöglichen Geschwindigkeit läuft. Aber Sie brauchen viel experince für eine Beschleunigung über einen Intel-Compiler (wenn möglich).

SSE-Befehle waren ursprünglich nur auf Intel-Chips, aber in letzter Zeit (seit Athlon?) AMD sie auch unterstützt, wenn Sie also Code gegen den SSE-Befehlssatz zu tun, sollten Sie portable zu den meist x86-Procs sein.

Dass gesagt wird, kann es nicht wert sein, Ihre Zeit SSE-Codierung zu lernen, wenn Sie bereits vertraut sind mit Assembler auf x86 ist - eine einfachere Option sein könnte Ihre Compiler-Dokumentation zu überprüfen und sehen, ob es Möglichkeiten, den Compiler zu ermöglichen automatisch generieren SSE-Code für Sie. Einige Compiler haben sehr gut Vektorisierung Schleifen auf diese Weise. (Du bist wahrscheinlich nicht überrascht zu hören, dass der Intel-Compiler einen guten Job tun:)

Schreiben Sie Code, den Compiler hilft zu verstehen, was Sie tun. GCC wird verstehen, und SSE-Code wie diese optimieren:

typedef union Vector4f
{
        // Easy constructor, defaulted to black/0 vector
    Vector4f(float a = 0, float b = 0, float c = 0, float d = 1.0f):
        X(a), Y(b), Z(c), W(d) { }

        // Cast operator, for []
    inline operator float* ()
    { 
        return (float*)this;
    }

        // Const ast operator, for const []
    inline operator const float* () const
    { 
        return (const float*)this;
    }

    // ---------------------------------------- //

    inline Vector4f operator += (const Vector4f &v)
    {
        for(int i=0; i<4; ++i)
            (*this)[i] += v[i];

        return *this;
    }

    inline Vector4f operator += (float t)
    {
        for(int i=0; i<4; ++i)
            (*this)[i] += t;

        return *this;
    }

        // Vertex / Vector 
        // Lower case xyzw components
    struct {
        float x, y, z;
        float w;
    };

        // Upper case XYZW components
    struct {
        float X, Y, Z;
        float W;
    };
};

Nur nicht vergessen, -msse -msse2 auf Ihren Build-Parameter zu haben!

Auch wenn es stimmt, dass SSE zu einigen Prozessoren spezifisch ist (SSE kann relativ sicher sein, SSE2 viel weniger in meiner Erfahrung), können Sie die CPU zur Laufzeit erkennen und laden Sie den Code dynamisch auf dem Ziel-CPU abhängig.

SIMD-Spezifika (wie SSE2) kann diese Art der Sache beschleunigen, sondern nehmen Fachwissen richtig zu verwenden. Sie sind sehr empfindlich auf die Ausrichtung und die Pipeline-Latenz; unvorsichtiger Gebrauch kann die Leistung noch schlimmer machen, als es ohne sie gewesen wäre. Sie haben eine viel einfachere und direktere Beschleunigung erhalten von einfach Cache-Prefetching mit um sicherzustellen, dass alle Ihre Ints in L1 in der Zeit sind für Sie auf ihnen zu arbeiten.

Es sei denn, Ihre Funktion einen Durchsatz von mehr als 100 Millionen ganzen Zahlen pro Sekunde benötigt, SIMD ist wahrscheinlich nicht der Mühe wert, für Sie.

Just hinzuzufügen kurz zu dem, was vorher über die verschiedenen gesagt SSE Versionen auf verschiedenen CPUs zur Verfügung stehen: Dies kann, indem man die jeweiligen Merkmals Flags durch den CPUID-Befehl (siehe zB Intel-Dokumentation) zurücküberprüft werden.

Haben Sie einen Blick auf Inline-Assembler für C / C ++, hier ist ein DDJ Artikel . Es sei denn, Sie zu 100% sicher, dass Ihr Programm wird auf einem kompatiblen Plattform laufen, sollten Sie die Empfehlungen folgen viele hier gegeben haben.

Ich stimme mit den vorherigen Plakaten. Vorteile können sehr groß sein, aber es bekommen kann eine Menge Arbeit erfordern. Intels Dokumentation zu diesen Anweisungen ist über 4K-Seiten. Möglicherweise möchten Sie EasySSE (c ++ Wrapper-Bibliothek von über intrinsics + Beispiele) frei von Ocali Inc überprüfen.

Ich nehme an meiner Zugehörigkeit zu diesem EasySSE klar.

Ich empfehle nicht, dies selbst zu tun, es sei denn, Sie sind mit der Montage einigermaßen vertraut.Die Verwendung von SSE erfordert höchstwahrscheinlich eine sorgfältige Neuorganisation Ihrer Daten Skizz weist darauf hin, und der Nutzen ist oft bestenfalls fraglich.

Es wäre wahrscheinlich viel besser für Sie, sehr kleine Schleifen zu schreiben, Ihre Daten sehr streng zu organisieren und sich einfach darauf zu verlassen, dass der Compiler dies für Sie erledigt.Sowohl der Intel C Compiler als auch GCC (seit 4.1) können Ihren Code automatisch vektorisieren und werden wahrscheinlich einen besseren Job machen als Sie.(Fügen Sie einfach -ftree-vectorize zu Ihren CXXFLAGS hinzu.)

Bearbeiten:Eine andere Sache, die ich erwähnen sollte, ist, dass mehrere Compiler dies unterstützen Montageeigenschaften, was meiner Meinung nach wahrscheinlich einfacher zu verwenden wäre als die Syntax asm() oder __asm{}.

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