c++ - 使用混合(浮点、 double )输入 vector 执行 AVX 内积运算的最快方法

标签 c++ vectorization simd avx sse2

我需要为混合单精度/ double 浮点 vector 构建一个单精度浮点内积例程,利用 SIMD 的 AVX 指令集寄存器为 256 位。

问题:一个输入 vector 是float (x),而另一个是double (yD)。

因此,在计算真正的内积运算之前,我需要将我的输入 yD vector 数据从 double 转换为 float 。

使用 SSE2 指令集,我能够实现一个非常快速的代码来满足我的需要,并且速度性能非常接近 vector x 和 y 都是 float 的情况:

  void vector_operation(const size_t i) 
  {
    __m128 X = _mm_load_ps(x + i);
    __m128 Y = _mm_movelh_ps(_mm_cvtpd_ps(_mm_load_pd(yD + i + 0)), _mm_cvtpd_ps(_mm_load_pd(yD + i + 2)));
    //inner-products accumulation
    res = _mm_add_ps(res, _mm_mul_ps(X, Y));
  }   

现在,为了进一步提速,我用AVX指令集实现了一个对应的版本:

  inline void vector_operation(const size_t i) 
  {
    __m256 X = _mm256_load_ps(x + i);
    __m128 yD1 = _mm_cvtpd_ps(_mm_load_pd(yD + i + 0));
    __m128 yD2 = _mm_cvtpd_ps(_mm_load_pd(yD + i + 2));
    __m128 yD3 = _mm_cvtpd_ps(_mm_load_pd(yD + i + 4));
    __m128 yD4 = _mm_cvtpd_ps(_mm_load_pd(yD + i + 6));

    __m128 Ylow = _mm_movelh_ps(yD1, yD2);
    __m128 Yhigh = _mm_movelh_ps(yD3, yD4);

    //Pack __m128 data inside __m256 
    __m256 Y = _mm256_permute2f128_ps(_mm256_castps128_ps256(Ylow), _mm256_castps128_ps256(Yhigh), 0x20);

    //inner-products accumulation 
    res = _mm256_add_ps(res, _mm256_mul_ps(X, Y));
  }

我还测试了其他 AVX 实现,例如使用强制转换和插入操作而不是执行数据。与 x 和 y vector 均为 float 的情况相比,性能相对较差。

AVX 代码的问题在于,无论我如何实现它,它的性能远不如仅使用 float x 和 y vector (即不需要双 float 转换)所实现的性能。

yD vector 从 double 到 float 的转换看起来相当快,但在将数据插入 _m256 Y 寄存器的行中浪费了很多时间。

您知道这是否是 AVX 的一个众所周知的问题吗?

您有可以保持良好性能的解决方案吗?

提前致谢!

最佳答案

我重写了您的函数并更好地利用了 AVX 所提供的功能。最后我还使用了融合乘加;如果您不能使用 FMA,只需将该行替换为加法和乘法即可。我现在才看到我写了一个使用未对齐负载的实现,而你的使用对齐负载,但我不会为此失眠。 :)

__m256 foo(float*x, double* yD, const size_t i, __m256 res_prev)
{
  __m256 X = _mm256_loadu_ps(x + i);

  __m128 yD21 = _mm256_cvtpd_ps(_mm256_loadu_pd(yD + i + 0));
  __m128 yD43 = _mm256_cvtpd_ps(_mm256_loadu_pd(yD + i + 4));

  __m256 Y = _mm256_set_m128(yD43, yD21);

  return _mm256_fmadd_ps(X, Y, res_prev);
}

我做了一个快速基准测试并比较了你和我的实现的运行时间。我尝试了两种不同的基准测试方法并重复了几次,每次我的代码都快了 15% 左右。我使用 MSVC 14.1 编译器并使用/O2 和/arch:AVX2 标志编译程序。

编辑:这是函数的反汇编:

vcvtpd2ps   xmm3,ymmword ptr [rdx+r8*8+20h]  
vcvtpd2ps   xmm2,ymmword ptr [rdx+r8*8]  
vmovups     ymm0,ymmword ptr [rcx+r8*4]  

vinsertf128 ymm3,ymm2,xmm3,1  

vfmadd213ps ymm0,ymm3,ymmword ptr [r9] 

编辑 2:这是相同算法的 AVX 实现的反汇编:

vcvtpd2ps   xmm0,xmmword ptr [rdx+r8*8+30h]  
vcvtpd2ps   xmm1,xmmword ptr [rdx+r8*8+20h]  

vmovlhps    xmm3,xmm1,xmm0  
vcvtpd2ps   xmm0,xmmword ptr [rdx+r8*8+10h]  
vcvtpd2ps   xmm1,xmmword ptr [rdx+r8*8]  
vmovlhps    xmm2,xmm1,xmm0  

vperm2f128  ymm3,ymm2,ymm3,20h  

vmulps      ymm0,ymm3,ymmword ptr [rcx+r8*4]  
vaddps      ymm0,ymm0,ymmword ptr [r9]

关于c++ - 使用混合(浮点、 double )输入 vector 执行 AVX 内积运算的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49414268/

相关文章:

r - 为矩阵向量化 min()

python - Python 中的数学库和数组

c++ - 我应该如何提高此 C++ 代码的性能?

c# - 如何使用矢量 SSE 操作将图像像素数据的字节数组转换为灰度

c++ - set 数据结构中的 end() 迭代器应该返回什么?

c++ - 尝试运行 projectPoints 时断言失败

c++ - 为什么会出现这个段错误?

c++ - 如何将 dd-mmm-yyyy 和 now() 转换为天?

python - 使用 numpy 进行乘法累加

parallel-processing - CPU SIMD和GPU SIMD?