c – 使用AVX后,SSE运行缓慢

前端之家收集整理的这篇文章主要介绍了c – 使用AVX后,SSE运行缓慢前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一些奇怪的问题,一些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,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,++out)
    *out = (int16_t)lrint(*in * ratio);
}

我也通过valgrind运行它,检测没​​有错误.

解决方法

混合AVX代码和旧版SSE代码会导致性能损失.最合理的解决方案是在AVX段代码后执行VZEROALL指令,特别是在执行SSE代码之前.

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

参考文献:

> Intel: Avoiding AVX-SSE Transition Penalties
> Intel® AVX State Transitions: Migrating SSE Code to AVX

猜你在找的C&C++相关文章