为什么C++和C之间会有这样的差异?
为什么C++和C之间会有这样的差异?
我想知道为什么这种情况总是发生... !!
我写了两个程序,一个是用C语言写的,另一个是用C++写的.. 两个程序都执行相同的操作,即打印从1到2000000的数字。我还在执行开始时设置了计时器.. 在打印完所有数字后,还会打印出经过的时间..
C++程序的经过时间总是大于C程序的。
我觉得时间差异很明显。我很好奇这是什么原因..????..
下面是这两个程序
//iotest.c
#include#include clock_t start=clock(); int main() { for(int i=0;i<2000000;i++) printf("%d\n",i); printf("Time Elapsed: %f\n",((double)clock()-start)/CLOCKS_PER_SEC); return 0; }
//iotest.cpp
#include#include using namespace std; clock_t start=clock(); int main() { for(int i=0;i<2000000;i++) cout<
// ver C++ 4.3.2
通过输入命令编译C程序
g++ iotest.c
执行后得到
1
.
.
2000000
Time Elapsed: 5.410000(不总是一样的..)
执行第二个程序
1
.
.
2000000
Time elapsed: 5.81(不总是一样的..)
在C++中,使用cout << endl
会强制刷新输出缓冲区。如果使用cout << "\n"
,可能会降低这种差异。
尽管如此,我个人认为iostreams不是最有效的机制,我预计printf仍然会更快。如果输出速度是应用程序的性能瓶颈,你应该使用fwrite来写入stdout,否则就坚持使用类型安全的cout。
你的第二段可能是对的,但如果stdout是终端,C会在stdout上使用行缓冲。所以我怀疑刷新不是问题,除非你正在重定向stdout。
造成这种差异的原因是C++中endl
会强制刷新输出缓冲区,而C中stdout在终端上使用行缓冲。解决方法是在C++中使用cout << "\n"
来替代endl
,或者在C中重定向stdout。
这个问题的出现的原因是,C++标准库在静态初始化期间可能有更多的工作要做,而你在重复刷新流,而iostreams有点慢。解决方法是将程序运行多次,然后取结果的平均值作为正确的基准。以下是解决方法的代码示例:
#include#include int main() { std::clock_t start = std::clock(); for (int i=0;i<2000000;i++) std::cout << i << '\n'; std::cout << "Time elapsed " << (static_cast (std::clock()-start)/CLOCKS_PER_SEC) << std::endl; return 0; }
另外,注意到我将time.h
更改为它的C++对应部分ctime
。然后是一些讨论,其中提到了静态初始化时间的差异对效率的影响可能比缓冲刷新问题更大。最后,问题的提问者承认错误并感谢指出,并表示两个程序经过编辑后给出了类似的结果。
C语言和C++语言之间的一个区别是,C++版本使用了endl
,它不仅插入了一个换行符,还会刷新缓冲区。输出的缓冲区刷新是输出过程中最耗时的部分。
如果你将C++程序改为使用count << i << "\n"
,那么两个程序的速度可能会相差不大。
下面是两个类似执行时间的程序:
C语言程序(a.c
):
#include#include int main() { clock_t start=clock(); for (int i=0; i<2000000; i++) printf("%d\n",i); clock_t end=clock(); fprintf(stderr, "Time Elapsed: %f\n",((double)end-start)/CLOCKS_PER_SEC); return 0; }
编译命令:gcc -O3 -std=c99 a.c
C++程序(b.cpp
):
#include#include using namespace std; int main() { clock_t start=clock(); for (int i=0;i<2000000;i++) cout << i << '\n'; clock_t end=clock(); cerr << "Time elapsed " << ((double)end-start)/CLOCKS_PER_SEC << endl; return 0; }
编译命令:g++ -O3 b.cpp
但是,如果输出被重定向到终端,C语言会自动进行行缓冲,所以问题并不仅仅是刷新缓冲区的原因。
通过对两个程序中每一行进行强制刷新,并将输出重定向到/dev/null
,可以发现a.c
比b.cpp
快大约12%。如果两个程序都避免刷新缓冲区并将输出重定向到/dev/null
,则运行时间相同(或者有差异的话,可能是b.cpp
稍微快一些)。
这个发现与之前的分析相吻合。
这可能意味着C++的缓冲区管理与C语言略有不同。额外的12%可能只是每次刷新缓冲区所需要的几百纳秒。
我已经多次遇到过endl
带来的惊讶,以至于不禁思考将EOL
和flush
打包到同一个对象中是否是一个设计错误。初学者无法理解,而专家们也是通过吃亏才学会的。
C++的iostreams内部还有大量的虚函数调用,这是为了使其正常工作。
如果反过来设计,你会看到同样多的用户会说:“我的程序崩溃了,但最后一行输出与崩溃位置完全不相干...”。如果语言的使用者不愿花20秒钟查阅文档,那么他们就应该为自己的困惑付出代价。
当两个程序都不刷新输出时,C++并不慢,所以虚函数调用的开销不是问题(除非它减慢了缓冲区的刷新)。