当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>Visual C++教程

Microsoft Visual C++ 浮点优化

    Eric Fleegal
        Microsoft Corporation
    适用于:Microsoft Visual C++
        C++ 中的浮点代码优化
        C++ 优化编译器不仅能够将源代码转换为机器码,而且能够对机器指令进行适当的排列以便改善性 能和/或减小大小。遗憾的是,许多常用的优化在应用于浮点计算时未必安全。在下面的求和算法 [1] 中,可以看到这方面的一个恰当的示例:
    float KahanSum( const float A[], int n )
    {
       float sum=0, C=0, Y, T;
       for (int i=0; i<n; i++)
       {
          Y = A[i] - C;
          T = sum + Y;
          C = T - sum - Y;
          sum = T;
       }
       return sum;
    }

        该函数将数组向量 A 中的 n 个浮点值相加。在循环体中,算法计算 一个“修正”值,然后将其应用于求和的下一步。与简单的求和相比,该方法大大减小了累积性舍入 误差,同时保持了 O(n) 时间复杂性。
        一个不完善的 C++ 编译器可能假设浮点算法遵循与实数算法相同的代数规则。这样的编译器可能 继而错误地断定
    C = T - sum - Y ==> (sum+Y)-sum-Y ==> 0;
        也就是说,C 得到的值总是常量零。如果随后将该常量值传播到后续表达式中,循环体将化简为简 单的求和。更准确地说,就是
    Y = A[i] - C ==> Y = A[i]
    T = sum + Y ==> T = sum + A[i]
    sum = T ==> sum = sum + A[i]

    因此,对于不完善的编译器而言,KahanSum 函数的逻辑转换将是:
    float KahanSum( const float A[], int n )
    {
       float sum=0; // C, Y & T are now unused
       for (int i=0; i<n; i++)
          sum = sum + A[i];
       return sum;
    }

        尽管转换后的算法更快,但它根本没有准确表达程序员的意图。精心设计的误差修正已经 被完全消除,只剩下一个具有所有其关联误差的简单的直接求和算法。
        当然,完善的 C++ 编译器知道实数算法的代数规则通常并不适用于浮点算法。然而,即使是完善 的 C++ 编译器,也可能错误地解释程序员的意图。

        考虑一种常见的优化措施,它试图在寄存器中存放尽可能多的值(称为“登记”值)。在 KahanSum 示例中,这一优化可能试图登记变量 C、Y 和 T,因为这些变量仅在循环体内使用。如果寄存器精度为 52 位(双精度)而不是 23 位(单精度),这一优化可以有效地将 C、Y 和 T 的类 型提升为 double。如果没有以同样的方式登记 sum 变量,则它仍将编 码为单精度。这会将 KahanSum 的语义转换为下面的语义
    float KahanSum( const float A[], int n )
    {
       float sum=0;
       double C=0, Y, T; // now held in-register
       for (int i=0; i<n; i++)
       {
          Y = A[i] - C;
          T = sum + Y;
          C = T - sum - Y;
          sum = (float) T;
       }
       return sum;
    }

        尽管现在 Y、T 和 C 以更高的精度进行计算,但新的编码可能产生精确性较低的结果,具体取决 于 A[] 中的值。因而,即使看起来无害的优化也可能具有消极的后果。
        这些种类的优化问题并不局限于“棘手”的浮点代码。即使是简单的浮点算法,在经过错误的优化 后也可能失败。考虑一个简单的直接求和算法:
    float Sum( const float A[], int n )
    {
       float sum=0;
       for (int i=0; i<n; i++)
          sum = sum + A[i];
       return sum;
    }

        因为一些浮点单元能够同时执行多个运算,所以编译器可能选择采用标量简化 优化。这一 优化有效地将简单的 Sum 函数从上述形式转换为以下形式:
    float Sum( const float A[], int n )
    {
       int n4 = n-n%4; // or n4=n4&(~3)
       int i;
       float sum=0, sum1=0, sum2=0, sum3=0;
       for (i=0; i<n4; i+=4)
       {
          sum = sum + A[i];
          sum1 = sum1 + A[i+1];
          sum2 = sum2 + A[i+2];

       sum3 = sum3 + A[i+3];
       }
       sum = sum + sum1 + sum2 + sum3;
       for (; i<n; i++)
          sum = sum + A[i];
       return sum;
    }

共2页 首页 上一页 1 2 下一页 尾页 跳转到
相关内容
赞助商链接