在调用方法

时间:2015-05-21 14:25:30

标签: c++ performance templates

这个问题主要与我所拥有的大量代码维护和可重用性问题有关。我有几个类有这样的方法:

void MyClassX::Draw(){

   ///Some large chunk of code#1 here


   ///Another large chunk of code#2 goes here


   ///Another large chunk of code#3 goes here

}


void MyClassY::Draw(){

   ///Some large chunk of code#1 here


   ///Another large chunk of code#2 goes here

}

该方法的变体之间的差异在于,这些代码块中的一些在不同的类中是可选的。 现在,它可能听起来很愚蠢,但因为该方法做了一些性能关键的实时处理(3D渲染)。我没有为这些代码块使用函数,也不想用它们包装它们,因为我试图减少一般的函数调用。我想要的是在某个块中定义这些函数(例如使用#define),并在void Draw()内的适当位置声明那些而不是整个代码块。我不想为它使用预处理器,因为很难编辑这样的代码(没有正确的错误提示或调试)。我认为另一种选择是内联函数。但是因为编译器不保证内联我不确定这种方式是否有效。还有其他技巧吗?也许通过C ++模板使用?我可以强制一些函数体在调用方法内衰减吗?

P.S。由于性能考虑,我也不对这些类使用继承,因为它基本上也可以通过将所有这些块定义为基类中的函数来解决该问题。同样在void Draw()内部,我尝试将条件分支减少到最低限度。

1 个答案:

答案 0 :(得分:4)

有一些方法可以在某些编译器上强制使用内联函数,例如MSVC上的__forceinline或GCC上的__attribute__((always_inline))。它是特定于编译器的,但你可以保证它有一些警告。在那里,我建议检查您的编译器文档以及习惯于将反汇编分析到某个广泛的级别(足以识别函数调用,例如,这很容易,因为您可以使用调试器跟踪它并注意它的分支) )。

那就是说,我真的认为你可能会看到这个错误。我可能是错的,也许你有非常特殊的需求我以前没有遇到过,但我处在一个性能关键的领域,除了正确性,效率通常与感知质量成正比。产品(我也是3D,包括路径追踪)。

我过去曾经在手中使用过分析器,而且我已经学会了用一点点信任优化器的想法。优化器和标准库没有做得很好,而且我习惯于做一些事情,比如滚动我自己的内存分配器并获得相当大的收益。

但我从未在我的众多分析会话中遇到过一个案例,其中编译器在函数调用的开销方面表现不佳。我已经看到优化器有打嗝并且在指令选择或寄存器分配方面表现不佳,其中以某种方式重写代码实际上不应该帮助发出更高效的汇编。这包括将一大块代码转换为更多的函数,你不会认为这些函数有助于提高性能,但却帮助编译器完成这两个方面(寄存器分配和指令选择)。

我在优化中发现的一个更有帮助的事情是优化器可能很棒,但是他们不了解在运行时将接收哪种用户输入。这是常见案例执行的一个关键方面,只有了解产品设计的人才能预料到。所以我发现的一件事就是将罕见的代码分支放入单独的,无衬里的函数中,这样逻辑就不会在一个巨大的blob中全部内联实际上有所帮助(没有调查过)所有这些情况的反汇编,只是注意到时间的改进)。我认为它只是帮助编译器不要将所有代码视为一个平坦的竞争领域,帮助它更有选择性地/本地优化,因为只有那么多的寄存器,例如,将所有内容放在一个巨大的函数中可能会使优化器混淆哪个部分需要更多关注。

也就是说,我发现在分析和优化期间使用更集中的代码通常很有帮助。这并不是因为在大型功能中使用更集中的代码实际上可以加快速度,但是因为当您尝试挤出微观级别的效率时,更容易使用结构较少的东西。在你完成微调之后,更容易强加更多的结构,而不是在你经历那些详尽的分析会议之前解开它,所以有时候以这种方式编写代码并牺牲它会很有帮助大块代码的一些整洁和结构。但是,在对其进行分析并在后见之明获得效率知识以帮助组织代码之后,更容易更改这种代码;我从来没有真正看到这样的代码实际上让事情变得更快。

所以我真的认为你最好不要专注于内联那么多,而是让它更多地归功于优化器。通过更好的方式进行优化,即使在最小的微效率水平下也能获得更清晰的收益。

相关问题