2015-10-15 13 views
10

Mam dziwny problem z niektórymi kodami SSE2 i AVX, nad którymi pracowałem. Buduję swoją aplikację za pomocą GCC, który wykrywa funkcje procesora runtime. Pliki wynikowe są zbudowane z oddzielnych flagi dla każdej funkcji procesora, na przykład:SSE działa wolno po użyciu 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 

Kiedy po raz pierwszy uruchomić program, uważam, że procedury SSE2 są za normalne z ładnym prędkości podbicia nad rutyny spoza SSE (około 100% szybciej). Po uruchomieniu dowolnej procedury AVX, ta sama procedura SSE2 działa teraz znacznie wolniej.

Czy ktoś mógłby wyjaśnić, jaka jest tego przyczyna?

Przed uruchomieniem procedury AVX wszystkie testy są około 80-130% szybsze niż matematyka FPU, jak widać tutaj, po uruchomieniu procedury AVX, procedury SSE są znacznie wolniejsze.

Jeśli pominę procedury testowe AVX, nigdy nie zobaczę utraty wydajności.

Oto moja SSE2 rutyna

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

a wersja AVX tego samego.

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

Uruchomiłem to również przez valgrind, który wykrywa brak błędów.

+1

W jaki sposób mierzy się czas? – Gilles

+0

@Gilles używając 'clock_gettime (CLOCK_MONOTONIC, & start);' przed i po, a następnie obliczając różnicę. – Geoffrey

+0

Wpadłem w dziwne problemy z mieszanym kodem SSEX i AVX ..., głównie z powodu generowania kodu Link Time/etc. problemy. Spójrz (i być może dodaj) swoje pliki zespołu. – Christopher

Odpowiedz

15

Mieszanie kodu AVX i starszego kodu SSE pociąga za sobą obniżenie wydajności. Najbardziej sensownym rozwiązaniem jest wykonanie instrukcji VZEROALL po segmencie AVX kodu, szczególnie tuż przed wykonaniem kodu SSE.

Zgodnie ze schematem Intela kara przy przejściu do stanu C lub wycofaniu z niego (starszy SSE z zapisaną górną częścią rejestrów AVX) jest rzędu 100 cykli zegarowych. Pozostałe przejścia są tylko 1 cykl:

Referencje:

+2

Ten problem może wpływać na drastyczne efekty, które wydają się zupełnie niezwiązane z tą karą. Zobacz [to pytanie] (http://stackoverflow.com/q/21960229/2542702), gdzie OP zobaczył przyspieszenie z ponad 500 wątków w systemie, który miał tylko osiem hyper-wątków. –

Powiązane problemy