对于每个调用,gcc是否会对同一个变量的相同变量优化掉重复的函数调用?

时间:2014-07-02 03:31:48

标签: c++ gcc optimization compiler-construction

对于一个应用程序,我处于多种形式存在相同信息的情况:Base64字符串,十六进制字符串和char[]

就目前而言,为了提高生产力,而不是刻意宣布&每个函数初始化一次变量,我只在上述表单之间的明显转换点上应用它。原因是因为有些点不需要将变量转换为另一种形式来进行条件比较等操作。

从我所看到的情况来看,似乎编译器的效率非常高,并且日益变得如此;然而,当我尝试阅读更深入的分析和描述时,我经常超越我的经验限制,我的脑堆溢出。

如果一个函数被重复调用一个变量以将其改为另一种形式,比如从Base64字符串到每次产生相同结果的十六进制字符串,编译器是否会优化这些调用以便为该变量声明整个范围是不必要的?

就我而言,我正在使用-Ofast,直到有更好的东西。

3 个答案:

答案 0 :(得分:2)

哪些编译器可以优化,实际上取决于代码的编写方式;然而,依靠编译器过于聪明一般是不明智的。编译器非常适合优化寄存器分配和各种低级别的东西,但如果你的程序中有一些不确定的东西,你可以更高效地编写代码,不要认为编译器整体上理解了程序

对于您提到的这个特定示例,如果将数据包装在实现各种格式的转换运算符的类中并缓存转换结果,那么这将比依赖编译器重做更好的方法相同的计算。但是,如果将这些转换运算符标记为“const”,则有可能(假设没有执行交错的非const运算)编译器将重用先前调用“const”方法的结果。但是,我建议除了缓存结果外,还要依赖此优化。

此外,当谈到这些优化时,唯一可以确定的方法是使用特定的编译器实际编译代码并检查程序集输出以确定它是否确实应用了该优化。

答案 1 :(得分:2)

我希望gcc不要执行那种优化。如果确实如此,则必须满足几个要求,例如同时编译被调用的函数 - 以提供在整个调用中优化寄存器的可能性。

这样的优化令人印象深刻,但并非纯粹有用。程序员可以轻松地编写对函数的调用并保存返回值。

CDC Cyber​​(c.1975)FORTRAN编译器有一些有趣的行为。它会优化对IRAND()的调用。这让很多学生写游戏感到惊讶和困惑,就像滚动两个骰子的片段一样:

integer roll
roll = mod (irand(0), 6)  +  mod (irand(0), 6)  +  2

这只产生偶数,因为它认为它就像是写的

roll = 2 * mod (irand(0), 6)  +  2

据报道,1978年左右的一个错误是通过不优化涉及irand()rand()的表达来解决的。通过使其更难以优化来解决这个问题很容易:

integer roll, die1, die2
die1 = mod (irand(0), 6)  +  1
die2 = mod (irand(0), 6)  +  1
roll = die1 + die2

只要优化不会太高,它就会按预期工作。第一个例子是始终优化:它无法关闭。

答案 2 :(得分:1)

这里有一些代码来说明这个概念:

class TriString
{
  public:
    enum Format { Binary, Hex, Base64 };

    TriString(const std::string& s) : s_(s) { }

    // mutators - must modify b_ and h_ accordingly or clear them

    TriString& operator=(const std::string& rhs)
        { s_ = rhs; b_.clear(); h_.clear(); }

    TriString& erase(size_type index = 0, size_type count = npos)
    {
        s_.erase(index, npos);
        h_.clear(); // will need regeneration...
        b_.erase(index * 2, count == npos ? npos : count * 2);
    }

    char& operator[](size_type n)
    {
        h_.clear();
        b_.clear();
        return s_[n];
    }

    // ...add more as needed...

    // accessors

    const std::string& get(Format) const
    {
        if (Format == Binary || s_.empty())
            return s_;
        if (Format == Hex)
        {
            if (h_.empty()) h_ = to_hex(s_);
            return h_;
        }
        // Format == Base64
        if (b_.empty()) b_ = to_base64(s_);
        return b_;
    }

    const char& operator[](size_type n) const { return s_[n]; }

    // ...add more as needed...

  private:
    std::string s_;          // normal string

    // "cached" conversions - invariant: valid if not empty(), or s_.empty() too
    // (mutable so get(Format) const can modify despite being const)
    mutable std::string b_;  // base64 encoded
    mutable std::string h_;  // hex encoded
};

使用通常的std::string界面执行此操作并不安全,因为以下客户端代码无法正常工作:

TriState s("hello!");
char& c = s[2];
const std::string& h = s.get(TriState::Hex);  // triggers caching of hex conversion
c = 'x';                                      // oops - modifies s_ without clearing/updating h_
const std::string& h2 = s.get(TriState::Hex); // oops - gets old cached h_ despite changed s_

您必须做出一些选择来限制接口以避免授予更改字符串的持续能力(与非const operator[],迭代器等一样),返回代理对象(而不是例如字符引用)可以在写完时清除缓存的转换,或记录对客户端使用的一些限制,并希望获得最佳....