快速代码:
#include <stdio.h>
#include <iostream>
long fib(int num)
{
if (num <= 1)
return 1;
else
return fib(num-1) + fib(num-2);
}
int main()
{
long res = fib(45);
printf("%li\n", res);
return 0;
}
较慢的代码:
#include <stdio.h>
long fib(int num)
{
if (num <= 1)
return 1;
else
return fib(num-1) + fib(num-2);
}
int main()
{
long res = fib(45);
printf("%li\n", res);
return 0;
}
两者之间的唯一区别是第二行#include <iostream>
。
二者均使用带有-O2标志的clang ++ 8.0.0-3编译。
clang++-8 -O2 fib.cpp && time ./a.out # 3.59s
clang++-8 -O2 fib_io.cpp && time ./a.out # 3.15s
编辑:
重新启动后,行为似乎发生了变化,这次iostream版本变慢了,这更有意义。
我倾向于说这只是a幸,因为我再也无法复制了。
答案 0 :(得分:2)
当包含#include <iostream>
时,至少会有一个副作用:std::ios_base::Init
的一个实例必须被构造和销毁(请参阅C ++草案[ios.init]p1):
类
Init
描述了一个对象,该对象的构造可确保构造<iostream>
中声明的八个对象([iostream.objects]),这八个对象将文件流缓冲区与该对象提供的标准C流相关联。在<cstdio>
中声明的函数。
cppreference中对此的解释:
该类用于确保正确初始化和销毁默认C ++流(
std::cin
,std::cout
等)。该类跟踪创建它的实例数,并在构造第一个实例时初始化C ++流,并在销毁最后一个实例时刷新输出流。标头
<iostream>
的行为就像它(直接或间接)定义了具有静态存储持续时间的std::ios_base::Init
的实例一样:这样可以安全地访问构造函数和析构函数中的标准I / O流有序初始化的静态对象(只要在定义这些对象之前在转换单元中包含#include <iostream>
)
这不一定意味着性能应该有所不同(更好或更差)。但是,从C ++ Standard的角度来看,这意味着您的两个程序不相等。
没有查看给定标准库中的实际实现(或对其进行概要分析),我们无法知道详细的原因(随意这样做并添加答案!)。
在Linux机器上检查从clang生成的代码(这似乎是您的情况),即libstdc++
:
_GLOBAL__sub_I_a.cpp: # @_GLOBAL__sub_I_a.cpp
push rax
mov edi, offset std::__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edi, offset std::ios_base::Init::~Init() [complete object destructor]
mov esi, offset std::__ioinit
mov edx, offset __dso_handle
pop rax
jmp # TAILCALL
因此,std::ios_base::Init::Init()
或__cxa_atexit
都有一些副作用,使您的整体程序更快。