2015-10-15 13 views
10

Ho un problema strano con alcuni codici SSE2 e AVX su cui ho lavorato. Sto costruendo la mia applicazione utilizzando GCC quale rilevamento delle funzioni della cpu di runtime. I file oggetto sono costruiti con bandiere separate per ogni funzione della CPU, ad esempio:SSE funziona lentamente dopo l'utilizzo di AVX

g++ -c -o ConvertSamples_SSE.o ConvertSamples_SSE.cpp -std=c++11 -fPIC -O0 -g -Wall -I./include -msse 
g++ -c -o ConvertSamples_SSE2.o ConvertSamples_SSE2.cpp -std=c++11 -fPIC -O0 -g -Wall -I./include -msse2 
g++ -c -o ConvertSamples_AVX.o ConvertSamples_AVX.cpp -std=c++11 -fPIC -O0 -g -Wall -I./include -mavx 

Quando ho primo avvio il programma, trovo che le routine SSE2 sono come al solito con una spinta piacevole di velocità nel corso degli routine non SSE (circa il 100% più veloce). Dopo aver eseguito qualsiasi routine AVX, la stessa routine SSE2 ora funziona molto più lentamente.

Qualcuno potrebbe spiegare quale potrebbe essere la causa?

Prima dell'esecuzione della routine AVX, tutti i test sono di circa 80-130% più veloci rispetto alla matematica FPU, come si può vedere qui, dopo l'esecuzione della routine AVX, le routine SSE sono molto più lente.

Se ignoro le routine di test AVX, non vedo mai questa perdita di prestazioni.

Qui è la mia routine di SSE2

void Float_S16(const float *in, int16_t *out, const unsigned int samples) 
{ 
    static float ratio = (float)Limits<int16_t>::range()/(float)Limits<float>::range(); 
    static __m128 mul = _mm_set_ps1(ratio); 

    unsigned int i; 
    for (i = 0; i < samples - 3; i += 4, in += 4, out += 4) 
    { 
    __m128i con = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(in), mul)); 
    out[0] = ((int16_t*)&con)[0]; 
    out[1] = ((int16_t*)&con)[2]; 
    out[2] = ((int16_t*)&con)[4]; 
    out[3] = ((int16_t*)&con)[6]; 
    } 

    for (; i < samples; ++i, ++in, ++out) 
    *out = (int16_t)lrint(*in * ratio); 
} 

E la versione AVX dello stesso.

void Float_S16(const float *in, int16_t *out, const unsigned int samples) 
{ 
    static float ratio = (float)Limits<int16_t>::range()/(float)Limits<float>::range(); 
    static __m256 mul = _mm256_set1_ps(ratio); 

    unsigned int i; 
    for (i = 0; i < samples - 7; i += 8, in += 8, out += 8) 
    { 
    __m256i con = _mm256_cvtps_epi32(_mm256_mul_ps(_mm256_load_ps(in), mul)); 
    out[0] = ((int16_t*)&con)[0]; 
    out[1] = ((int16_t*)&con)[2]; 
    out[2] = ((int16_t*)&con)[4]; 
    out[3] = ((int16_t*)&con)[6]; 
    out[4] = ((int16_t*)&con)[8]; 
    out[5] = ((int16_t*)&con)[10]; 
    out[6] = ((int16_t*)&con)[12]; 
    out[7] = ((int16_t*)&con)[14]; 
    } 

    for(; i < samples; ++i, ++in, ++out) 
    *out = (int16_t)lrint(*in * ratio); 
} 

Ho anche eseguito questo tramite valgrind che non rileva errori.

+1

Come viene misurato il tempo? – Gilles

+0

@Gilles utilizzando 'clock_gettime (CLOCK_MONOTONIC, & start);' prima e dopo, quindi calcola la differenza. – Geoffrey

+0

Ho incontrato problemi curiosi con SSEX e codice AVX misti ..., principalmente perché la generazione del codice Link Time/etc. i problemi. Guarda (e forse pubblica) i tuoi file di assemblaggio. – Christopher

risposta

15

Il missaggio del codice AVX e del codice SSE precedente comporta una riduzione delle prestazioni. La soluzione più ragionevole è eseguire l'istruzione VZEROALL dopo un segmento di codice AVX, specialmente prima di eseguire il codice SSE.

Come da diagramma di Intel, la penalità durante la transizione in o fuori dallo stato C (SSE legacy con metà superiore dei registri AVX salvati) è nell'ordine di 100 cicli di clock. Le altre transizioni sono solo 1 ciclo:

Riferimenti:

+2

Questo problema può avere effetti drastici che sembrano totalmente estranei a questa penalità. Vedere [questa domanda] (http://stackoverflow.com/q/21960229/2542702) dove l'OP ha visto una velocità con oltre 500 thread su un sistema che aveva solo otto hyper-thread. –

Problemi correlati