为什么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-但此处的数组数据结构仅用于测试目的:我真正要衡量的是标量性能,以用于代码中无法向量化。)
慕标5832272
BIG阳