在 AVX 中进行线性平均的最短方法

Posted

技术标签:

【中文标题】在 AVX 中进行线性平均的最短方法【英文标题】:Shortest way to do Linear Averaging in AVX 【发布时间】:2021-02-10 14:14:14 【问题描述】:

我有一个向量 Inp = A0, 0, A2, 0, A4, 0, A6, 0, ...;

我的兴趣是 Out = A0, mean(A0, A2), A2, mean(A2, A4), A4, mean(A4, A6), A6, ...;

Inp 和 Out 是浮点变量。 输入长度总是偶数。因此,Out(end) = inp(end -1);

编辑:

我的实现,

float *vecAPtr;
__m256 vecA;
__m256 vecB;
__m256 res1;
__m256 res2;
__m256 *AVXInp = (__m256*)Inp;
for(i = 0;i<inpLength;i = i+8)

  if( (inpLength - i) <= 8)
      //Normal C code
      Out[0] = Inp[0];
      Out[1] = (Inp[0]+Inp[2])/2;
      soon... (A simple for loop will take care of this part, not shown here)
   
else

vecA = _mm256_permutevar8x32_ps(*AVXInp, _mm256_set_epi32(1, 1, 6, 1, 4, 1, 2, 1) );
//vecA = [0 A2 0 A4 0 A6 0 0];

vecB = _mm256_permute_ps( *AVXInp, 0b10010001);
//vecB = [0 A0 0 A2 0 A4 0 A6];

vecAPtr = (float *)&vecA;
vecAPtr[7] = *( (float *)(AVXInp+1));
//vecA = [0 A2 0 A4 0 A6 0 A8];

res1 = _mm256_add_ps(vecA, vecB);
res2 = _mm256_mul_ps(res1, _mm256_set1_ps(0.5));
*AVXInp = _mm256_add_ps( *AVXInp, res2);

还有比这个更好的版本吗?

【问题讨论】:

A0、A2、...的类型是什么? @PaulR 它们是浮点变量.. 输入中的那些0元素是对空间和带宽的浪费;我想你有他们的理由?仅作记录,vecAPtr[7] = *( (float *)(AVXInp+1)); 可能很糟糕。最好只做另一个 32 字节的未对齐加载。虽然如果你幸运的话,编译器会发现它可以用vbroadcastss + vblendps 来完成。如果你不走运,它会编译成更糟糕的洗牌。 最好进行加载以获取所需的所有元素并跳过零,因为引入零可能会便宜得多! 【参考方案1】:

我将首先使用_mm256_moveldup_ps 复制偶数索引元素,然后加载从Inp+1 开始的向量并将其添加到其中。然后将奇数元素按0.5 缩放(偶数元素按1.0):

void foo(float* out, float const* inp, size_t size)
    __m256 const scale = _mm256_setr_ps(1.f,.5f,1.f,.5f,1.f,.5f,1.f,.5f);

    for(size_t i=0; i<size-9; i+=8)
        __m256 even = _mm256_moveldup_ps(_mm256_loadu_ps(inp+i));
                                                // [a0, a0,      a2, a2,    ...]
        __m256 odd = _mm256_loadu_ps(inp+i+1);  // [ 0,    a2,    0,    a4, ...]
        __m256 sum = _mm256_add_ps(even,odd);   // [a0, a0+a2,   a2, a2+a4, ...]
        __m256 res = _mm256_mul_ps(scale, sum); // [a0,(a0+a2)/2,a2,(a2+a4)/2, ...]
        _mm256_storeu_ps(out+i, res);
    

这不处理最后的元素。此外,它假定您可以返回 a0+0,其中预期 a0(只有在 a0=-0 时才会有所不同)并且 a0+a2 不会溢出。

假设编译器融合了加载和_mm256_moveldup_ps,生成的vmovsldup甚至不需要shuffle操作,所以每次迭代只需要两次加载、两次算术和一次存储(因此应该有1个周期的吞吐量,忽略循环开销)。

【讨论】:

以上是关于在 AVX 中进行线性平均的最短方法的主要内容,如果未能解决你的问题,请参考以下文章

fio

线性搜索平均需要检查多少个元素?

android:如何平均划分线性布局元素

将线性拟合估计为移动平均线

线性搜索的平均案例复杂度

非线性微分方程的平均法