为什么 FMA x86 MS C 编译器的 AVX2 速度会变慢?

Why this AVX2 slowdown with FMA x86 MS C Compiler?

提问人:Martin Brown 提问时间:10/29/2023 更新时间:10/29/2023 访问量:69

问:

这是求解开普勒方程的三个经典启动器,S3 提供了一个有趣的示例,即具有奇怪时序行为的看似短的代码片段。这个问题涉及在 Microsoft C 编译器 x86 模式下编译它们,其中观察到异常行为 - 即 AVX2 代码在最大优化下对概念上更简单的代码 S3 执行的时间比 S9 长得多。英特尔没有这样的问题(无法用 GCC 分辨,因为 Mingw 中的 x87 sin 问题阻止我测试它)。 所有启动器都是偏心率 0 <= e <= 1 和平均异常 -pi <= M <= pi 的函数 这些经典作品由Gooding&Odel(1986)创作

double S1(double e, double M)
{ 
  if(M>0) return M + e; else return M - e;
  // simplest starter guaranteed to converge (eventually)
}
// convergence is not guaranteed with either S3 or S9 when e -> 1

double Gooding_S3(double e, double M)
{
  return M + e*sin(M)*(1+e*cos(M));  
// slow and fairly useless S9 is much more accurate!
}

double Gooding_S9(double e, double M)
{
 // originally written in their paper as sin(M)/sqrt(1-2*e*cos(M)+e*e)
 // it is one form of Halley's method but derived as the root of a
 // simple quadratic approximation for E-M. Rather good and fast e<0.7!
  double y;
  y = 1-e;
  if (M == 0.0) return M; // defend against divide by zero
  s = sin(M/2);
  return M+e*sin(M)/sqrt(y*y+4*e*s*s);  // more accurate form (very wrong when e -> 1)
}

对操作数量及其性质的快速检查表明,它们按执行时间增加的顺序排列,但情况并非总是如此!S9 有时更快。他们的操作计数受我的计数误差的影响是

  1. S1 +
  2. S3 ++ *** 正余弦
  3. S9 -++ ***** / 罪孽 sqrt

今天使用标准_cdecl调用约定、x86 代码生成和 MSC 2022 编译器,我得到的 S3 和 S9 的时序如下(S1 是函数调用开销的 ~8 个周期)。AVX2 的 74 不是错别字!如果代码生成是 x64,问题就会消失。

起动机 x87的 上证2 AVX的 AVX2系列 *_64
S3系列 199 23 24 74 23
S9系列 206 50 48 47 30

我已经在 x86 codegen 上隔离了 AVX 和 AVX2 之间与 S3 不同的小代码片段,但不明白为什么一个应该比另一个差得多。我也有点惊讶 S9 的速度如此之快(而且时间稳定)。x64 代码是一致且更快的,所以我仍然对它感到困惑。我通过为另一个问题生成 x87 trig 代码再次遇到它。

这是来自 sincos(无 FMA)的 S3 的更快的 AVX 代码

call        ___libm_sse2_sincos_ (0BF7D50h)    
vshufpd     xmm1,xmm0,xmm0,1  
vmulsd      xmm1,xmm1,mmword ptr [e]  
vmulsd      xmm0,xmm0,mmword ptr [e]  
vaddsd      xmm1,xmm1,mmword ptr [__real@3ff0000000000000 (0BFBFB8h)]  
vmulsd      xmm0,xmm1,xmm0  
vaddsd      xmm0,xmm0,mmword ptr [M]  
vmovsd      qword ptr [esp],xmm0  
fld         qword ptr [esp]  

这是来自 AVX2 的缓慢 S3 代码,显然使用 FMA 很糟糕。我不明白为什么。

 call        ___libm_sse2_sincos_ (0672600h)  
 vmovsd      xmm2,qword ptr [e]  
 vpermilpd   xmm1,xmm0,1  
 vfmadd213sd xmm2,xmm1,mmword ptr [__real@3ff0000000000000 (0676F50h)]  
 vmulsd      xmm0,xmm0,mmword ptr [e]  
 vfmadd213sd xmm0,xmm2,mmword ptr [M]  
 vmovsd      qword ptr [esp],xmm0  
 fld         qword ptr [esp] 2022 

为什么 AVX2 代码比 AVX/SSE2 慢得多,为什么它只发生在 x86 代码上?

我对避免这种奇怪的减速的建议很感兴趣,在这种减速中,来自改进指令集的更短、更智能的代码可能会导致性能更差。这似乎对 x64 代码生成的问题要小得多,这可能暗示它可能与在 ST(0) 中返回 FP 结果的旧 x86 链接约定有关。

它似乎与超越库函数调用有关,但这可能只是我的抽样偏差。

感谢您的任何启发。

C x86 AVX2 微基准测试 FMA

评论


答: 暂无答案