zoukankan      html  css  js  c++  java
  • 优化编译器的局限性

    考察以下代码:

    void Twiddle1(int *xp, int *yp)
    {
        *xp += *yp;
        *xp += *yp;
    }
    
    void Twiddle2(int *xp, *yp)
    {
        *xp += 2 * *yp;
    }

    这两个过程等价吗? 事实上 Twiddle2 的效率更高, 因为它只要求 3 次存储器引用, 而 Twiddle1 需要 6 次, 但它们不能相互代替, 因为当 xp == yp 时,

    *xp += *xp;
    *xp += *xp;

    之后, xp 的值是原来的 4 倍, 但是类似 Twiddle2 的操作:

    *xp += 2 * *xp;

    的结果却是原来的 3 倍. 所以编译器不会产生 Twiddle2 的代码作为 Twiddle1 的优化代码.
    考察以下代码:

    x = 1000; y = 3000;
    *q = y;
    *p = x;
    t1 = *q    // ?

    t1 的值依赖于指针 p 和 q 的位置关系, 如果 p == q, 那么 t1 == 1000; 如果不相等, t1 == 3000. 这就是一种妨碍优化的因素, 它严重限制了编译器产生优化代码的机会和优化策略.
    两个指针可能指向同一个储存器的位置的情况称为储存器别名使用(memory aliasing). 在直至项安全优化的代码中, 编译器需要假设储存器别名使用的情况.
    第二个妨碍优化的因素是函数调用, 考察以下代码:

    int F();
    
    int Func1()
    {
        return F() + F() + F() + F();
    }
    
    int Func2()
    {
        return 4 *  F();
    }

    看上去似乎是相同的结果, 但是 Func2() 只调用 F() 1 次, 而 Func1() 调用 4 次, 考虑以下代码:

    int counter = 0;
    int F()
    {
        return counter++;
    }

    对于 Func1() 我们可以用内联函数进行优化即将函数调用替换为函数体:

    //优化后的版本
    int Funct1Inline()
    {
        int t = counter++;    // +0
        t = counter++;        // +1
        t = counter++;        // +2
        t = counter++;        // +3
        return t;
    }

    这样的转换不仅可以减少了函数调用的开销, 也允许编译器对代码进行进一步优化:

    //编译器进一步优化
    int Func1Opt()
    {
        int t = 4 * counter + 6;
        counter = t + 4;
        return t;
    }
  • 相关阅读:
    bzoj3272 Zgg吃东西
    bzoj3894 文理分科
    poj1149 PIGS
    poj1637 Sightseeing tour
    [Wc2007]剪刀石头布
    poj2396 Budget
    [NOI2017]游戏
    CF666E Forensic Examination
    bzoj4889 [Tjoi2017]不勤劳的图书管理员
    CF587F Duff is Mad
  • 原文地址:https://www.cnblogs.com/wuOverflow/p/4123525.html
Copyright © 2011-2022 走看看