我们在开发一个项目的时候对于程序的时间有着严格的要求,不过在开发的初期由于整个程序的设计上可能存在的一些问题,程序在实际运行时间上的表现要远超出预期。这个时候,我们需要知道每个函数具体的运行时间,然后针对性进行一些优化和处理。下面的这个方法也许是一个不错的选择。
1、编写一个Timer.h文件如下
#pragma once
#include<chrono>
#include<string>
class Timer {
private:
std::string mFunctionName;
int mLine;
std::chrono::time_point<std::chrono::high_resolution_clock> m_StartTimePoint;
public:
Timer(const std::string& iFunctionName,int iLine);
~Timer();
void Stop();
};
#ifdef _DEBUG
#ifndef TIMER
#define TIMER Timer timer(__FUNCTION__,__LINE__)
#endif
#else
#ifndef TIMER
#define TIMER
#endif
#endif
2、在编写一个Timer.cpp文件如下
#include"Timer.h"
#include<iostream>
Timer::Timer(const std::string& iFunctionName, int iLine):
mFunctionName(iFunctionName) ,
mLine(iLine)
{
m_StartTimePoint = std::chrono::high_resolution_clock::now();
}
Timer::~Timer() {
Stop();
}
void Timer::Stop() {
auto endTimepoint = std::chrono::high_resolution_clock::now();
auto start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimePoint).time_since_epoch().count();
auto end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint).time_since_epoch().count();
auto duration = end - start; //函数总共运行了多少微秒
double ms = duration * 0.001; //将微秒转换为毫秒
std::cout << "Function: " << mFunctionName << " | Line: " << mLine << "\n";
std::cout << duration << "us (" << ms << "ms)" << std::endl;
}
看头文件可知,通过使用定义的宏我们就可拿到对应的函数名称,以及在文件的第几行开始计时的。并且要定义全局 _DEBUG 才会进行计时。笔者使用的VS 2022在Debug模式下设置有_DEBUG 宏
3、使用的方式如下
#include<iostream>
#include"Timer.h"
void AccumulateAdd(int iNum, int iCount);
void AccumulateSub(int iNum, int iCount);
int main() {
AccumulateAdd(0, 10000);
AccumulateSub(0, 10000);
}
void AccumulateAdd(int iNum, int iCount) {
TIMER;
for (int i = 0; i < iCount; i++)
iNum += i;
}
void AccumulateSub(int iNum, int iCount) {
TIMER;
for (int i = 0; i < iCount; i++)
iNum -= i;
}
4、运行结果如下
Debug版本
Release版本
5、注意事项
编译器在Release,DeBug两个版本当中采取的优化方式不一样,很可能会直接改写代码,导致测试的能容其实并没运行。我们看到两个测试函数当中是两个循环,但是在Release模式下,这个循环很可能被直接改写,请看下面的例子。
#include<iostream>
#include"Timer.h"
void AccumulateAdd(int iNum, int iCount);
void AccumulateSub(int iNum, int iCount);
int main() {
AccumulateAdd(0, 10000);
AccumulateSub(0, 10000);
}
void AccumulateAdd(int iNum, int iCount) {
Timer timer(__FUNCTION__,__LINE__);
for (int i = 0; i < iCount; i++)
iNum += i;
}
void AccumulateSub(int iNum, int iCount) {
Timer timer(__FUNCTION__, __LINE__);
for (int i = 0; i < iCount; i++)
iNum -= i;
}
运行模式设置在Release模式下
结果如下
所以保证测试内容编译进入运行程序是关键。
6、总结
市面上有很多强大的IDE都有内置的基准测试工具,但是有些小团队可能给不出那么多的经费购买IDE,那么这个方法在项目开发的前中期也许会是一个不错的选择。