优化是否应用于单行函数?

Is optimization applied to single-line functions?

我不喜欢在代码中重复自己,也不想因为简单的功能而失去性能。假设 class 具有 operator+ 和具有相同功能的函数 Add(将前者视为在表达式中使用 class 的便捷方式,将后者视为 "expilicit" 的方式)

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 次呼叫,这并不令人愉快。

但是编译器是否足够智能以消除此类双重调用?

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

如果这是发生 RVO 的地方,那么在更大的调用序列 (3-4) 中是否可以在 1 次调用中折叠它?

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

据我了解,现代编译器需要在您的案例中应用复制省略。根据https://en.cppreference.com/w/cpp/language/copy_elision,当你写return Obj(a.float_val + b, a.some_other_stuff)时,构造函数调用是纯右值;返回它不会创建临时对象,因此不会发生移动或复制。

总体

参见https://godbolt.org/z/VB23-W第21行

生成的指令clang
   movsd   xmm0, qword ptr [rsp]   # xmm0 = mem[0],zero
   addsd   xmm0, qword ptr [rip + .LCPI3_0]

它只是直接应用 AddDetails 的代码,而不是调用您的 operator+。这称为内联,甚至适用于此价值链返回调用。

详情

不仅 RVO 优化会发生在单行函数上,包括内联在内的所有其他优化都发生在 https://godbolt.org/z/miX3u1 and https://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/M2jaSH and https://godbolt.org/z/OyQfJI