嵌套函数调用速度与否?

时间:2010-04-25 01:18:22

标签: c++ c performance optimization compiler-construction

我和朋友讨论这个愚蠢的论点,并且需要一个权威性的词语。

我有这两个片段,想知道哪一个更快? [A或B]

(假设编译器没有优化任何东西)

[A]

if ( foo () ); 

[B]

int t = foo ();
if ( t )

编辑:伙计们,这对你来说可能看起来很愚蠢,但我有一个硬件工程师朋友,他认为即使没有优化(带任何处理器,任何处理器)编译器对)CASE B总是更快,因为它不会从前一条指令获取内存,而是通过绕过该数据直接访问公共数据总线的结果(记住5级流水线)。

虽然 My Argument 是这样的,没有编译器通知要复制或检查多少数据,但是不可能这样做(你必须转到内存来获取数据,没有编译器优化那个)

6 个答案:

答案 0 :(得分:13)

将[B]转换为[A]所需的“优化”非常简单(特别是如果t未在其他任何地方使用),编译器可能甚至不会调用这是一个优化。当然,无论是否明确启用优化,它都可能是它所做的事情。

要告诉的唯一方法是让编译器为两个代码位生成汇编源列表,然后进行比较。

答案 1 :(得分:4)

执行摘要
我们谈论的是纳秒。在那段时间里,光线移动了30厘米。 有时,如果你真的很幸运,[A]会更快


旁注:[B]可能有不同的含义
如果foo的返回类型不是int,而是对intbool进行隐式转换的对象,则会执行不同的代码路径。一个可能包含Sleep

假设函数返回int:

取决于编译器
即使受到“无优化”的限制,也无法保证生成的代码的外观如何。 B可能快10倍,编译器仍然符合要求(你很可能不会注意到)。

取决于硬件
根据您的体系结构,无论您的编译器尝试了多少,生成的代码都可能没有区别。

假设在现代x86 / x64架构上使用现代编译器:

在典型的编译器上,差异至多是微不足道的
 将t存储在堆栈变量中,两个额外的堆栈负载通常需要2个时钟周期(在我的CPU上不到一纳秒)。与“周围成本”相比,这是微不足道的 - 对foo的调用,foo本身的成本以及分支。使用完整堆栈帧的未优化调用很容易花费20.200个周期,具体取决于patform。

用于比较:不在第一级缓存中的单个内存访问的周期成本(大约:第二级100个周期,主要1000个,磁盘数十万个)

......甚至不存在
即使你的编译器没有优化,你的CPU也可能。由于配对/微码生成,循环成本实际上可能是相同的。

答案 2 :(得分:3)

对于记录,gcc在使用特别禁用的优化(-O0)进行编译时,会为两个输入生成不同的代码(在我的情况下,foo的正文为return rand();所以结果不会在编译时确定。)

没有临时变量t

        movl    $0, %eax
        call    foo
        testl   %eax, %eax
        je      .L4
        /* inside of if block */
.L4:
        /* rest of main() */

这里,foo的返回值存储在EAX寄存器中,寄存器针对自身进行测试以查看它是否为0,如果是,则跳过if块的主体。 / p>

使用临时变量t

        movl    $0, %eax
        call    foo
        movl    %eax, -4(%rbp)
        cmpl    $0, -4(%rbp)
        je      .L4
        /* inside of if block */
.L4:
        /* rest of main() */

这里,foo的返回值存储在EAX寄存器中,然后压入堆栈。然后,将堆栈上的位置内容与文字0进行比较,如果它们相等,则跳过if块的主体。

因此,如果我们进一步假设处理器在为此生成微码时没有进行任何“优化”,那么没有临时的版本应该更快几个时钟周期。它不会快得多,因为即使具有临时的版本涉及堆栈推送,当比较指令在字之后立即执行时,堆栈值几乎肯定仍然在处理器的L1缓存中,因此不会是一次往返RAM。

当你打开任何优化级别时,代码变得相同,甚至是-O1,并且编译任何非常重要的东西,以至于他们关心所有优化关闭的少数时钟周期?

编辑:关于您有关硬件工程师朋友的更多信息,我无法看到如何访问L1缓存中的值更快直接注册。如果值永远不会离开管道,我可以看到它只是快速,但我看不到它更快,特别是因为它仍然必须执行除了比较之外,还有movl条指令。但是告诉他上面的汇编代码并询问他的想法;它比以C语言讨论问题更有成效。

答案 3 :(得分:2)

它们可能都是一样的。在任何一种情况下,int都将存储到寄存器中。

答案 4 :(得分:0)

这实际上取决于编译器的构建方式。但我认为在大多数情况下,A会更快。原因如下:

在B中,编译器可能不会发现t是否再次被使用,因此它将被强制保留if语句之后的值。这可能意味着将其推入堆栈。

答案 5 :(得分:-1)

A可能会快一点,因为它不会进行变量赋值。我们所谈论的差异太小而无法衡量。