C++ 嵌套函数调用是否更快?
我和一个朋友有一场愚蠢的争论,需要一个权威的说法 我有这两个片段,想知道哪一个更快?[甲或乙] (假设编译器没有优化任何内容) [A] [乙] 编辑:伙计们,这个问题在你们看来可能很傻,但我有一个硬件工程师朋友,他认为即使没有优化(使用任何处理器、任何编译器对)案例B总是更快,因为它不从上一条指令中获取结果的内存,而是通过绕过该数据直接从公共数据总线访问结果(记住5级管道)C++ 嵌套函数调用是否更快?,c++,c,performance,optimization,compiler-construction,C++,C,Performance,Optimization,Compiler Construction,我和一个朋友有一场愚蠢的争论,需要一个权威的说法 我有这两个片段,想知道哪一个更快?[甲或乙] (假设编译器没有优化任何内容) [A] [乙] 编辑:伙计们,这个问题在你们看来可能很傻,但我有一个硬件工程师朋友,他认为即使没有优化(使用任何处理器、任何编译器对)案例B总是更快,因为它不从上一条指令中获取结果的内存,而是通过绕过该数据直接从公共数据总线访问结果(记住5级管道) 虽然我的论点是,如果没有编译器通知要复制或检查的数据量,就不可能做到这一点(您必须进入内存获取数据,而没有编译器对其进行优
虽然我的论点是,如果没有编译器通知要复制或检查的数据量,就不可能做到这一点(您必须进入内存获取数据,而没有编译器对其进行优化)由于不执行变量赋值,A可能只会快一点点。我们讨论的差异太小,无法衡量。将[B]转换为[A]所需的“优化”非常微小(特别是如果
t
没有在其他任何地方使用),编译器甚至可能不会称之为优化。不管是否明确启用了优化,这可能只是一件理所当然的事情
唯一的方法是让编译器为这两段代码生成一个汇编源代码列表,然后比较它们。它们可能都是相同的。在任何一种情况下,该int都将存储到寄存器中 执行摘要
1.我们说的是纳秒。在这段时间里,光线移动了30厘米。 2.有时候,如果你真的幸运的话,[A]会更快
旁注:[B]可能有不同的含义
如果
foo
的返回类型不是int
,而是同时具有int
和bool
隐式转换的对象,则执行不同的代码路径。其中可能包含睡眠
假设函数返回int:
取决于编译器即使有“无优化”的限制,也无法保证生成的代码会是什么样子。B可以快10倍,编译器仍然是兼容的(而且您很可能不会注意到) 取决于硬件
根据您的体系结构,生成的代码甚至可能没有差异,无论您的编译器尝试了多少次 假设在现代x86/x64体系结构上使用现代编译器: 在典型的编译器上,差异最多也很小
如果将
t
存储在堆栈变量中,则两个额外的堆栈加载通常需要2个时钟周期(在我的CPU上不到一纳秒)。与“周边成本”(调用foo
)、foo
本身以及分支机构的成本相比,这是微不足道的。使用完整堆栈帧的未优化调用很容易花费20.200个周期,具体取决于patform
比较:不在一级缓存中的单个内存访问的周期成本(大约:从二级缓存开始100个周期,从主缓存开始1000个周期,从磁盘开始几十万个周期)
…甚至不存在即使你的编译器没有优化,你的CPU也可能会。由于配对/微码生成,周期成本实际上可能相同 对于记录,gcc在编译时特别禁用了优化(
-O0
),为两个输入生成不同的代码(在我的例子中,foo
的主体是return rand();
,因此在编译时不会确定结果)
不带临时变量t
:
movl $0, %eax
call foo
testl %eax, %eax
je .L4
/* inside of if block */
.L4:
/* rest of main() */
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,如果为0,则跳过if块的主体
使用临时变量t
:
movl $0, %eax
call foo
testl %eax, %eax
je .L4
/* inside of if block */
.L4:
/* rest of main() */
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块的主体
因此,如果我们进一步假设处理器在为此生成微码时没有进行任何“优化”,那么没有临时代码的版本应该快几个时钟周期。它不会更快,因为即使带有临时的版本涉及堆栈推送,当比较指令在字后立即执行时,堆栈值几乎肯定仍然会在处理器的一级缓存中,因此不会有到RAM的往返
当然,只要您打开任何优化级别,甚至是-O1
,代码就会完全相同。谁编译了如此关键的东西,以至于他们在关闭所有优化的情况下只关心少数时钟周期
编辑:关于您的硬件工程师朋友的更多信息,我看不出访问一级缓存中的值比直接访问寄存器更快。如果该值从未离开管道,我可以看到它的速度几乎与管道一样快,但我看不到它的速度更快,特别是因为除了比较之外,它还必须执行
movl
指令。但是给他看上面的汇编代码,问问他怎么想;这将比试图用C语言来讨论这个问题更有成效。这实际上取决于编译器是如何构建的。但我认为在大多数情况下,A会更快。原因如下:
在B中,编译器可能不会费心去找出t是否再次被使用,因此它将被迫在if语句之后保留该值。这可能意味着要把它推到堆栈上。你为什么要假设