使用AVX后,SSE运行缓慢

时间:2015-10-15 13:16:08

标签: c++ gcc x86 avx sse2

我有一些奇怪的问题,我正在研究一些SSE2和AVX代码。我正在使用GCC构建我的应用程序,运行时cpu特征检测。对象文件使用每个CPU功能的单独标志构建,例如:

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例程运行之前,所有测试都比FPU数学快了大约80-130%,如此处所示,在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 个答案:

答案 0 :(得分:15)

混合AVX代码和传统SSE代码会导致性能下降。最合理的解决方案是在AVX代码段之后执行VZEROALL指令,特别是在执行SSE代码之前。

根据英特尔的图表,转换到或退出状态C(保存AVX寄存器的上半部分的传统SSE)时的损失大约为100个时钟周期。其他转换只有1个周期:

参考文献: