优化是否应用于单行功能?

时间:2019-02-27 16:40:35

标签: c++ compiler-optimization rvo

我不想在代码中重复我自己,但是我也不想因为简单的功能而失去性能。假设该类具有operator+和函数Add具有相同的功能(将前者视为在表达式中使用类的便捷方式,而后者则作为“灵活”的方式)

struct Obj {
   Obj operator+(float);
   Obj Add(float);
   /* some other state and behaviour */
};

Obj AddDetails(Obj const& a, float b) {
   return Obj(a.float_val + b, a.some_other_stuff);
}

Obj Obj::operator+(float b) {
   return AddDetails(*this, b);
}

Obj Obj::Add(float b) {
   return AddDetails(*this, b);
}

为了使更改更容易,两个功能都通过辅助功能调用来实现。因此,打给话务员的任何电话都会打2个电话,这确实不是很愉快。

但是编译器是否足够聪明,可以消除此类重复调用?

我用简单的类(包含内置的类型和指针)进行了测试,优化器只是不计算不需要的东西,但是它在大型系统中的表现如何(尤其是热调用)?

如果这是发生RVO的地方,那么它是否可以在较大的通话序列(3-4)中工作以将其折叠为1个通话?

P.S。是的,是的,过早的优化是万恶之源,但我仍然想要一个答案

2 个答案:

答案 0 :(得分:2)

总体

,请参阅在https://godbolt.org/z/VB23-W第21行上生成的说明叮当声

   movsd   xmm0, qword ptr [rsp]   # xmm0 = mem[0],zero
   addsd   xmm0, qword ptr [rip + .LCPI3_0]

它只需要直接应用AddDetails的代码,而不用甚至调用您的操作员+。这称为内联,甚至对于这组价值返回调用也有效。

详细信息

不仅RVO优化可以发生在单行函数上,而且其他所有优化(包括内联)也可以参见https://godbolt.org/z/miX3u1https://godbolt.org/z/tNaSW

看看这一点,您可以看到gcc和clang甚至对非内联声明的代码(https://godbolt.org/z/8Wf3oR)都进行了很大的优化

#include <iostream>

struct Obj {
    Obj(double val) : float_val(val) {}
    Obj operator+(float b) {
        return AddDetails(*this, b);
    }
    Obj Add(float b) {
        return AddDetails(*this, b);
    }
    double val() const {
        return float_val;
    }
private:
    double float_val{0};
    static inline Obj AddDetails(Obj const& a, float b);
};

Obj Obj::AddDetails(Obj const& a, float b) {
    return Obj(a.float_val + b);
}


int main() {
    Obj foo{32};
    Obj bar{foo + 1337};
    std::cout << bar.val() << "\n";
}

即使没有内联也无法通过

看到额外的C-Tor调用
#include <iostream>

struct Obj {
    Obj(double val) : float_val(val) {}
    Obj operator+(float);
    Obj Add(float);
    double val() const {
        return float_val;
    }
private:
    double float_val{0};
    static Obj AddDetails(Obj const& a, float b);
};

Obj Obj::AddDetails(Obj const& a, float b) {
    return Obj(a.float_val + b);
}

Obj Obj::operator+(float b) {
    return AddDetails(*this, b);
}

Obj Obj::Add(float b) {
    return AddDetails(*this, b);
}

int main() {
    Obj foo{32};
    Obj bar{foo + 1337};
    std::cout << bar.val() << "\n";
}

但是,由于编译器知道该值不会更改,因此进行了一些优化,因此我们将main更改为

int main() {
    double d{};
    std::cin >> d;
    Obj foo{d};
    Obj bar{foo + 1337};
    std::cout << bar.val() << "\n";
}

但是您仍然可以在两个编译器上看到优化 https://godbolt.org/z/M2jaSHhttps://godbolt.org/z/OyQfJI

答案 1 :(得分:0)

据我了解,现代编译器需要在您的情况下应用复制省略。根据{{​​3}},当您编写return Obj(a.float_val + b, a.some_other_stuff)时,构造函数调用是prvalue;返回它不会创建临时对象,因此不会发生移动或复制。