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;
    }
  • 相关阅读:
    BZOJ 3506 机械排序臂 splay
    BZOJ 2843 LCT
    BZOJ 3669 魔法森林
    BZOJ 2049 LCT
    BZOJ 3223 文艺平衡树 splay
    BZOJ 1433 假期的宿舍 二分图匹配
    BZOJ 1051 受欢迎的牛 强连通块
    BZOJ 1503 郁闷的出纳员 treap
    BZOJ 1096 ZJOI2007 仓库设计 斜率优化dp
    BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
  • 原文地址:https://www.cnblogs.com/wuOverflow/p/4123525.html
Copyright © 2011-2022 走看看