前言
基准测试就是你编写了一段代码,并且需要查看他的实际运行速度。因为你正在编写的代码对性能至关重要。如果你正在测试你刚刚学会的某种性能测试技术,但是向通过过去使用方式比较性能,那么那种性能测试方法会更合理呢?在C++中你可能碰到这种抉择。但基准测试并没有一个正确的答案,因为有很多不同的方法可以用来进行基准测试。
每个程序猿都有衡量程序性能的性能分析方法,因为这是一个非常棘手的问题。如果你用错了,那么可能给你待测试的代码本身增加额外的开销。有人喜欢使用第三方性能分析工具,有人喜欢在待检测的代码上下文添加计时器之类的测试代码。所以,没有唯一的方法,适合你的才是正确的。
本篇和大家一起探讨如何实际衡量C++代码的性能。还有需要指出的是,基准测试的主题远不止用于测试代码性能的工具。如果你要衡量一段理论假设开销的代码,而代码本身需要对该代码进行引用或解引操作。还有许多方法可以做到这一点(日后开篇写其他文章)。
示例代码
下面代码示例将会被用于测试期性能开销。
#include <iostream>
#include <memory>
#include "header/utils/debugbreak.hh"
int main(int argc, char const *argv[])
{
int v=0;
for (size_t i = 0; i < 1000000; I++)
{
v+=2;
}
std::cout<<v<<std::endl;
debug_break();
return 0;
}
示例代码中有个比较显著的debugbreak()的一个自定义断点函数,这是github上提供的第三方库,具体链接https://github.com/scottt/debugbreak
我们希望通过一些自定义的计时器的工具类来分析示例代码的能够跑多快,基本的做法就是将被测试的代码放到一个基于计时器类的作用域之中。
接下来,我们创建一个Timer类,因此必须依赖std::chrono,代码实现如下
#include <chrono>
class Timer{
private:
std::chrono::time_point<std::chrono::high_resolution_clock> d_start;
public:
//构造函数构造一个启动计时器
Timer(){
d_start=std::chrono::high_resolution_clock::now();
}
~Timer(){
stop();
}
void stop(){
auto d_end=std::chrono::high_resolution_clock::now();
auto _start=std::chrono::time_point_cast<std::chrono::microseconds>(d_start)
.time_since_epoch()
.count();
auto _end=std::chrono::time_point_cast<std::chrono::microseconds>(d_end)
.time_since_epoch()
.count();
auto duration=_end-_start;
double ms=duration*0.001;
std::cout<<"耗时:"<<duration<<"us / "<<ms<<"ms\n";
}
};
- Time::Time() 构造函数内部启动标准库的计时器,从高分辨计时器中获得的待测试程序的起始时间点 ,并且我们将该时间点存放到私有的成员变量d_start
- Timer::~Timer()函数将在析构函数中自动调用,因为当退出Timer作用域时,编译器会自动调用Time::~Timer()析构函数,继而再次调用Timer::stop()函数,这基本上意味着它与对象的生存期相关。
- Timer::stop()函数,当该函数被调用时,函数内部会停止计时器,因此会获得一个结束时间点。而stop函数的内部就是需要计算结束时间点和起始时间点的时间间隔
需要知道的是,Timer类计算的时间单位是微秒(us),虽然你可以使毫秒,这完全取决于你测量的时间,如果小于1毫秒,那么以毫米为单位的测量结果完全没有参考价值。
另外一个需要注意的是,Timer::stop函数输出的耗时结果打印中,我们没有用到std::endl,而使用"\n",因为std::endl操作符号很慢,因为它必须刷新缓冲区的每一行。
网友评论