SSE работает медленно после использования AVX
У меня странная проблема с некоторыми кодами SSE2 и AVX, над которыми я работал. Я строю свое приложение, используя GCC, который обнаруживает функцию процессора во время выполнения. Объектные файлы создаются с отдельными флагами для каждой функции ЦП, например:
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 ответ
Смешение кода AVX и устаревшего кода SSE влечет за собой снижение производительности. Наиболее разумным решением является выполнение инструкции VZEROALL после сегмента кода AVX, особенно перед выполнением кода SSE.
Согласно диаграмме Intel, штраф за переход в или из состояния C (устаревший SSE с сохраненной верхней половиной регистров AVX) составляет порядка 100 тактов. Другие переходы только 1 цикл:
Рекомендации: