Rob*_*ger 44 c++ optimization math.h micro-optimization
我浏览了最近发布的Doom 3 BFG源代码,当时我遇到了一些似乎没有任何意义的东西.Doom 3在idMath类中包含数学函数.一些函数只是向相应的函数提供math.h,但有些是重新实现(例如idMath :: exp16()),我认为它具有比它们的math.h对应物更高的性能(可能以牺牲精度为代价).
然而,令我感到困惑的是他们实现这一float idMath::Sqrt(float x)功能的方式:
ID_INLINE float idMath::InvSqrt( float x ) {
     return ( x > FLT_SMALLEST_NON_DENORMAL ) ? sqrtf( 1.0f / x ) : INFINITY;
}
ID_INLINE float idMath::Sqrt( float x ) {
     return ( x >= 0.0f ) ? x * InvSqrt( x ) : 0.0f;
}
这似乎执行两个不必要的浮点运算:首先是除法然后是乘法.
值得注意的是,原始的Doom 3源代码也以这种方式实现了平方根函数,但是反平方根使用了快速平方根算法.
ID_INLINE float idMath::InvSqrt( float x ) {
    dword a = ((union _flint*)(&x))->i;
    union _flint seed;
    assert( initialized );
    double y = x * 0.5f;
    seed.i = (( ( (3*EXP_BIAS-1) - ( (a >> EXP_POS) & 0xFF) ) >> 1)<<EXP_POS) | iSqrt[(a >> (EXP_POS-LOOKUP_BITS)) & LOOKUP_MASK];
    double r = seed.f;
    r = r * ( 1.5f - r * r * y );
    r = r * ( 1.5f - r * r * y );
    return (float) r;
}
ID_INLINE float idMath::Sqrt( float x ) {
    return x * InvSqrt( x );
}
你看,在计算任何优势Sqrt(x)的x * InvSqrt(x),如果InvSqrt(x)内部只是调用math.h的fsqrt(1.f/x)?我可能在这里遗漏了一些关于非规范化浮点数的重要事项,还是这只是id软件的一部分?
我可以看到这样做的两个原因:首先,"快速invSqrt"方法(实际上是Newton Raphson)现在是许多硬件中使用的方法,因此这种方法留下了利用这种硬件的可能性(和一次可能做四次或更多这样的操作).本文稍微讨论一下:
第二个原因是兼容性.如果更改用于计算平方根的代码路径,则可能会得到不同的结果(尤其是对于零,NaN等),并且与依赖于旧系统的代码失去兼容性.