반응형


boost 에도 chrono 가 있다

거의 동일하다봐도됨



http://jacking.tistory.com/988



[ VC11-C++11 ] chrono - 시간 측정하기

chrono는 C++11에서 새로 추가된 시간 라이브러리입니다기존의 C 런타임에서 제공하는 time 함수에 비해서 다양한 기능이 있고사용이 쉽고 정밀도는 훨씬 높습니다. time 함수는 초 단위의 값만 측정할 수 있는 것에 비해 chrono는 나노 밀리 초 단위도 측정할 수 있습니다.

 

현재의 C++(C++03)에서는 초 단위보다 더 정밀한 단위로 시간을 측정할 때는 OS에서 제공하는 API를 사용해야 했는데 chrono를 사용하면 OS 독립적으로 높은 단위의 시간을 측정할 수 있습니다.

 

VC10에서는 chrono가 들어가지 않았지만 이번 VC11에서 chrono STL에 들어갔습니다만약 VC11을 사용하지 못하는 경우라면 Boost 라이브러리를 사용하면 사용할 수 있습니다.

 

chrono를 사용하면 특정 시간 구간에 걸린 시간을 초밀리 초나노 초 단위로 얻을 수 있으며 또 시간끼리 연산을 할 수 있습니다

 

 

 

chrono 사용 방법

 

chrono를 사용하기 위해서는 아래의 헤더 파일을 추가합니다.

#include <chrono>

 

 

chrono에 대한 자세한 설명에 앞서 어떤 기능인지 좀 더 쉽게 알 수 있도록 예제를 하나 보여드리겠습니다아래의 예제는 어떤 함수의 성능을 측정하기 위해서 chrono를 사용했습니다.

 

 

예제. 1 >

#include <chrono>

#include <iostream>

#include <cmath>

 

 

void Test()

{

           for ( long i = 0; i < 10000000; ++i )

           {

                     std::sqrt( 123.456L );

           }

}

 

 

 

int main()

{

 

    std::chrono::system_clock::time_point start = std::chrono::system_clock::now();

 

    Test();

          

    std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;

 

    std::cout << "Test() 함수를 수행하는 걸린 시간() : " << sec.count() << " seconds" << std::endl;

 

    return 0;

}

 

실행 결과 >

 


<예제.1>에서는 std::chrono::system_clock::now()을 사용하여 현재 시간을 얻습니다.

 std::chrono::system_clock::time_point start = std::chrono::system_clock::now();

여기서 time_point 타입은 시간 상의 한 축을 뜻합니다.

 

 

 

이후 Test() 함수를 실행한 후 다시 현재 시간을 얻은 후 Test()를 시작하기 전에 저장한 현지 시간을 빼면 Test()를 수행하는 걸린 시간을 얻을 수 있습니다.

 

std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;

 

 

* std::chrono::system_clock::now()에 의해서 얻는 시간의 초기 시간은 1970년 입니다.

 

 


 

시간 단위

chrono는 경과 시간을 계산할 때 다양한 시간 타입으로 계산할 수 있습니다.

 

<예제.1>에서는 초 단위의 정밀도로 소수점까지 표시할 수 있었습니다.

 

std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;

 

그런데 보통은 소수점이 나오는 결과 값보다는 정수로 나오는 값을 사용하는 경우가 많을 것입니다.

 

 

chrono에서는 경과 시간을 나타내는 클래스는 duration입니다.

duration 6개의 시간 단위를 지원합니다.

 

std::chrono::nanoseconds  // 나노 세컨드. 10억분의 1

std::chrono::microseconds // 마이크로 세컨드. 100만분의 1

std::chrono::milliseconds   // 밀리 세컨드. 1000분의 1

std::chrono::seconds        // 

std::chrono::minutes         // 

std::chrono::hours            // 

 

<예제.1>을 수정하여 위에 열거한 chrono의 다양한 단위를 사용하여 시간 측정을 해 보겠습니다.

 

예제. 2 >

#include <chrono>

#include <iostream>

#include <cmath>

 

 

