2015-10-15 4 views
10

У меня странная проблема с некоторыми SSE2 и кодом AVX, над которыми я работаю. Я создаю свое приложение, используя GCC, которое обнаруживает функцию cpu. Объектные файлы построены с отдельными флагами для каждой функции центрального процессора, например:SSE работает медленно после использования 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 

Когда я первый запустить программу, я считаю, что подпрограммы SSE2 являются в нормальном с хорошим повышением скорости над не являющимися рутинами SSE (примерно на 100% быстрее). После запуска любой процедуры AVX точная процедура SSE2 теперь выполняется намного медленнее.

Может кто-нибудь объяснить причину этого?

До запуска процедуры AVX все тесты примерно на 80-130% быстрее, чем математика FPU, как можно видеть здесь, после запуска процедуры AVX процедуры SSE выполняются намного медленнее.

Если я пропущу процедуры тестирования AVX, я никогда не вижу этой потери производительности.

Вот мой 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); 
} 

И версия AVX то же самое.

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

Я также запускаю это через valgrind, который не обнаруживает ошибок.

+1

Как время измеряется? – Gilles

+0

@ Gilles, используя 'clock_gettime (CLOCK_MONOTONIC, & start);' до и после, затем вычисляя разницу. – Geoffrey

+0

У меня возникли любопытные проблемы со смешанным кодом SSEX и AVX ..., в основном потому, что генерация кода Time Time/etc. проблемы. Посмотрите (и, возможно, опубликуйте) свои файлы сборки. – Christopher

ответ

15

Смешивание кода AVX и устаревшего кода SSE несет штраф за производительность. Наиболее разумным решением является выполнение инструкции VZEROALL после сегмента AVX кода, особенно перед выполнением кода SSE.

Согласно диаграмме Intel, штраф при переходе в состояние C или выходе из него (устаревший SSE с верхней половиной сохраненных регистров AVX) составляет порядка 100 тактов. Остальные переходы только 1 цикл:

Ссылки:

+2

Эта проблема может повлиять на радикальные эффекты, которые кажутся совершенно не связанными с этим штрафом. См. [Этот вопрос] (http://stackoverflow.com/q/21960229/2542702), где OP увидела ускорение с более чем 500 потоками в системе, в которой было только восемь гиперпотоков. –

Смежные вопросы