我一直在剖析我们的一些核心数学上的英特尔酷睿双核,在注视我注意到一些奇怪的各种方法来平方根:使用SSE标量运算,它是更快采取倒数平方根和乘以拿到开方,比它是使用原生的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 /浮充使用的x87 FPU,这是可怜坏:
inline float TestSqrtFunction( float in )
{ return sqrt(in); }
接下来的事情我试图用一种内在强制编译器使用上证所标开方操作码:
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 /浮动。 我也试过卡马克的古怪牛顿迭代逼近技术 ,其运行时间比硬件,为4.3ns /浮甚至更好,虽然以1比2 10的错误(这是太多了,我的目的)。
所述讲给是当我尝试SSE运算为倒数平方根,然后使用乘法获得的平方根(X * 1 /√x=√x)。 尽管这需要两个相关的操作,这是迄今为止最快的解决方案,为1.24ns / float和精确到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
}
我的问题是基本上怎么办? 为什么上证所内置到硬件平方根操作码慢于合成出来另外两个数学运算的?
我敢肯定,这是真正的运算本身的成本,因为我已经验证:
- 在缓存中的所有数据拟合,并访问是顺序
- 将内联函数
- 展开循环没什么区别
- 编译器标志被设置为全面优化(与装配好,我选中)
( 编辑 :stephentyrone正确地指出,在数字应该使用的长字符串操作向量化SIMD包装OPS,像rsqrtps
-但这里的阵列仅用于测试目的:什么我真的试图测量是在使用标性能代码不能被量化。)