Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/146.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 嵌套函数调用是否更快?_C++_C_Performance_Optimization_Compiler Construction - Fatal编程技术网

C++ 嵌套函数调用是否更快?

C++ 嵌套函数调用是否更快?,c++,c,performance,optimization,compiler-construction,C++,C,Performance,Optimization,Compiler Construction,我和一个朋友有一场愚蠢的争论,需要一个权威的说法 我有这两个片段,想知道哪一个更快?[甲或乙] (假设编译器没有优化任何内容) [A] [乙] 编辑:伙计们,这个问题在你们看来可能很傻,但我有一个硬件工程师朋友,他认为即使没有优化(使用任何处理器、任何编译器对)案例B总是更快,因为它不从上一条指令中获取结果的内存,而是通过绕过该数据直接从公共数据总线访问结果(记住5级管道) 虽然我的论点是,如果没有编译器通知要复制或检查的数据量,就不可能做到这一点(您必须进入内存获取数据,而没有编译器对其进行优

我和一个朋友有一场愚蠢的争论,需要一个权威的说法

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

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

[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语句之后保留该值。这可能意味着要把它推到堆栈上。

你为什么要假设