22 타이머(Timer)를 이용한 프로파일러(Profiler) 만들기

원문 : http://kkamagui.springnote.com/pages/503123

 

들어가기 전에...

 

0.시작하면서...

 프로그램을 만들다 보면 이상하게 느린 경우가 있다. 대부분이 알고리즘을 잘못 선택했거나, 아니면 쓸데없는 루프를 돈다거나, 극히 드문 경우지만 버퍼 오버플로우가 나서 다른 함수를 실컷 수행하다가 운좋게 다시 돌아오는 경우도 있다(실제로 겪어봤다... ㅡ_ㅡ;;;)

 이런 경우 GetTickCount()나 혹은 다른 카운팅 함수를 이용해서 실행 시간을 측정해서 원인을 분석하는게 일반적이다. 시간을 측정하기 위해서는 어느정도 만족할만한 시간 분해능을 가진 타이머가 필요한데, 마침 NDS에서는 4개의 Timer를 가지고 있으므로 이것을 이용하여 프로파일러를 만들어보자.

 

1.타이머(Timer) 설정

 타이머는 3번 타이머를 사용한다고 했다. 그럼 이제 남은건 분해능 설정인데, 1/1000초 정도면 괜찮을 것 같다. 아래는 Timer를 1/1000초로 설정한는 소스이다.

  1. /**
     프로파일러 초기화
      1/1000 마다 한번씩 튀게 만든다.
    */
    void CProfiler::Initialize( void )
    {
        TIMER3_DATA = TIMER_FREQ_256( 1000 ); 
        TIMER3_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_DIV_256;
     
        irqSet( IRQ_TIMER3, isrProfilerTimer );
        irqEnable( IRQ_TIMER3 );

 

2.최대 프로파일러(Profiler) 개수

 여러군데 값을 저장하여 테스트 할 수 있으므로 어느정도의 개수를 가지도록 해야 할텐데... 일단 지금은 10개로 해놨다.

 

3.구현

 프로파일러를 구현하는 방법은 의외로 간단하다. 프로파일링을 시작하는 부분에서 현재 Timer의 값을 저장하고, 후에 결과를 출력할 부분에서 현재 값과 저장한 값의 차이를 리턴하여 걸린 시간을 리턴하면 된다.  시간을 계속 갱신하여 인터벌을 계산하고 싶으면 업데이트를 수행하면 된다(윈도우에서 간단하게나마 GetTickCount()를 이용해서 테스트 해본 사람은 금방 알 것이다.)

 

아래는 Profiler.h의 내용이다.

  1. #ifndef __PROFILER_H__
    #define __PROFILER_H__

  2. #define MAX_PROFILERCOUNT 10
    #define DWORD unsigned int
  3. class CProfiler
    {
    protected:
     static DWORD ms_vdwTime[ MAX_PROFILERCOUNT ];
     
    public:
     CProfiler();
     ~CProfiler();
  4.  static void Initialize( void );
     static void Finalize( void );
     
     static void Update( int iIndex );
     static DWORD GetInterval( int iIndex );
     static bool IsValidIndex( int iIndex );
    };
  5. #endif /*PROFILER_H_*/

 

 아래는 Profiler.cpp의 내용이다.

  1. #include "Profiler.h"
    #include <nds.h>
  2. // Static 변수
    DWORD CProfiler::ms_vdwTime[ MAX_PROFILERCOUNT ];
  3. static DWORD gs_dwTimerTick = 0;
  4. /**
     Profiler Timer의 핸들러 루틴
    */
    void isrProfilerTimer( void )
    {
     gs_dwTimerTick++;
    }

  5. /**
     Constructor
    */
    CProfiler::CProfiler()
    {
  6. }
  7. /**
     Destructor
    */
    CProfiler::~CProfiler()
    {
  8. }
  9. /**
     프로파일러 초기화
      1/1000 마다 한번씩 튀게 만든다.
    */
    void CProfiler::Initialize( void )
    {
     TIMER3_DATA = TIMER_FREQ_256( 1000 );
     TIMER3_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_DIV_256;
     
        irqSet( IRQ_TIMER3, isrProfilerTimer );
        irqEnable( IRQ_TIMER3 );
    }
  10. /**
     프로파일러 종료
    */
    void CProfiler::Finalize( void )
    {
     TIMER3_CR &= ~TIMER_ENABLE;
     
        irqSet( IRQ_TIMER3, 0 );
        irqDisable( IRQ_TIMER3 );
    }

  11. /**
     현재 Timer 값을 Update 한다.
    */
    void CProfiler::Update( int iIndex )
    {
     if( IsValidIndex( iIndex ) == false )
     {
      return ;
     }
     ms_vdwTime[ iIndex ] = gs_dwTimerTick;
    }
  12. /**
      걸린 시간을 얻는다.
    */
    DWORD CProfiler::GetInterval( int iIndex )
    {
     if( IsValidIndex( iIndex ) == false )
     {
      return 0xFFFFFFFF;
     }
     return ( gs_dwTimerTick - ms_vdwTime[ iIndex ] );
    }
  13. /**
     인덱스가 유효한가 리턴한다.
    */
    bool CProfiler::IsValidIndex( int iIndex )
    {
     if( ( iIndex < 0 ) || ( iIndex >= MAX_PROFILERCOUNT ) )
     {
      return false;
     }
     return true;

 

4.사용법

 프로파일러는 Static 함수들을 가지고 있으므로 굳이 객체를 만들 필요 없다. 아래는 사용방법을 나타낸 것이다.

  1. // 초기화 
  2. CProfiler::Initialize();
  3. ...... 생략 ......
  4. // 프로파일링 시작 시점
  5. // 0번 프로파일러의 값을 갱신한다. 
  6. CProfiler::Update( 0 );
  7. DoSomething();
  8. printf( "실행하는데 걸린 시간 = %d", CProfiler::GetInterval( 0 ) );
  9. // 다시 0번 프로파일러 값 갱신
  10. CProfiler::Update( 0 );

 아주 간단하다. Update() 및 GetInterval() 함수에 사용된 인덱스는 현재 0 ~ 9번까지 사용할 수 있다.

 

5.마치면서...

 NDS의 타이머를 이용해서 간단한 프로파일러를 만드는 방법에 대해 알아보았다. NDS의 열악한 디버깅 환경으로 인해 속도 측정에 어려운 부분이 있었다. 이제 프로파일러도 생겼으니 열심히 효율을 높여보자. @0@)/~!!!

 

6.첨부

 

 

이 글은 스프링노트에서 작성되었습니다.

+ Recent posts