void Test()

{

           for ( long i = 0; i < 10000000; ++i )

           {

                     std::sqrt( 123.456L );

           }

}

 

int main()

{

    std::chrono::system_clock::time_point StartTime = std::chrono::system_clock::now();

          

    Test();

 

    std::chrono::system_clock::time_point EndTime = std::chrono::system_clock::now();

 

 

 

    std::chrono::duration<double> DefaultSec = EndTime - StartTime;

 

    std::chrono::nanoseconds nano = EndTime - StartTime;

 

    std::chrono::microseconds micro = std::chrono::duration_cast<std::chrono::microseconds>(EndTime - StartTime);

 

    std::chrono::milliseconds mill  = std::chrono::duration_cast<std::chrono::milliseconds>(EndTime - StartTime);

 

    std::chrono::seconds sec = std::chrono::duration_cast<std::chrono::seconds>(EndTime - StartTime);

 

    std::chrono::minutes min = std::chrono::duration_cast<std::chrono::minutes>(EndTime - StartTime);

 

    std::chrono::hours hour = std::chrono::duration_cast<std::chrono::hours>(EndTime - StartTime);

  

    std::cout << "Test() 함수를 수행하는 걸린 시간 : " << DefaultSec.count() << " default" << std::endl;

    std::cout << "Test() 함수를 수행하는 걸린 시간 : " << nano.count() << " nanoseconds" << std::endl;

    std::cout << "Test() 함수를 수행하는 걸린 시간 : " << micro.count() << " microseconds" << std::endl;

    std::cout << "Test() 함수를 수행하는 걸린 시간 : " << mill.count() << " milliseconds" << std::endl;

    std::cout << "Test() 함수를 수행하는 걸린 시간 : " << sec.count() << " seconds" << std::endl;

    std::cout << "Test() 함수를 수행하는 걸린 시간 : " << min.count() << " minutes" << std::endl;

    std::cout << "Test() 함수를 수행하는 걸린 시간 : " << hour.count() << " hour" << std::endl;

          

    return 0;

}

 

실행 결과 >









http://sweeper.egloos.com/2996847






1. 지금까지의 시간 계측 방법들

프로젝트를 진행하다 보면, 이래저래 특정 이벤트가 수행된 시간을 계측해야 되는 경우가 꽤 된다.

이럴 때 아주 높은 정밀도가 필요치 않다면, mill-second까지 뽑아줄 수 있는 아래 함수들을 사용한다.
  • GetTickCount
  • timeGetTime : 멀티미디어 라이브러리를 이용 (Windows.h와 winmm.lib 필요)
GetTickCount의 경우 시스템 타이머 정밀도의 영향을 받아, 최소 정밀도가 10~16ms 정도 된다.

이보다 조금 더 정밀도가 높은 timeGetTime의 경우, 멀티미디어 타이머에 영향을 받으며,
보통 5ms 이상이라고 한다. (머신에 따라 다르다고 한다)

timeGetTime의 경우, 정확한 정밀도를 갖게 하려면 다음과 같이 타이머 정밀도를 끌어 올릴 수 있다.

  1. #include <Windows.h>
  2. #pragma comment(lib, "winmm.lib")
  3.  
  4.  
  5. timeBeginPeriod(1);
  6.  
  7. // ...timeGetTime 사용
  8.  
  9. // timeEndPeriod의 인자값은 timeBeginPeriod에서 설정한 인자값과 동일해야 한다
  10. timeEndPeriod(1);

Milli-second보다 더 높은 정밀도의 계측 도구가 필요한 경우 유일한 대안은 
QueryPerformanceQuery / QueryPerformanceCounter 였다.

하지만, 이는 OS가 제공하는 기능이었지, C++의 표준 메써드는 아니었다.

이와 관련된 자세한 내용은 예전에 정리해 둔 문서로 대체한다.

즉, 지금껏 C++ 표준의 고정밀 시간 계측 도구가 없었던 셈이다.


2. boost::chrono의 등장

