AVX2顶点分量的重心插值

时间:2017-01-22 05:01:20

标签: c++ optimization interpolation simd avx2

我刚刚开始使用simd内在函数的路径。我的分析器已经表明,在顶点插值上花费了大量时间。我的目标是AVX2,我试图找到以下的优化 - 假设我有3个需要插值的vector2s,我想我应该能够将它们加载到单个__m256中并进行乘法并有效地添加。这是我试图转换的代码 - 是否值得将它作为256位操作?向量是不对齐的。

Vector2 Interpolate( Vector3 uvw, Vector2 v0, Vector2 v1, Vector2 v2 )
{
   Vector2 out;
   out = v0 * uvw.x;
   out += v1 * uvw.y;
   out += v2 * uvw.z;

   return out;
}

struct Vector2 { float x; float y; } ;
struct Vector3 { float x; float y; float z; } ;

我的问题是 - 如何将三个未对齐的vector2加载到单个256位寄存器中,以便我可以进行乘法并添加?

我正在使用VS2013。

1 个答案:

答案 0 :(得分:1)

我很无聊所以我写了它,没有经过测试(但是编译过来,Clang和GCC都从中做出了合理的代码)

void interpolateAll(int n, float* scales, float* vin, float* vout)
{
  // preconditions:
  // (n & 7 == 0) (not really, but vout must be padded)
  // scales & 31 == 0
  // vin & 31 == 0
  // vout & 31 == 0

  // vin format:
  // float v0x[8]
  // float v0y[8]
  // float v1x[8]
  // float v1y[8]
  // float v2x[8]
  // float v2y[8]
  // scales format:
  // float scale0[8]
  // float scale1[8]
  // float scale2[8]
  // vout format:
  // float vx[8]
  // float vy[8]

  for (int i = 0; i < n; i += 8) {
    __m256 scale_0 = _mm256_load_ps(scales + i * 3);
    __m256 scale_1 = _mm256_load_ps(scales + i * 3 + 8);
    __m256 scale_2 = _mm256_load_ps(scales + i * 3 + 16);
    __m256 v0x = _mm256_load_ps(vin + i * 6);
    __m256 v0y = _mm256_load_ps(vin + i * 6 + 8);
    __m256 v1x = _mm256_load_ps(vin + i * 6 + 16);
    __m256 v1y = _mm256_load_ps(vin + i * 6 + 24);
    __m256 v2x = _mm256_load_ps(vin + i * 6 + 32);
    __m256 v2y = _mm256_load_ps(vin + i * 6 + 40);
    __m256 x = _mm256_mul_ps(scale_0, v0x);
    __m256 y = _mm256_mul_ps(scale_0, v0y);
    x = _mm256_fmadd_ps(scale_1, v1x, x);
    y = _mm256_fmadd_ps(scale_1, v1y, y);
    x = _mm256_fmadd_ps(scale_2, v2x, x);
    y = _mm256_fmadd_ps(scale_2, v2y, y);
    _mm256_store_ps(vout + i * 2, x);
    _mm256_store_ps(vout + i * 2 + 8, y);
  }
}

如果我理解正确的话,使用Z boson的格式。无论如何,从SIMD的角度来看,它是一种不错的格式。从C ++的角度来看有点不方便。

FMA会不必要地序列化乘法,但这并不重要,因为它不是循环携带依赖的一部分。

预测的吞吐量(假设数组足够小)是每9个循环2次迭代,由负载引起瓶颈。在实践中可能稍微差一点,有些人谈论偶尔窃取p2或p3的简单商店,那种事情,我不太确定。无论如何,这个时间足够18“FMA”但是只有12(8和4 mulps),所以如果有的话,在这里移动一些额外的计算可能是有用的。