지난번에 네이버 블로그를 통해 현재 시간 얻는 방법에 대해 언급하면서 chrono에 대해 잠시 알아봤습니다.
현재 시간을 알아낼 수 있다는 것은 어떠한 함수 시작 부분에서의 시간을 얻어오고 함수의 끝 부분의 시간을 얻어온 후
두 시간의 차이를 측정하면 그 함수(이벤트)의 수행 시간을 알 수 있겠죠.
chrono 클래스란?
chrono는 C++11 에서 추가된 시간 관련된 라이브러리입니다.
clock, getTickCount, timeGetTime 등 어떠한 함수(이벤트)의 수행 시간을 측정하는 방법들이 많이 있지만 정밀도와 사용성 측면에서 chrono 를 사용하는 것을 추천합니다.
정밀도는 nano second 까지 지원하고, 시간 단위의 값 끼리 연산도 가능합니다.
chrono 사용법
chrono를 사용하기 위해서 #include <chrono>를 추가해 주어야 합니다.
가장 기본적으로 foo()라는 함수의 수행 시간을 측정하는 소스는 아래와 같습니다.
#include <iostream>
#include <chrono>
void foo()
{
int sum = 0;
for (int i = 0; i < 10000000; i++) {
sum += i;
}
}
int main()
{
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
foo();
std::chrono::system_clock::time_point end = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> msec = end - start;
std::cout << "실행시간: " << msec.count() << " msec" << std::endl;
return 0;
}
time_point는 시간상의 하나의 포인트를 의미하고 now() 함수를 통해 그 시점의 시간을 마킹한다라고 생각하면 됩니다.
경과 시간을 아래와 같은 단위로 나타낼 수 있습니다.
std::chrono::nanoseconds // 나노 세컨드
std::chrono::microseconds // 마이크로 세컨드
std::chrono::milliseconds // 밀리 세컨드
std::chrono::seconds // 초
std::chrono::minutes // 분
std::chrono::hours // 시
chrono 의 now()는 기본이 nanoseconds 로 반환하기 때문에 nano 초일 때에는 바로 가능하지만, 나머지 단위를 사용하려면 아래와 같이 chrono에서 제공하는 duration_cast< >를 사용해야 합니다.
std::chrono::nanoseconds nano = end - start;
std::chrono::microseconds micro = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::chrono::milliseconds milli = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::chrono::seconds sec = std::chrono::duration_cast<std::chrono::seconds>(end - start);
std::chrono::minutes min = std::chrono::duration_cast<std::chrono::minutes>(end - start);
std::chrono::hours hour = std::chrono::duration_cast<std::chrono::hours>(end - start);
이걸 이용해서 한번 측정 결과를 print 해 볼까요?
#include <iostream>
#include <chrono>
void foo()
{
int sum = 0;
for (int i = 0; i < 10000000; i++) {
sum += i;
}
}
int main()
{
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
foo();
std::chrono::system_clock::time_point end = std::chrono::system_clock::now();
std::chrono::nanoseconds nano = end - start;
std::chrono::microseconds micro = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::chrono::milliseconds milli = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::chrono::seconds sec = std::chrono::duration_cast<std::chrono::seconds>(end - start);
std::chrono::minutes min = std::chrono::duration_cast<std::chrono::minutes>(end - start);
std::chrono::hours hour = std::chrono::duration_cast<std::chrono::hours>(end - start);
std::cout << "실행시간(nanoseconds):" << nano.count() << " nsec" << std::endl;
std::cout << "실행시간(microseconds):" << micro.count() << " usec" << std::endl;
std::cout << "실행시간(milliseconds):" << milli.count() << " msec" << std::endl;
std::cout << "실행시간(seconds):" << sec.count() << " sec" << std::endl;
std::cout << "실행시간(minutes):" << min.count() << " min" << std::endl;
std::cout << "실행시간(hours):" << hour.count() << " hour" << std::endl;
return 0;
}
위와 같이 프린트 해 보면, 결과는 아래와 같아요.
아. 그런데 좀 소수점 자리가 짤리는게 좀 아쉽죠?
std::chrono::duration<double, std::micro> micro = end - start;
std::chrono::duration<double, std::milli> milli = end - start;
std::chrono::duration<double> sec = end - start;
std::chrono::duration<double, std::ratio<60>> min = end - start;
std::chrono::duration<double, std::ratio<3600>> hour = end - start;
위와 같이 duration을 이용한 템플릿을 사용하면 소수점 표현이 가능합니다.
전체 소스를 보면,
#include <iostream>
#include <chrono>
void foo()
{
int sum = 0;
for (int i = 0; i < 10000000; i++) {
sum += i;
}
}
int main()
{
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
foo();
std::chrono::system_clock::time_point end = std::chrono::system_clock::now();
std::chrono::nanoseconds nano = end - start;
std::chrono::duration<double, std::micro> micro = end - start;
std::chrono::duration<double, std::milli> milli = end - start;
std::chrono::duration<double> sec = end - start;
std::chrono::duration<double, std::ratio<60>> min = end - start;
std::chrono::duration<double, std::ratio<3600>> hour = end - start;
std::cout << "실행시간(nanoseconds):" << nano.count() << " nsec" << std::endl;
std::cout << "실행시간(microseconds):" << micro.count() << " usec" << std::endl;
std::cout << "실행시간(milliseconds):" << milli.count() << " msec" << std::endl;
std::cout << "실행시간(seconds):" << sec.count() << " sec" << std::endl;
std::cout << "실행시간(minutes):" << min.count() << " min" << std::endl;
std::cout << "실행시간(hours):" << hour.count() << " hour" << std::endl;
return 0;
}
위와 같이 되고, 결과는 아래와 같습니다.
clock 클래스들
chrono에는 정말 많은 clock들이 있지만, system_clock, steady_clock 정도만 알아두시면 됩니다.
1. system_clock
가장 일반적인 clock 클래스입니다.
이는 time_t와 호환성이 있어서 아래와 같이 사용할수 있습니다.
// time_point -> time_t
std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
time_t curTime = std::chrono::system_clock::to_time_t(tp);
std::cout << "1970년으로부터 몇 초 지났나요?:" << curTime << " 초" << std::endl;
// time_t -> time_point
time_t currTime = time(nullptr);
std::chrono::system_clock::time_point curTp = std::chrono::system_clock::from_time_t(currTime);
2. steady_clock
지난 chrono 강의 때 함수 시간 측정하려면 steady_clock을 사용하라고 했던거 기억하시나요?
steady_clock은 system_clock을 상속 받으면서 2개의 정적 변수를 추가로 가지고 있습니다.
steady_clock은 절대 거꾸로 가는 일이 없으므로 monotonic_clock이라는 별명이 있어요. 아래와 같이 system_clock과 똑같아요.
system_clock만 steady_clock으로 바꾸면 끝~!!
#include <iostream>
#include <chrono>
void foo()
{
int sum = 0;
for (int i = 0; i < 10000000; i++) {
sum += i;
}
}
int main()
{
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
foo();
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::chrono::nanoseconds nano = end - start;
std::cout << "실행시간(nanoseconds):" << nano.count() << " nsec" << std::endl;
return 0;
}
여기서 그냥 끝내면 제가 아쉽죠?
chrono의 부하는 어느정도일까요?
프로그램이 수행되는 동안 chrono를 통해 함수 실행 속도를 측정하는데, 이게 얼마만큼의 시간을 소모할까요?
for문을 통해 100,000회 now()를 불러봤는데요.
#include <iostream>
#include <chrono>
void foo()
{
int sum = 0;
std::chrono::steady_clock::time_point start;
for (int i = 0; i < 100000; i++) {
start = std::chrono::steady_clock::now();
}
}
int main()
{
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
foo();
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::chrono::duration<double, std::milli> milli = end - start;
std::cout << "실행시간(milliseconds):" << milli.count() << " msec" << std::endl;
return 0;
}
결과는 겨우 2.4 msec 입니다. 별 무리 없겠죠?
그럼 정말 꿀팁 하나를 더 투척하자면, 여러 함수가 있을 때 매번 chrono를 쓰기 귀찮죠? 소스도 복잡해지고.
매크로 이용하여 chrono 쉽게 사용하기
우리에겐 매크로라는 게 있어요.
아래와 같이 매크로를 만들어서 쓰면,
#define _MEASURE_START(STR) std::chrono::steady_clock::time_point start_##STR = std::chrono::steady_clock::now();
#define _MEASURE_END(STR) std::chrono::duration<double, std::milli> duration_msec_##STR = std::chrono::steady_clock::now() - start_##STR;\
std::cout << ">>>>> [" << #STR << "]\t" << duration_msec_##STR.count() << " msec\n";
여러 함수의 실행 속도를 쉽게 print 해 볼 수 있습니다.
좀만 수정하면 파일로도 저장할 수 있고, 다양하게 응용하실 수 있겠죠?
#include <iostream>
#include <chrono>
#define _MEASURE_START(STR) std::chrono::steady_clock::time_point start_##STR = std::chrono::steady_clock::now();
#define _MEASURE_END(STR) std::chrono::duration<double, std::milli> duration_msec_##STR = std::chrono::steady_clock::now() - start_##STR;\
std::cout << ">>>>> [" << #STR << "]\t" << duration_msec_##STR.count() << " msec\n";
void foo()
{
std::chrono::steady_clock::time_point start;
for (int i = 0; i < 100000; i++) {
start = std::chrono::steady_clock::now();
}
}
void goo()
{
int sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i;
}
}
int main()
{
_MEASURE_START(foo);
foo();
_MEASURE_END(foo);
_MEASURE_START(goo);
goo();
_MEASURE_END(goo);
return 0;
}
속도는 각자가 사용하는 컴퓨터 사양에 따라, Release, Debug 모드에 따라 달라질꺼에요.