代码之家  ›  专栏  ›  技术社区  ›  Ramadheer Singh

嵌套函数调用是否更快?

  •  3
  • Ramadheer Singh  · 技术社区  · 15 年前

    我和一个朋友有这种愚蠢的争论,需要一个权威的词来解释。

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

    (假设编译器不优化任何内容)

    [A]

    if ( foo () ); 
    

    [B]

    int t = foo ();
    if ( t )
    

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

    同时 我的论点 如果没有编译器通知要复制或检查多少数据,就不可能做到这一点(如果没有编译器优化数据,就必须到内存中获取数据)

    6 回复  |  直到 15 年前
        1
  •  3
  •   Tyler McHenry    15 年前

    对于记录gcc,编译时特别禁用了优化( -O0 )为两个输入(在我的示例中,为 foo return rand(); 以便在编译时不确定结果)。

    无临时变量 t :

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

    这里,返回值 存储在EAX寄存器中,并对寄存器本身进行测试,以查看它是否为0,如果为0,它将跳过if块的主体。

    带临时变量 T :

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

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

    因此,如果我们进一步假设处理器在为此生成微码时没有进行任何“优化”,那么没有临时代码的版本应该更快一些时钟周期。它不会快得多,因为即使具有临时版本的版本涉及堆栈推送,当比较指令在单词后立即执行时,堆栈值几乎肯定仍将在处理器的一级缓存中,因此不会出现以及到RAM的行程。

    当然,只要打开任何优化级别,代码就会变得相同,甚至 -O1 还有,谁会编译任何如此关键的东西,以至于在关闭所有优化的情况下,他们只关心几个时钟周期?

    编辑: 关于您的硬件工程师朋友的进一步信息,我看不到如何访问一级缓存中的值。 更快 而不是直接访问寄存器。我可以看到它就在 以同样的速度 如果这个值从未离开管道,但我看不到它是 更快 尤其是因为它还必须执行 movl 除比较之外的说明。但是向他展示上面的汇编代码并询问他是怎么想的;这比用C语言讨论这个问题更有成效。

        2
  •  13
  •   Greg Hewgill    15 年前

    将[b]转换为[a]所需的“优化”是如此微不足道(尤其是如果 t 在其他任何地方都不使用)编译器可能不会 呼叫 这是一种优化。当然,它可能只是做了一些事情,不管是否明确地启用了优化。

    唯一的方法是让编译器为这两个代码位生成一个程序集源代码列表,然后比较它们。

        3
  •  4
  •   peterchen    15 年前

    执行摘要
    1。我们说的是纳秒。在那个时候,光移动了30厘米。 2。有时候,如果你真幸运的话,你的速度会更快。


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

    假设函数返回int:

    取决于编译器
    即使有“无优化”的限制,也无法保证生成的代码会是什么样子。B可能快10倍,编译器仍然是兼容的(你很可能不会注意到)。

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

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

    在典型的编译器上,差异至多是微乎其微的。
    那家商店 t 在堆栈变量中,两个额外的堆栈加载通常需要2个时钟周期(在我的CPU上小于一纳秒)。与“周边成本”相比,这是微不足道的——一个 ,的成本 它本身,还有一个分支。根据patform的不同,具有完整堆栈帧的未优化调用很容易花费20.200个周期。

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

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

        4
  •  2
  •   Michael    15 年前

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

        5
  •  0
  •   Ponkadoodle    15 年前

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

    在B语言中,编译器可能不会费心去发现是否再次使用t,因此它将被迫保留if语句之后的值。这可能意味着将它推到堆栈上。

        6
  •  -1
  •   Tom Cabanski    15 年前

    因为它不执行变量赋值,所以可能只快一点点。我们讨论的差异太小,无法衡量。