실행하는 코드라인의 execution time을 측정하는 class이다.
Lap 이란 스톱워치에서 측정되는 기능으로, 각 구간의 차이를 측정해 주는 기능이다.
(달리기에서 1등 선수와 2등 선수의 시간 차이 같은 거 측정 한거)
//lap_logger.h
#include <string>
#include "stop_watch.h"
#include <unordered_map>
#include <iostream>
class LapLogger
{
public:
LapLogger(bool enableModule = false);
static const int LAP_LOGGER_MICROSECOND = 1000000;
static const int LAP_LOGGER_MILLISECOND = 1000;
static const int LAP_LOGGER_SECOND = 1;
bool bEnable;
void operator() (const std::string& item, bool resetTime = true);
void Reset();
void Start();
double GetTime(const std::string& item) const;
double GetTotalTime() const;
std::string ShowResult(int type = LAP_LOGGER_MILLISECOND) const;
private:
StopWatch watch;
std::unordered_map<std::string, double> ptimes;
double mTimeSum;
};
디버깅 단계에서는 사용하지만, 실제 완성된 코드에서는 사용되지 않을 때를 대비해서 생성자에서 argument로 사용여부를 결정할 수 있게 하였다.
맨 처음에 이 상수들에 대하여 enum을 써야 할지 static 상수를 사용할지 고민하였다.
static const int LAP_LOGGER_MICROSECOND = 1000000;
static const int LAP_LOGGER_MILLISECOND = 1000;
static const int LAP_LOGGER_SECOND = 1;
단순히 ShowResult에서 결과를 보여주기 위한 단위를 정하기 위해 서만 사용한다면 Enum으로 해도 좋았겠지만, ShowResult에서 저 상수들을 사용해 elapsed time에 받은 argument에 따라 상수를 사용해서 단위에 맞추어 elapsed time에 곱해줘야 한다.
그렇기에 static 상수를 사용하였다.
Start함수에서 부터 시간을 측정하여, 그다음 operator [](&<msg>)가 호출되기 전까지 시간을 계속 측정하게 할 생각이다.그러고 나서 그다음 operator [](&<msg>) 가 불릴 때까지 또 시간을 측정한다. operator [](&<msg>) 는 일종의 stop watch에서 lap타임을 측정하는 것과 같다고 생각하면 좋다.
//lap_logger.cpp
#include "lap_logger.h"
#include <iomanip>
#include <sstream>
LapLogger::LapLogger(bool enableModule)
: bEnable(enableModule)
, mTimeSum(0)
{}
void LapLogger::Reset()
{
if (!bEnable) return;
for (auto & it: ptimes)
{
it.second = 0;
}
mTimeSum = 0;
}
void LapLogger::Start()
{
if (!bEnable) return;
watch.Stop();
}
void LapLogger::operator() (const std::string& item, bool resetTime)
{
if (!bEnable) return;
double etime = watch.Stop();
if (resetTime)
{
ptimes[item] = etime;
}
else
{
ptimes[item] += etime;
}
watch.Stop();
mTimeSum += etime;
}
double LapLogger::GetTime(const std::string& item) const
{
return ptimes.at(item);
}
double LapLogger::GetTotalTime() const
{
return mTimeSum;
}
std::string LapLogger::ShowResult(int type) const
{
if (!bEnable) return "";
int maxStringLength(0);
for (const auto& it : ptimes)
{
maxStringLength = std::max(maxStringLength, static_cast<int>(it.first.size()));
}
maxStringLength++;
double sum_of_time(0.0);
std::string unit;
switch (type)
{
case LAP_LOGGER_MICROSECOND:
unit = std::string("[us]");
break;
case LAP_LOGGER_MILLISECOND:
unit = std::string("[ms]");
break;
case LAP_LOGGER_SECOND:
unit = std::string("[ s]");
break;
}
const int maxFieldWidth = (maxStringLength + 16 > std::numeric_limits<int>::max())
? std::numeric_limits<int>::max() : maxStringLength + 16;
std::ostringstream output;
output << std::left << std::setw(maxStringLength) << "Item" << "Elapsed Time " << unit << std::endl;
output << std::setfill('-') << std::setw(maxFieldWidth) << "-" << std::endl;
output << std::setfill(' ');
// write down on screen
for (const auto& it : ptimes)
{
double etime = static_cast<double>(it.second) * type;
output << std::left << std::setw(maxStringLength) << it.first << std::fixed << std::setprecision(4)
<< std::setw(10) << etime << " " << unit << std::endl;
sum_of_time += etime;
}
output << std::setfill('-') << std::setw(maxFieldWidth) << "-" << std::endl;
output << std::setfill(' ');
output << std::left << std::setw(maxStringLength) << "Total Time : " << std::fixed << std::setprecision(4)
<< std::setw(10) << sum_of_time << " " << unit << std::endl;
return output.str();
}
operator [](&<msg>)가 호출되면 msg를 키로 그리고 측정 결과를 값으로 hash자료구조 기반의 unorderd_map에 저장.
unorderd_mapdms GetTime으로 사용자가 특정 구간에서의 elapsedtime만을 알고 싶을때 빠르게 서치를 해서 제공 하기위해서 사용 하였다. Tree자료구조 기반의 map보다 빠른데다가, 불필요하게 key값을 기준으로 sorting 하지 않아주기에 훨씬 용이하다.
이 class를 사용하는 예시는 다음과 같다.
#include <iostream>
#include <thread>
#include "lap_logger.h"
#include "periodic_task.h"
int main()
{
pandora::LapLogger lap_logger(true);
std::cout << "Time logger start " << std::endl;
lap_logger.Start();
std::this_thread::sleep_for(std::chrono::seconds(1));
lap_logger("step1");
std::this_thread::sleep_for(std::chrono::seconds(1));
lap_logger("step2");
std::this_thread::sleep_for(std::chrono::seconds(1));
lap_logger("step3");
std::this_thread::sleep_for(std::chrono::seconds(2));
lap_logger("step4");
std::cout << lap_logger.ShowResult(pandora::LapLogger::LAP_LOGGER_MICROSECOND) << std::endl;
return 0;
}
위의 예제 코드를 실행하면 다음과 같은 결과가 나온다.
Time logger start
Item Elapsed Time [us]
----------------------
step1 1011985.7000 [us]
step2 1005382.5000 [us]
step3 1014848.3000 [us]
step4 2011307.0000 [us]
----------------------
Total Time : 5043523.5000 [us]
실행 시간에 민감한 프로그램을 만들면서 실시간으로 디버깅을 하고자 할 때 유용하게 사용 가능하다.
'C++ > 프로젝트' 카테고리의 다른 글
[C++][프로젝트]판도라 라이브러리 배포를 위한 준비 (0) | 2023.03.05 |
---|