【问题标题】:C++ use templates to avoid compiler from checking a booleanC++ 使用模板来避免编译器检查布尔值
【发布时间】:2012-08-15 08:39:03
【问题描述】:

假设我有一个函数:

template <bool stuff>
inline void doSomething() {
    if(stuff) {
        cout << "Hello" << endl;
    }
    else {
        cout << "Goodbye" << endl;
    }
}

我这样称呼它:

doSomething<true>();
doSomething<false>();

它会弹出:

Hello
Goodbye

我真正想知道的是编译器是否完全优化了这个? 当我用true调用模板化函数时,它会创建一个只输出“Hello”并避免if语句和“Goodbye”代码的函数吗?

这对于我刚刚编写的这个巨大的函数非常有用,它应该非常优化并尽可能避免不必要的 if 语句检查。我有一种很好的感觉,至少在带有优化的发布版本中,如果不是在没有优化的调试版本中。

【问题讨论】:

  • 在优化方面,您遵循您的直觉。如果你这样做了,你就完蛋了,因为你被误导。查看生成的代码有多难?
  • @ablm 仅当您只想要给定构建的一个实现时。一旦选择取决于调用者(这似乎很可能在这里),它就会变得非常丑陋。
  • ... 并没有提供超过模板版本的任何好处。
  • @ablm 对不同构建的唯一引用是在最后,他推测编译器可能对模板版本进行的优化。
  • @ablm 实际上,我并不介意调试版本是否也经过优化。这实际上也只是一个关于模板如何工作的问题,而不一定是关于我应该如何最好地处理优化代码的问题。

标签: c++ templates boolean


【解决方案1】:

免责声明:没有人可以保证任何事情。

也就是说,对于任何编译器来说,这都是显而易见且简单的优化。可以肯定地说,它将被优化掉,除非优化器实际上是无用的。

由于您的“true”和“false”是常量,因此您明确地在每个类中创建了一个明显的死分支,编译器应该将其优化掉。应该在这里按字面意思理解 - 如果“优化”编译器没有删除死分支,我会认为这是一个主要的主要问题。

换句话说,如果您的编译器无法对此进行优化,那么应该评估的是该编译器的使用,而不是代码。

所以,我想说你的直觉是正确的:虽然是的,不能对每个编译器做出这样的“保证”,但我不会使用无法在任何生产环境中执行简单优化的编译器,并且当然不是在任何性能关键的一个。 (当然在发布版本中)。

所以,使用它。任何现代优化编译器都会将其优化掉,因为它是一种微不足道的优化。如有疑问,请检查反汇编,如果未优化,请将编译器更改为更现代的。

一般来说,如果您要编写任何类型的性能关键代码,您必须至少在一定程度上依赖编译器优化。

【讨论】:

    【解决方案2】:

    这本质上取决于编译器,因此您必须检查编译器的文档或生成的代码。但是在这种简单的情况下,您可以轻松地自己实现优化:

    template <bool stuff>
    inline void doSomething();
    
    template<>
    inline void doSomething<true>() {
        cout << "Hello" << endl;
    }
    
    template<>
    inline void doSomething<false>() {
        cout << "Goodbye" << endl;
    }
    

    但“优化”这个词实际上并不适合使用,因为这实际上可能会降低性能。如果它确实有益于您的代码性能,这只是一种优化。

    【讨论】:

      【解决方案3】:

      的确,它确实创建了两个函数,但是

      过早的优化是万恶之源

      尤其是如果您因为一个简单的 if 语句而更改了代码结构。我怀疑这会影响性能。此外,布尔值必须是静态的,这意味着您不能将运行时评估的 var 传递给函数。链接器应该如何知道要调用哪个函数?在这种情况下,您必须手动评估它并自行调用相应的函数。

      【讨论】:

      • 我同意过早的优化不是一件好事,但这只是一件微不足道的事情,我知道避免这些 if 语句肯定会减少一些指令。对于 3D 渲染器,我基本上每秒执行 60 次此算法。我想它可能会增加可执行文件的大小并使用更多的内存/影响缓存/我可能会影响的任何其他模糊事物,所以我可以尝试两种方式。
      • @Iseletsky 我想告诉你的是,与其优化一些小 if,不如优化渲染本身。如果您每秒迭代 60 次,那么一个小的 if 甚至没有影响。v也许如果您将迭代 10.000.000 次,您可能会感觉到很小的影响,但通常在这种情况下,诸如数学计算(pow、sqr 等)之类的东西会花费很多更多时间。除此之外,即使是 AAA 游戏也有相当抽象的 OOP 结构。我认为你应该更关心你如何存储你的 vericies 而不是试图消除一些 ifs。
      【解决方案4】:

      编译器非常擅长不断折叠。也就是说,在这种情况下,如果检查会一直保留到优化之后,我会感到惊讶。未优化的构建可能仍然有检查。最简单的验证方法是创建汇编程序输出并检查。

      也就是说,值得注意的是编译器必须检查两个分支的正确性,即使它只使用一个分支。这经常出现,例如,当对随机访问迭代器和其他迭代器使用稍微不同的算法时。该条件将取决于类型特征,并且其中一个分支可能无法编译,具体取决于特征测试的操作。委员会已经讨论在 static if 术语下关闭此检查,尽管尚未就功能的外观(如果添加)达成共识。

      【讨论】:

        【解决方案5】:

        如果我对您的理解正确,您希望(本质上)最终得到“两个”函数,这些函数针对真或假输入进行了优化,以便它们不需要检查该标志?

        除了可能产生的任何微不足道的优化(我反对过早的优化 - 我相信在优化之前测量之前的可维护性),我想说为什么不将您的函数重构为实际上是两个函数?如果他们有共同的代码,那么该代码也可以被重构出来。但是,如果要求是重构不是最优的,那么我会用#define 重构替换它。

        【讨论】:

        • 代码太难重构,所以有两个函数。这两个函数是相同的,除了在内部深处有一个内部 if 语句,它只需要以一种或另一种方式做某事。并且它需要在代码的另一部分再次进行检查,如果在另一部分中没有这样做,则在那里进行检查。这是一个非常具体的功能,无论如何只能在一个地方使用。与其有大量的代码重复,我正在考虑一种方法来让它成为一个函数调用,但仍然尽可能地优化速度。 (视锥体的 3D 光栅化)
        • #define 不起作用,因为我这样称呼它 doSomething(); doSomething
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-08
        • 2013-08-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-31
        • 1970-01-01
        相关资源
        最近更新 更多