标签(goto)的数量如何影响性能?

C/C++ How does the number of labels (goto) influence performance?

本文关键字:影响 性能 何影响 goto 标签      更新时间:2023-10-16

我猜这主要是关于破坏C/c++优化器。

l_line1:
   do_stuff(a, b);
l_line2:
   do_other_stuff(n, m, o);
l_line3:
   do_more_stuff(x);

如果标签在if块内,如果在运行中不为真,性能是否有差异?

if (debug_active) {
   l_line1:
   nextline = debugme(d1, d2, d3);
   goto nextline_check;
}
do_stuff(a, b);
if (debug_active) {
   l_line2:
   nextline = debugme(d1, d2, d3);
   goto nextline_check;
}
do_other_stuff(n, m, o);
if (debug_active) {
   l_line3:
   nextline = debugme(d1, d2, d3);
   goto nextline_check;
}
do_more_stuff(x);
return;
nextline_check:
   switch (nextline) {
     case 1: goto l_line1;
     case 2: goto l_line2;
     case 3: goto l_line3;
     default: DEB_ASSERT("Wrong line");
   }

:这些行是为源代码级调试器插入的。所有这些goto的原因是因为代码是生成的("原始"代码- 从另一种语言翻译成C/c++ -这是第一个没有标签的示例,第二个或多或少是调试选项生成的)。如果您希望能够在调试期间跳过行,甚至更改下一个语句(两者都将由"用户"在函数debugme期间设置),您需要在每个语句和goto之前添加标签,这些标签依赖于返回(在这种情况下简化为返回代码)。

通过可移植代码获得更好性能的一般想法也会很好(非可移植的建议也会很好-至少对于GCC和MSC)。

C和c++中的标签和goto很像汇编中的标签和分支,这可能是编译器将生成的。一定要查看示例程序生成的代码,以了解编译器的功能。如果这就是标签和goto的处理方式,那么"性能"就像在汇编器中使用相同的。

还应该注意的是,编译器将在机器码中生成大量的跳转,循环和条件以及函数调用都会导致跳转,并且没有太多的性能损失。然而,编译器比您更了解底层CPU的细节,因此可以生成更好的跳转,而不会像您的无条件跳转那样破坏指令缓存或使指令管道停滞,因此使用跳转可能确实会导致很小的性能下降,尽管它很少会引起注意,除非您使用标签和goto而不是循环或条件语句。 也就是说,是跳转导致了任何可能的性能问题,标签本身只是编译器中的占位符,因此它可以为机器码生成正确的跳转偏移量。一旦程序通过编译器,标签就不再存在于目标文件或生成的代码中。

最后是反对使用goto的常用建议。虽然它在少数情况下可能是有意义的,但大多数程序员永远不会遇到goto有意义的情况。简单地说,不要使用它,语言的其他结构通常会更适合,或者至少更优化,至少更易于维护。

我认为,通过"在运行中永远不为真",您承认变量debug_active在运行时可以取true或false值——尽管在任何给定的运行中只有一个这样的值——换句话说,它不是编译时宏。考虑到这一点:

if语句内或外的标签不会破坏优化器优化这段代码的能力。事实上,它所依赖的控制流在第二个版本中被debugme调用完全破坏,返回一个int,然后在nextline_checkswitch中使用该int来动态选择要执行的下一个代码块。任何依赖于通过知道控制流来计算数据流的优化都将在那里失败。

第一个版本更好,但不是很多,因为当控制流无法确定时,标签也会混淆许多/所有优化,这在许多情况下是可能的。除非有一个debugme/nextline_check隐藏在你没有显示的地方——在这种情况下,同样的问题就会出现在第二个版本中。

(实际上,对于苹果与苹果的比较,我们需要看到第一种情况下的分派代码,就像您在第二种情况中提供的那样。)

在任何情况下,无论您做什么,生成的代码的"性能"都将非常糟糕(与优化的未检测的原始源代码相比)。您唯一的问题是:它是否糟糕到足以破坏您的调试器概念,因为它使用户感到沮丧,而不是使调试器提供的好处使他们满意?(注:

就像一个简单的例子,优化器将不能在你的第二个版本中做的:它不能假设任何特定的值在语句开始的给定寄存器中(即,最初出现在源代码中的语句),因为它已经留在了前面的语句中。

我怀疑标签的数量是否有任何实际的性能影响,因为如果没有跳转到它们,就没有成本-它们只是可能的(符号)目的地,它们本身不会导致任何代码,甚至不会进入最终的目标文件。即使有东西跳转到它们,它们也很容易被跳转位置的常量所替换。但是,通过检查生成的asm,您应该能够轻松地验证或反驳这一点—您应该看到所有标签都消失了。

我是这样做的。不需要goto

if (! debug_active) {
    // fast path when not debugging
    do_stuff(a, b);
    do_other_stuff(n, m, o);
    do_more_stuff(x);
} else {
   nextline = debugme(d1, d2, d3);
   while ( true ) {
      if( nextline == 1 ) {
          do_stuff(a, b);
          nextline = debugme(d1, d2, d3);
          if( nextline < 2 ) continue;
      }
     if( nextline == 2 ) {
        do_other_stuff(n, m, o);
        nextline = debugme(d1, d2, d3);
        if( nextline < 3 ) continue;
     }
     if( nextline == 3 ) {
         do_more_stuff(x);
         nextline = debugme(d1, d2, d3);
         if( nextline < 4 ) continue;
     }
     break;
   } // while
} // if
return;