【C++】C++预编译头文件、基准测试benchmark
二十一、C++预编译头文件、基准测试benchmark
1、C++预编译头文件
预编译头文件(Precompiled Headers, PCH)对大型项目来说非常重要。
我们现在已经知道,开发一个项目,里面会有多个.cpp文件,编译这个项目时,先是对每个.cpp文件进行预处理,然后再编译,最后是将这多个文件合并成一个.exe文件。
我们知道我们开发的每个.cpp文件开头都会include很多头文件。当这个.cpp文件被预处理时,预处理器就会把所有的include语句替换成对应文件的全部内容,然后才进行编译、链接。
而替换后的源文件就已经是个庞然大物了。如果我们的项目非常大,那替换后的源文件就更大了。所以此时一般都会面临着编译一次要用很长的时间。如果此时源文件再有一点点改动,就不得不对整个文件进行重新编译。时间成本非常高。预编译头文件就可以帮我们节省大量时间。
预编译头文件就是让我们提前先整理出一些头文件,比如C++库、标准模板库、windows api类的、这些几乎永远不会改变的、而且几乎都要用到的东西,都放到一个单独的头文件中(通常命名为pch.h或类似名称),然后让编译器提前我这些头文件编译成一个二进制文件(编译器通常默认的是生成.pch或.gch格式的),以二进制格式存储到磁盘。当你编译你开发的整个源文件时,编译器就会直接使用已经编译完毕的二进制数据(.pch或.gch文件),这对编译器来说比单纯的文本处理要快得多。这样就可以加快编译速度。
下图展示一下,一个简单的main.cpp背后有多少行代码:
可见,当你的源文件中有很多include包含文件时,源文件预处理完毕后,代码就是巨量的。此时编译器要对这个巨量的文件进行切词tockenize、词法分析、语义分析、生成AST,然后链接器再在这个庞然大物中找到和源文件中实际代码相关的变量和函数,进行链接,链接成一个.exe文件。这个过程是非常耗时的。
那我们是不是可以把像C++库、标准模板库、windows api类的、这些几乎永远不会改变、而且几乎都要用到的东西,就都放到pch.h预编译头文件中,提前把这些文件都编译了。下面在VS中展示一下这个操作流程和效果:
2、基准测试benchmark
如何测量C++代码的性能?
当你写了一些代码,你想知道它实际运行有多块?那就做一个基准测试吧。但是很不幸,基准测试的方法有很多,比如有的人用第三方分析工具,而有的人则将代码封装到一个计时器中来测试特定的程序。不能说哪种方法好,哪种方法不好,只能说当你使用了不恰当的测量方法,不一定能得到真实的答案。因为每种方法方式都有自己的衡量标准,而且要做衡量性能这件事本身就可能会增加开销。
看下面例子,我就是想测试一下我的for循环10000次有多快,我创建一个作用域计时器:
上面的例子看似完美的测试了10000次for循环,但是你测量的真的就是你的10000次for循环被编译后的代码的运行时间吗?未必。因为C++编译器是可以优化我们写的代码的,有时会剥离某些代码,有时甚至会完全更改代码:
上图左下(debug模式下)和右边(release模式下)都是汇编代码,就是编译器已经编译了我写的代码(左上),然后从机器码又反编译回来的汇编代码。所以从汇编代码是可以看到cpu真正执行的指令的。
除了for循环的开销外,我们还想计算出一个数加另一个数需要多长时间?从左下的汇编码我们可以看到cpu确实是执行了加2。但是右边的汇编码就太逆天了。在编译阶段就算出了value,都不需要生成指令让cpu去计算了。所以在release模式下,计时器就记了个寂寞,你计的时间就是一个joke。
这个例子就是想说明你做基准测试时,一定要先确保你测试的对象是存在的,别测一个不存在的东西,那结果就是一个可笑的东西。
下面再看一个测试shared_ptr与unique_ptr的性能对比:
待续。。。