为什么SSE标量sqrt(x)比rsqrt(x)* x慢?

为什么SSE标量sqrt(x)比rsqrt(x)* x慢?

我一直在Intel Core Duo上进行一些核心数学分析,在查看各种平方根方法时,我注意到了一些奇怪的事情:使用SSE标量运算,倒数平方根乘以它会更快获取sqrt,而不是使用本机sqrt操作码!


我正在用类似这样的循环对其进行测试:


inline float TestSqrtFunction( float in );


void TestFunc()

{

  #define ARRAYSIZE 4096

  #define NUMITERS 16386

  float flIn[ ARRAYSIZE ]; // filled with random numbers ( 0 .. 2^22 )

  float flOut [ ARRAYSIZE ]; // filled with 0 to force fetch into L1 cache


  cyclecounter.Start();

  for ( int i = 0 ; i < NUMITERS ; ++i )

    for ( int j = 0 ; j < ARRAYSIZE ; ++j )

    {

       flOut[j] = TestSqrtFunction( flIn[j] );

       // unrolling this loop makes no difference -- I tested it.

    }

  cyclecounter.Stop();

  printf( "%d loops over %d floats took %.3f milliseconds",

          NUMITERS, ARRAYSIZE, cyclecounter.Milliseconds() );

}

我已经为TestSqrtFunction使用了几种不同的主体进行了尝试,并且确实有一些时机让我很头疼。到目前为止,最糟糕的是使用本机sqrt()函数并让“智能”编译器“优化”。在24ns / float的情况下,使用x87 FPU确实很糟糕:


inline float TestSqrtFunction( float in )

{  return sqrt(in); }

我尝试的下一件事是使用内部函数强制编译器使用SSE的标量sqrt操作码:


inline void SSESqrt( float * restrict pOut, float * restrict pIn )

{

   _mm_store_ss( pOut, _mm_sqrt_ss( _mm_load_ss( pIn ) ) );

   // compiles to movss, sqrtss, movss

}

效果更好,为11.9ns / float。我也试过卡马克的古怪牛顿迭代逼近技术,这甚至比硬件跑,在4.3ns /浮动,虽然以1比2的错误10(这是太多了,我的目的)。


当我尝试SSE op求倒数平方根,然后使用乘积获得平方根(x * 1 /√x=√x)时,doozy出现了。即使需要两次相关操作,它还是迄今为止最快的解决方案,速度为1.24ns /浮点,精确度为2 -14:


inline void SSESqrt_Recip_Times_X( float * restrict pOut, float * restrict pIn )

{

   __m128 in = _mm_load_ss( pIn );

   _mm_store_ss( pOut, _mm_mul_ss( in, _mm_rsqrt_ss( in ) ) );

   // compiles to movss, movaps, rsqrtss, mulss, movss

}

我的问题基本上是什么给?为什么SSE的内置于硬件的平方根操作码比从其他两个数学运算中合成出来的速度慢?


我确信这确实是操作本身的成本,因为我已经验证:


所有数据都适合缓存,并且访问是顺序的

内联函数

展开循环没有区别

编译器标志设置为完全优化(并且汇编很好,我检查过)

(编辑:stephentyrone正确地指出,长数字串上的运算应使用矢量化SIMD打包操作,例如rsqrtps-但此处的数组数据结构仅用于测试目的:我真正要衡量的是标量性能,以用于代码中无法向量化。)


绝地无双
浏览 1504回答 4
4回答

慕标5832272

sqrtss给出正确的舍入结果。 rsqrtss给出倒数的近似值,精确到大约11位。sqrtss当需要准确性时,可以产生更准确的结果。 rsqrtss存在一个近似值但需要速度的情况。如果您阅读了英特尔的文档,您还将发现一条指令序列(平方根的倒数,后跟一个牛顿-拉夫森步长),几乎可以提供全精度(如果我没记错的话,精度约为23位),并且仍然有些比快sqrtss。编辑:如果速度至关重要,并且您实际上是在循环中调用许多值,则应该使用这些指令的向量化版本,rsqrtps或sqrtps,这两个指令每条处理四个浮点数。

BIG阳

划分也是如此。MULSS(a,RCPSS(b))比DIVSS(a,b)快得多。实际上,即使使用牛顿-拉夫森(Newton-Raphson)迭代来提高精度时,它仍然更快。英特尔和AMD均在其优化手册中推荐了该技术。在不需要IEEE-754兼容的应用程序中,使用div / sqrt的唯一原因是代码可读性。
打开App,查看更多内容
随时随地看视频慕课网APP