Frage

Ich brauche eine Vorstellung davon, wie eine C ++ Cross-Plattform-Implementierung von einigen parallelizable Problemen in einer Art und Weise zu schreiben, so dass ich den Vorteil von SIMD nehmen kann (SSE, SPU, etc), falls verfügbar. Neben ich zur Laufzeit, um zwischen SIMD und nicht SIMD der Lage sein wollen.

Wie würden Sie mir empfehlen, um dieses Problem zu nähern? (Natürlich will ich nicht das Problem mehrfach für alle möglichen Optionen implementieren)

kann ich sehen, wie diese Macht nicht sehr einfache Aufgabe mit C ++, aber ich glaube, dass ich etwas fehle. Bisher meine Idee sieht wie folgt aus ... Eine Klasse CStream wird Array aus einem einzigen Feld sein. Die Verwendung mehrerer cStreams kann ich erreichen, SoA (Struktur des Arrays). Dann ein paar Funktoren verwenden kann ich gefälschte Lambda-Funktion, dass ich über die gesamte CStream ausgeführt werden.

// just for example I'm not expecting this code to compile
cStream a; // something like float[1024]
cStream b;
cStream c;

void Foo()
{
    for_each(
        AssignSIMD(c, MulSIMD(AddSIMD(a, b), a)));
}

Wo for_each den aktuellen Zeiger der Ströme sowie inlining der functors' Körper mit SIMD und ohne SIMD für die Erhöhung verantwortlich sein wird.

etwas wie folgt:

// just for example I'm not expecting this code to compile
for_each(functor<T> f)
{
#ifdef USE_SIMD
    if (simdEnabled)
        real_for_each(f<true>()); // true means use SIMD
    else
#endif
        real_for_each(f<false>());
}

Beachten Sie, dass, wenn der SIMD aktiviert wird geprüft, sobald und dass die Schleife um den Haupt Funktors.

War es hilfreich?

Lösung 2

Wenn jemand interessiert ist, dies ist der schmutzige Code, den ich mit gekommen, um eine neue Idee zu testen, dass ich mit kam, während über die Bibliothek zu lesen, dass Paul geschrieben.

Danke Paul!

// This is just a conceptual test
// I haven't profile the code and I haven't verified if the result is correct
#include <xmmintrin.h>


// This class is doing all the math
template <bool SIMD>
class cStreamF32
{
private:
    void*       m_data;
    void*       m_dataEnd;
    __m128*     m_current128;
    float*      m_current32;

public:
    cStreamF32(int size)
    {
        if (SIMD)
            m_data = _mm_malloc(sizeof(float) * size, 16);
        else
            m_data = new float[size];
    }
    ~cStreamF32()
    {
        if (SIMD)
            _mm_free(m_data);
        else
            delete[] (float*)m_data;
    }

    inline void Begin()
    {
        if (SIMD)
            m_current128 = (__m128*)m_data;
        else
            m_current32 = (float*)m_data;
    }

    inline bool Next()
    {
        if (SIMD)
        {
            m_current128++;
            return m_current128 < m_dataEnd;
        }
        else
        {
            m_current32++;
            return m_current32 < m_dataEnd;
        }
    }

    inline void operator=(const __m128 x)
    {
        *m_current128 = x;
    }
    inline void operator=(const float x)
    {
        *m_current32 = x;
    }

    inline __m128 operator+(const cStreamF32<true>& x)
    {
        return _mm_add_ss(*m_current128, *x.m_current128);
    }
    inline float operator+(const cStreamF32<false>& x)
    {
        return *m_current32 + *x.m_current32;
    }

    inline __m128 operator+(const __m128 x)
    {
        return _mm_add_ss(*m_current128, x);
    }
    inline float operator+(const float x)
    {
        return *m_current32 + x;
    }

    inline __m128 operator*(const cStreamF32<true>& x)
    {
        return _mm_mul_ss(*m_current128, *x.m_current128);
    }
    inline float operator*(const cStreamF32<false>& x)
    {
        return *m_current32 * *x.m_current32;
    }

    inline __m128 operator*(const __m128 x)
    {
        return _mm_mul_ss(*m_current128, x);
    }
    inline float operator*(const float x)
    {
        return *m_current32 * x;
    }
};

// Executes both functors
template<class T1, class T2>
void Execute(T1& functor1, T2& functor2)
{
    functor1.Begin();
    do
    {
        functor1.Exec();
    }
    while (functor1.Next());

    functor2.Begin();
    do
    {
        functor2.Exec();
    }
    while (functor2.Next());
}

// This is the implementation of the problem
template <bool SIMD>
class cTestFunctor
{
private:
    cStreamF32<SIMD> a;
    cStreamF32<SIMD> b;
    cStreamF32<SIMD> c;

public:
    cTestFunctor() : a(1024), b(1024), c(1024) { }

    inline void Exec()
    {
        c = a + b * a;
    }

    inline void Begin()
    {
        a.Begin();
        b.Begin();
        c.Begin();
    }

    inline bool Next()
    {
        a.Next();
        b.Next();
        return c.Next();
    }
};


int main (int argc, char * const argv[]) 
{
    cTestFunctor<true> functor1;
    cTestFunctor<false> functor2;

    Execute(functor1, functor2);

    return 0;
}

Andere Tipps

Sie könnten für einige Ideen in diesem Bereich an der Quelle für die MacSTL Bibliothek aussehen wollen: www.pixelglow .com / macstl /

Sie können einen Blick auf meinem Versuch SIMD / Nicht-SIMD nehmen wollen:

  • VREP , eine Templat-Basisklasse mit Spezialisierungen für SIMD (wie note unterscheidet sie zwischen floats-only SSE und SSE2, die ganzzahlige Vektoren eingebracht.).

  • Weitere nützliche V4F , V4I etc Klassen (subclassed über Zwischen v4 ).

Natürlich ist es viel mehr darauf ausgerichtet, 4-Element-Vektoren für rgba / xyz Typ Berechnungen als SoA, werden so vollständig die Puste aus, wenn 8-Wege-AVX entlang kommt, aber die allgemeinen Prinzipien könnten nützlich sein.

Der eindrucksvollste Ansatz für SIMD-Skalierung ich gesehen habe, ist der RTfact Ray-Tracing-Framework: Dias , Papier . Auch einen Blick wert. Die Forscher sind eng mit Intel verbunden ist (Saarbrücken beherbergt nun das Intel Visual Computing Institute), so dass Sie sicher, nach vorne auf die Skalierung sein kann AVX und Larrabee war auf ihren Köpfen.

Intel Ct "Datenparallelität" Vorlagenbibliothek sieht vielversprechend aus ganz zu .

Beachten Sie, dass das gegebene Beispiel entscheidet, was zum Zeitpunkt der Kompilierung ausgeführt werden (da Sie den Präprozessor verwenden), in diesem Fall können Sie komplexere Techniken verwenden, um zu entscheiden, was Sie tatsächlich ausgeführt werden soll; Zum Beispiel Tag Versand: http://cplusplus.co.il/2010 / 01/03 / tag-Dispatching / Nach dem Beispiel dort gezeigt ist, können Sie die schnelle Implementierung mit SIMD sein müssen, und die langsame ohne.

Haben Sie sich Gedanken über die Verwendung von vorhandenen Lösungen wie liboil ? Es implementiert viele gemeinsame SIMD-Operationen und zur Laufzeit entscheiden können, ob SIMD verwenden / nicht-SIMD-Code (unter Verwendung von Funktionszeigern durch eine Initialisierungsfunktion zugewiesen).

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