C++:对微不足道的包装方法进行优化
C++: Optimizations made on wrapped methods that are trivial
当我写 wrapper 类 时,很多方法都采用以下形式:
class Wrapper{
public:
vec3 getPos(){ return m_position.get(); }
void setPos(vec3 pos){ m_position.set(pos); }
private:
ThingGettingWrapped m_position;
}
}
不过我很好奇,getPos()
和 setPos()
会产生开销吗,或者现代编译器是否足够聪明来优化委托,所以 wrapper.set/get()
等同于 wrapper.m_thingWrapped.set/get()
?
(我知道我的示例中的方法是内联的,但假设它们没有明确内联和单独定义)
只要遵循一些简单的规则,这些函数就会被编译器内联:
- 它们不是虚拟的(它们不是你的情况,但可能在其他地方)——编译器通常不能内联虚拟函数——因为它会使虚拟函数的整个概念变得无用。
- 函数的实际实现在编译源代码调用期间可供编译器使用
getPos
。同样,编译示例代码是不可能的,但您可以将 getPos
和 setPos
声明为成员函数,而无需在 class 中为它们提供主体,在这种情况下,编译器会获胜't(必然)能够内联它们。 [1]
[1] 支持 Link 时间优化 (LTO) 或全程序优化的编译器(一些编译器供应商喜欢这样称呼它)可以内联函数,即使源代码不可用 - 这是因为最终 code-generation 直到稍后才完成,因此 half-compiled 版本的源代码存储在目标文件中。根据我的经验,LTO 不用于大多数项目。
优化器可以扩展内联调用,在这种情况下没有额外调用的开销。
如果调用未内联扩展,那么 可以 产生开销。
现代编译器的优化器进行内联扩展。
assume they are not explicitly inlined and defined separately
除非优化器可以内联扩展调用,否则将(小)开销。您可以通过在定义它的同一翻译单元中调用该函数来保证内联的能力。定义内联函数 - 就像您在示例中所做的那样 - 为所有翻译单元启用并强制执行此操作,因为在所有翻译单元中都定义了内联函数。允许跨翻译单元内联扩展的另一种方法是 LTO.
函数不需要是内联的就可以内联扩展,并且优化器不需要inline-expand所有对内联函数的调用。内联函数将始终可内联扩展。非内联函数在某些情况下可能是可扩展的,但并非总是如此。
当我写 wrapper 类 时,很多方法都采用以下形式:
class Wrapper{
public:
vec3 getPos(){ return m_position.get(); }
void setPos(vec3 pos){ m_position.set(pos); }
private:
ThingGettingWrapped m_position;
}
}
不过我很好奇,getPos()
和 setPos()
会产生开销吗,或者现代编译器是否足够聪明来优化委托,所以 wrapper.set/get()
等同于 wrapper.m_thingWrapped.set/get()
?
(我知道我的示例中的方法是内联的,但假设它们没有明确内联和单独定义)
只要遵循一些简单的规则,这些函数就会被编译器内联:
- 它们不是虚拟的(它们不是你的情况,但可能在其他地方)——编译器通常不能内联虚拟函数——因为它会使虚拟函数的整个概念变得无用。
- 函数的实际实现在编译源代码调用期间可供编译器使用
getPos
。同样,编译示例代码是不可能的,但您可以将getPos
和setPos
声明为成员函数,而无需在 class 中为它们提供主体,在这种情况下,编译器会获胜't(必然)能够内联它们。 [1]
[1] 支持 Link 时间优化 (LTO) 或全程序优化的编译器(一些编译器供应商喜欢这样称呼它)可以内联函数,即使源代码不可用 - 这是因为最终 code-generation 直到稍后才完成,因此 half-compiled 版本的源代码存储在目标文件中。根据我的经验,LTO 不用于大多数项目。
优化器可以扩展内联调用,在这种情况下没有额外调用的开销。
如果调用未内联扩展,那么 可以 产生开销。
现代编译器的优化器进行内联扩展。
assume they are not explicitly inlined and defined separately
除非优化器可以内联扩展调用,否则将(小)开销。您可以通过在定义它的同一翻译单元中调用该函数来保证内联的能力。定义内联函数 - 就像您在示例中所做的那样 - 为所有翻译单元启用并强制执行此操作,因为在所有翻译单元中都定义了内联函数。允许跨翻译单元内联扩展的另一种方法是 LTO.
函数不需要是内联的就可以内联扩展,并且优化器不需要inline-expand所有对内联函数的调用。内联函数将始终可内联扩展。非内联函数在某些情况下可能是可扩展的,但并非总是如此。