Domanda

Ho bisogno di qualche idea di come scrivere un C ++ croce implementazione della piattaforma di alcuni problemi parallelizzabili in modo così posso approfittare di SIMD (SSE, SPU, ecc) se disponibile. Così come io voglio essere in grado in fase di esecuzione per passare da SIMD e non SIMD.

Come suggeriresti me per affrontare questo problema? (Naturalmente non voglio implementare il problema più volte per tutte le opzioni possibili)

Posso vedere come questo potrebbe non essere molto facile compito con C ++, ma credo che mi manca qualcosa. Finora la mia idea assomiglia a questo ... Un cStream classe sarà array di un singolo campo. L'utilizzo di più cStreams posso raggiungere SoA (Struttura del Array). Quindi, utilizzando un paio di Funtori posso fingere funzione lambda che ho bisogno di essere eseguito su tutto il cStream.

// 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)));
}

Dove for_each sarà responsabile per incrementare il puntatore attuale dei corsi d'acqua, così come inlining corpo i funtori con SIMD e senza SIMD.

qualcosa in questo modo:

// 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>());
}

Si noti che se il SIMD è abilitato viene controllato una volta che il ciclo è di circa il funtore principale.

È stato utile?

Soluzione 2

Se qualcuno è interessato questo è il codice sporca vengo con per testare una nuova idea che mi è venuto con durante la lettura sulla biblioteca che Paolo ha scritto.

Grazie Paolo!

// 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;
}

Altri suggerimenti

Si potrebbe desiderare di guardare il sorgente per la libreria MacSTL per alcune idee in questa area: www.pixelglow .com / macstl /

Si potrebbe desiderare di prendere uno sguardo al mio tentativo di SIMD / non-SIMD:

  • vrep , una classe base template con specializzazioni per SIMD (si noti come essa distingue galleggianti sola SSE e SSE2, che ha introdotto vettori interi.).

  • v4f , V4i classi ecc (sottoclasse via intermedia v4 ).

Naturalmente è molto più orientata verso vettori 4-elemento per il RGBA / xyz tipo calcoli di dichiarazione di affidabilità, così sarà completamente a corto di vapore quando a 8 vie AVX arriva, ma i principi generali potrebbero essere utili.

L'approccio più impressionante per SIMD-scaling che ho visto è il quadro di ray-tracing RTFact: scivoli , carta . Vale la pena dare un'occhiata. I ricercatori sono strettamente associati con Intel (Saarbrucken ora ospita il processore Intel visiva Computing Institute) in modo da poter essere sicuri avanti ridimensionamento sul AVX e Larrabee era nelle loro menti.

Ct "parallelismo dei dati" libreria di modelli sembra abbastanza promettente troppo .

Si noti che l'esempio dato decide cosa eseguire in fase di compilazione (dal momento che si sta utilizzando il preprocessore), in questo caso è possibile utilizzare le tecniche più complesse per decidere che cosa si vuole realmente eseguire; Ad esempio, Tag spedizione: http://cplusplus.co.il/2010 / 01/03 / tag-invio / Seguendo l'esempio mostrato lì, si potrebbe avere la rapida implementazione sia con SIMD, e il lento, senza.

Hai pensato di usare le soluzioni esistenti, come Liboil ? Implementa un sacco di SIMD comuni operazioni e possono decidere in fase di esecuzione se utilizzare SIMD / non-SIMD codice (usando puntatori a funzione assegnati da una funzione di inizializzazione).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top