boost 1.47.0(2011년 7월에 발표)에 Vicente J. Botet Escribá 라는 사람에 의해 처음으로 boost::chrono가 도입된다.
(이 히스토리는 http://www.boost.org/doc/libs/ 에서 확인 가능하다)

자세한 내용은 boost::chrono 페이지에서 확인이 가능하다.

참고로, 문서의 내용은 boost의 것이 MSDN 것보다 훨씬 자세하므로, 
개별 클래스나 구조체에 대한 내용은 boost의 것을 보는 게 더 나을 수도 있다.


3. C++11 표준의 chrono

C++11에서 드디어 chrono가 표준으로 채택되었고, VS2012에도 포함이 되어 있다.
(boost::chrono의 주요 특징을 거의 채택하였으나, VS2012의 chrono는 clock class가 boost::chrono와 조금 다르다)

VS2012의 chrono에 대한 설명은 MSDN 링크 페이지에서 확인할 수 있다.

chrono는 요약해 정리하면,
기존 C/C++ 런타임의 time 함수에 비해 많은 기능을 가지고 있으며, 훨씬 더 정밀하고 훨씬 더 편하게 사용할 수 있다.

정밀도는 nano-second까지 지원하며, 시간 단위의 값끼리 연산도 가능하다.

또한, 기존의 C/C++ 런타임의 time 체계와도 어느 정도 호환이 가능하다.


4. 기본 사용법

chrono를 사용하기 위해선 <chrono> 헤더 파일이 필요하며, 아래와 같은 namespace에 속한다.

  1. #include <chrono>
  2.  
  3. /-
  4. namespace std
  5. {
  6.     namespace chrono
  7.     {
  8.     }
  9. }
  10. *-
  11.  
  12. // 즉, 주로 사용시엔 아래와 같이 using namespace를 쓰는 게 편하다.
  13. using namespace std;
  14. using namespace chrono;

우선, 간단한 예제를 통해 가장 기초적인 사용법을 익혀보자.

  1. #include <chrono>
  2.  
  3. using namespace std;
  4. using namespace chrono;
  5.  
  6. // Debug 모드로 재 보니, i5-750에서 9.87초나 걸린다
  7. void BigLoop()
  8. {
  9.     for (int i = 1; i <= 50000; i++)
  10.     {
  11.         for (int j = 1; j <= 50000; j++)
  12.         {
  13.             int x = (+ j) * 2 + (/ j);
  14.         }
  15.     }
  16. }
  17.  
  18. int main()
  19. {
  20.     // now() 함수를 통해 현재 시간값을 구한다.
  21.     system_clock::time_point start = system_clock::now();
  22.  
  23.     BigLoop();
  24.  
  25.     system_clock::time_point end = system_clock::now();
  26.  
  27.     // 초 단위 (소수점으로 표현)
  28.     duration<double> sec = end - start;
  29. }

chrono에서 시간을 구하는 함수는 now() 함수이며, 원형은 아래와 같이 작성되어 있다.

  1. static time_point now() _NOEXCEPT
  2. {
  3.     // get current time
  4.     return (time_point(duration(_Xtime_get_ticks())));
  5. }

정적 함수로 정의되어 있으며, time_point 객체를 반환한다.

time_point 클래스는 시간을 나타내는 하나의 포인트를 의미하며,
now() 함수를 통해 찍어놓는 하나의 타임 마크라고 생각하면 된다.

참고로, time_point 클래스는 아래와 같이 정의되어 있다.

  1. template<class _Clock, class _Duration = typename _Clock::duration>
  2. class time_point
  3. {       // represents a point in time
  4. public:
  5.     typedef _Clock clock;
  6.     typedef _Duration duration;
  7.     typedef typename _Duration::rep rep;
  8.     typedef typename _Duration::period period;
  9.  
  10.     time_point() : _MyDur(_Duration::zero())
  11.     {   // check asserts
  12.         static_assert(_Is_duration<_Duration>::value,
  13.                         "duration must be an instance of std::duration");
  14.     }
  15.  
  16.     explicit time_point(const _Duration& _Other) : _MyDur(_Other)
  17.     {   // construct from a duration
  18.     }
  19.  
  20.     template<class _Duration2>
  21.     time_point(const time_point<_Clock, _Duration2>& _Tp,
  22.                typename enable_if<is_convertible<_Duration2, _Duration>::valuevoid>::type ** = 0)
  23.         : _MyDur(_Tp.time_since_epoch())
  24.     {   // construct from another duration
  25.     }
  26.  
  27.     _Duration time_since_epoch() const
  28.     {   // get duration from epoch
  29.         return (_MyDur);
  30.     }
  31.  
  32.     time_point& operator+=(const _Duration& _Dur)
  33.     {   // increment by duration
  34.         _MyDur += _Dur;
  35.         return (*this);
  36.     }
  37.  
  38.     time_point& operator-=(const _Duration& _Dur)
  39.     {   // decrement by duration
  40.         _MyDur -= _Dur;
  41.         return (*this);
  42.     }
  43.  
  44.     static time_point (min)()
  45.     {   // get minimum time point
  46.         return (time_point((_Duration::min)()));
  47.     }
  48.        
  49.     static time_point (max)()
  50.     {   // get maximum time point
  51.         return (time_point((_Duration::max)()));
  52.     }
  53.  
  54. private:
  55.     _Duration _MyDur;   // duration since the epoch
  56. };

템플릿 인자로 Duration을 가지는데, Duration은 chrono에서 경과 시간을 나타내는 클래스로써 역할을 한다.

Duration에 대해선 아래에 자세히 설명하곘다.


5. chrono의 시간 단위

chrono는 duration 클래스의 typedef 형태로 6개의 기본 시간 단위를 제공된다. 

이는 유저가 필요시 또 다른 typedef를 사용할 수 있음을 의미한다.

  1. typedef duration<int, ratio<3600> > hours;
  2. typedef duration<int, ratio<60> > minutes;
  3.  
  4. typedef duration<long long> seconds;
  5.  
  6. typedef duration<long long, milli> milliseconds;
  7. typedef duration<long long, micro> microseconds;
  8. typedef duration<long long, nano> nanoseconds;

이를 통해 위 예제를 다양한 시간 단위로 표현할 수 있다.

  1. system_clock::time_point start = system_clock::now();
  2.  
  3. BigLoop();
  4.  
  5. system_clock::time_point end = system_clock::now();
  6.  
  7. // 시간 단위
  8. hours hour = duration_cast<hours>(end - start);
  9. // 분 단위
  10. minutes min = duration_cast<minutes>(end - start);
  11. // 초 단위
  12. duration<double> sec = end - start;
  13. // 밀리 초 단위 (1/1000)
  14. milliseconds milliSec = duration_cast<milliseconds>(end - start);
  15. // 마이크로 초 단위 (1/1000000)
  16. microseconds microSec = duration_cast<microseconds>(end - start);
  17. // 나노 초 단위 (1/1000000000)
  18. nanoseconds nanoSec = end - start;

나노 초 단위가 아닌 시간 단위로 표현할 때엔, 
위와 같이 duration_cast를 사용하여 명시적 캐스팅을 해 주어야 한다.

이는 now() 함수가 반환하는 기본 단위가 정밀도가 가장 높은 nano seconds 단위임을 의미한다.

또한, 위에서 6개의 시간 단위는 duration 클래스의 typedef라고 얘기하였다.
이는 milli second로 표현할 때에도 소수점 표현이 가능하다는 이야기이다.

즉, 아래와 같이 자신만의 typedef를 통해 소수점으로 표현이 가능한 milli-second 단위를 사용할 수 있다.

  1. // 밀리 초 단위를 double로 표현하고 싶다.
  2. typedef duration<double, milli> doublemillisec;
  3.  
  4. doublemillisec doubleMilliSec = duration_cast<doublemillisec>(end - start);

이쯤 되면, duration 클래스가 어찌 생겨먹었는지 궁금해 진다.
아래 코드를 살펴보자.

  1. // _Rep : 표현하고자 하는 단위의 타입 (즉, long, double)
  2. // _Period : 시간 단위를 얼마의 비율로 보여 줄것인가? (1/1000인가? milli, 1/1000000인가? micro)
  3. template<class _Rep, class _Period>
  4. class duration
  5. {    // represents a time duration
  6.  
  7.     // ...
  8.  
  9.     // 기본 사칙 연산과 % 연산자를 지원한다.
  10. };

위에서 보듯이 duration 클래스의 인자를 통해 초(second) 대비 시간의 비율 값을 특정 타입으로 표현함을 지정할 수 있다.
따라서, 기본 6개의 단위 외 얼마든지 원하는 형태로 시간 단위를 정의할 수 있다.

참고로, chrono의 milli, nano 등은 ratio 클래스를 typedef 한 것이다.
ratio 클래스까지 모조리 설명하면, 너무 멀리 나가게 되므로, 아래 typedef만 살펴 보자.

  1. typedef ratio<1(_LONGLONG)1000000000 * 1000000000> atto;
  2. typedef ratio<1(_LONGLONG)1000000 * 1000000000> femto;
  3. typedef ratio<1(_LONGLONG)1000 * 1000000000> pico;
  4. typedef ratio<11000000000> nano;
  5. typedef ratio<11000000> micro;
  6. typedef ratio<11000> milli;
  7. typedef ratio<1100> centi;
  8. typedef ratio<110> deci;
  9. typedef ratio<101> deca;
  10. typedef ratio<1001> hecto;
  11. typedef ratio<10001> kilo;
  12. typedef ratio<10000001> mega;
  13. typedef ratio<10000000001> giga;
  14. typedef ratio<(_LONGLONG)1000 * 10000000001> tera;
  15. typedef ratio<(_LONGLONG)1000000 * 10000000001> peta;
  16. typedef ratio<(_LONGLONG)1000000000 * 10000000001> exa;


6. 다양한 clock 클래스

boost::chrono와 VS2012 chrono가 clock 클래스에서 조금 다른 부분이 있다고 하였다.

우선, boost::chrono의 clock 클래스로는 아래 것들이 있다.
  • system_clock
  • steady_clock
  • high_resolution_clock
  • process_real_cpu_clock
  • process_user_cpu_clock
  • process_system_cpu_clock
  • process_cpu_clock
  • thread_clock
헌데, VS2012 chrono에는 딱 세 가지 clock 클래스만 포함되어 있다.
  • system_clock
  • steady_clock
  • high_resolution_clock
지금부터는 VS2012의 chrono를 기준으로 각 클래스의 성격에 대해 설명하겠다.

1. system_clock

가장 일반적인(특수성이 없는) clock 클래스라고 볼 수 있다.

위에서 chrono를 설명할 때, time 함수들과 호환성이 있다고 하였는데, 
아래 두 가지 함수를 통해 호환 기능을 제공하고 있다.

  1. // time_point -> time_t
  2. system_clock::time_point currTimePoint = system_clock::now();
  3. time_t currTime = system_clock::to_time_t(currTimePoint);
  4.  
  5. // time_t -> time_point
  6. time_t currTime = time(nullptr);
  7. system_clock::time_point currTimePoint = system_clock::from_time_t(currTime);

2. steady_clock

steady_clock은 system_clock을 상속받으며, 딱 2개의 정적 변수만을 추가로 가지고 있다.

또한, steady_clock은 monotonic_clock이라는 별칭을 가지고 있다.
(monotonic은 "증가 또는 감소만 하는" 이라는 사전적 의미를 가지고 있다)

  1. class steady_clock : public system_clock
  2. {    // wraps monotonic clock
  3. public:
  4.     static const bool is_monotonic = true;      // retained
  5.     static const bool is_steady = true;
  6. };
  7.  
  8. typedef steady_clock monotonic_clock;   // retained

원래 steady(monotonic)_clock은 사전적 의미처럼, 절대로 역행하지 않는 개념의 clock 클래스이다.

steady_clock 이외의 clock 클래스는 time_point를 얻은 후 
운영체제의 시간을 뒤로 돌린 후 time_point을 얻으면 시간이 과거의 값으로 구해진다.

하지만, steady_clock에서는 이것이 통하지 않는다.
따라서, 시간의 값이 시스템 설정과 무관하게 한결같이 흘러가야 할 때엔 steady_clock을 사용하는 것이 좋다.

문제는 현재 VS2012의 chrono 구현에서는 is_monotonic이든, is_steady이든 변수를 사용하는 곳이 없다.
(주석의 retained 즉, 보류되었음을 통해서도 알 수 있다)

혹시나 하고 테스트를 해 보았지만, 아쉽게도 steady_clock을 쓰고 날짜를 바꾼 뒤 다시 재 보았지만, 시간이 역행했다.
즉, 아직 VS2012에는 steady_clock 기능이 제대로 구현되어 있지 않은 것이다.

3. high_resolution_clock

그리고, high_resolution_clock은 system_clock의 typedef일 뿐이다.

typedef system_clock high_resolution_clock;


총 정리해보면, 아직 VS2012의 실질적 clock 클래스는 system_clock 하나인 셈이다.


7. chrono vs QueryPerformanceCounter 성능

음... 뭐만 나왔다 하면, 쓰윽 써보고 성능을 재보는 게 몸에 베서리...
이번에도 chrono의 now() 함수와 QueryPerformanceCounter() 함수의 성능을 비교해 보았다.

실험에 사용된 머신의 개략적 스펙은 다음과 같다.
  • CPU : i5-750 no overclock
  • RAM : 4GB
테스트 코드

  1. #include <Windows.h>
  2. #include <iostream>
  3. #include <chrono>
  4.  
  5. using namespace std;
  6. using namespace chrono;
  7.  
  8. void ChronoLoop()
  9. {
  10.     system_clock::time_point start;
  11.  
  12.     for (int i = 1; i <= 10000; i++)
  13.     {
  14.         for (int j = 1; j <= 10000; j++)
  15.         {
  16.             start = system_clock::now();
  17.         }
  18.     }
  19. }
  20.  
  21. void QueryLoop()
  22. {
  23.     LARGE_INTEGER li;
  24.     for (int i = 1; i <= 10000; i++)
  25.     {
  26.         for (int j = 1; j <= 10000; j++)
  27.         {
  28.             QueryPerformanceCounter(&li);
  29.         }
  30.     }
  31. }
  32.  
  33. typedef duration<double, milli> doublemillisec;
  34.  
  35. int main()
  36. {
  37.     system_clock::time_point start = system_clock::now();
  38.     ChronoLoop();
  39.     system_clock::time_point end = system_clock::now();
  40.  
  41.     doublemillisec doubleMilliSec = duration_cast<doublemillisec>(end - start);
  42.     cout << "ChronoLoop: " << doubleMilliSec.count() << endl;
  43.  
  44.     start = system_clock::now();
  45.     QueryLoop();
  46.     end = system_clock::now();
  47.  
  48.     doubleMilliSec = duration_cast<doublemillisec>(end - start);
  49.     cout << "QueryLoop: " << doubleMilliSec.count() << endl;
  50.  
  51.     return 0;
  52. }

테스트 결과

<Debug>
ChronoLoop: 10046.4 ms
QueryLoop: 1544.4 ms

<Release>
ChronoLoop: 951.6 ms
QueryLoop: 1357.2 ms

디버그에서는 심하게 느리지만, 릴리즈 모드에서는 Chrono가 약 30% 정도 빠름을 알 수 있다.

출처:[C++11] chrono


반응형

'메타프로그래밍 > Boost::' 카테고리의 다른 글

boost::unordered_map  (0) 2012.11.13
boost::filesystem (파일, 디렉터리 다루기)  (0) 2012.11.04
Chapter 6. Boost.Container - 번역중  (0) 2012.10.27
Chapter 2. Boost.Any  (0) 2012.10.27
Chapter 1. Boost.Accumulators  (0) 2012.10.27

+ Recent posts