[테크월드=정환용 기자] 소프트웨어 코드의 수행 속도는 소프트웨어 성능 향상을 측정하는 지표 중 하나다. 예를 들어 동일한 하드웨어에서 동작하는 기존의 특정 알고리즘 코드와 새로 만들어진 알고리즘 코드가 동일한 기능을 하지만 코드 수행속도가 빨라졌다 가정하자. 새로 만들어진 알고리즘 코드는 이전의 알고리즘보다 성능이 좋다고 평가할 수 있다. 코드 수행 속도의 평가를 위해 일반적으로 가장 많이 사용하는 방법은 GPIO핀을 출력으로 설정해 코드 시작 전후를 다르게 제어해 오실로스코프로 GPIO핀 출력 변화 시간을 측정하는 방법이다. 이 같은 방법은 MCU의 하드웨어를 추가로 사용하며 실행 코드 또한 수정해 측정해야하기에 측정 정확도와 효율성이 낮다.
로그 브레이크포인트 활용하기
만일 정밀도가 그리 높지 않은 코드 수행시간 측정을 원한다면 로그 브레이크포인트(Log Breakpoint)를 사용 할 수 있다. 로그 브레이크포인트는 해당 브레이크 위치의 코드가 수행되면 멈추지 않고 Debug log 창에 시간 정보와 함께 기록만 남긴다.
로그 브레이크포인트를 활용하면 소스코드의 수정 없이 시간을 측정할 수 있지만, 기록되는 시간이 초 단위로 기록되기에 아주 정밀한 수행 시간 측정이 될 수 없다. 로그 브레이크포인트로 실행 시간 측정 방법은 [그림 1]과 같다. 실행 시간을 측정하기 원하는 코드의 위치에서 마우스 오른쪽 버튼을 클릭하고 나타나는 컨텍스트 메뉴 중 Toggle Breakpoint (Log) 를 선택한다.
[그림 3]의 예제 코드는 1초마다 이벤트를 발생하는 타이머의 인터럽트 핸들러 코드다. 1초마다 로그 브레이크포인트 위치의 코드를 수행하며 Debug Log 창에 시간 정보와 함께 기록을 남긴다.
로그 브레이크포인트를 활용하는 방법은 정확한 실행 시간 측정은 불가능하지만 가장 쉽게 사용할 수 있는 방법이다. 또한, 실행 시간 측정을 원하는 실행 코드 시작 위치와 끝 위치의 다음 코드 위치에 로그 브레이크포인트를 설정한 후 실행해 Debug Log 창에 남겨진 두 위치의 기록을 비교해 대략적인 실행 시간을 측정 할 수 있다.
MCU의 CYCLECOUNTER 레지스터 활용하기
최근 출시된 MCU는 CPU Clock Cycle이 발생 카운트를 기록하는 레지스터를 가지고 있다. ARM의 코어텍스-M3/4/7 디바이스의 경우는 CYCLECOUNTER라는 레지스터가 있다. CPU의 Clock이 발생할 때 마다 CYCLECOUNTER 레지스터에 Clock 발생 수를 기록한다. 코드 수행 시간 측정을 위한 코드의 시작 부분이 수행될 때의 CYCLECOUNTER 수와 코드가 클 때의 CYCLECOUNTER의 차이를 비교하면 정확한 코드 수행 시간을 알 수 있다.
예를 들어 100MHz로 동작하는 CPU에서 수행 시간 측정을 위한 코드의 시작과 끝에서 측정한 CYCLECOUNTER의 차이가 100,000,000 이였다면 코드 수행 시간은 정확히 1초다.
또한, CCSTEP 레지스터를 활용하면 CYCLECOUNTER의 차이를 쉽게 계산할 수 있다.
CYCLECOUNTER레지스터 확인
[그림 4]처럼 CYCLECOUNTER 레지스터는 C-SPY 디버거 모드 진입 후 View Register를 선택하면 CPU의 모든 Register를 확인 할 수 있는 Register 창이 활성화 된다.
[그림 5]처럼 CYCLECOUNTER Register는 CPU 동작 시작부터 소요된 Clock cycle을 카운트한다. 또한, CCSTEP Register는 마지막 표시되었던 CYCLECOUNTER Register 값과 현재 값의 차이를 표시한다.
확인을 위해 Step Over를 실행하면 코드가 수행되고 CYCLECOUNTER 값이 증가하는 것을 확인할 수 있다. 또한, 이전 CYCLECOUNTER 값과 수행 후의 값의 차이를 CCSTEP 값으로 알 수 있다.
CYCLECOUNTER Register는 ARM 코어텍스-M3/4 MCU에서는 기본 제공되고 있다. 하지만IAR Embedded Workbench for ARM에서는 기본값으로 비활성화 됐다. 만일 I-jet 디버거 장비를 사용하면 자동 활성화가 되지만 다른 디버거 장비를 사용할 경우 활성화 작업이 필요하다. 이러한 경우 [그림 8]를 참조해 DWT Control register (DWT_CTRL.CYCCNTENA) 의 CYCCNTENA 비트를 1로 설정한다.
CYCLECOUNTER Register를 사용하는 방법은 코드가 실행되는 동안의 CPU clock 수를 이용하는 방법으로 실행 시간을 가장 정확하게 측정할 수 있다. CYCLECOUNTER Register의 차이 값을 시간으로 환산하는 수식을 매크로함수로 작성해 활용하면 좀 더 편리하게 시간을 측정할 수 있다.
Data Log Breakpoint 활용하기
Data Log Breakpoint는 ARM 코어텍스-M3/M4의 디바이스의 강력한 디버깅 기능 중 하나다. I-jet과 같은 디버깅 프로브를 사용하는 경우 SWD/SWO의 연결을 지원하며 해당 연결을 이용해 데이터 접근의 기록을 시간 기록과 함께 남기게 된다. 디버깅 모드 진입 전 [그림 9, 10]과 같이 SWD/SWO 연결을 설정 한다.
[그림 11]은 Data Log Breakpoint를 활용하기 위한 예제코드다.
[그림 11]의 예제코드는 main 함수의 while 루프 안에서 ‘data_main’ 변수의 값은 Delay 함수 수행 이후 계속 증가된다. 그리고 SysTick 타이머의 SysTick_Handler에 의해 ‘data_systick’ 변수도 SysTick이 발생할 때마다 값이 10씩 증가한다. 이런 경우 Data Log Breakpoint를 사용해 특정 변수에 접근하면 시간 정보와 함께 기록을 남길 수 있다.
Data Log Breakpoint 사용
[그림 12]와 같이 ‘data_main’ 변수에서 마우스 오른쪽 버튼을 클릭하면 ‘Data Log Breakpoint for ‘data_main’ 메뉴를 확인 할 수 있고, 해당항목을 선택해 Data Log Breakpoint를 설정한다.
Data Log 창은 [그림 17]과 같이 디버거 장비 메뉴아래에 Data Log를 선택해 Data Log 창을 열 수 있다. Data Log 창은 기본 설정 값으로 비활성화 돼있다.
[그림 20]에서 ‘data_main’ 변수에 12를 쓰기(W:12) 했을 때와 13를 쓰기(W:13) 했을 때의 Cycle 수 차이는 866,768 으로 확인 할 수 있다. 따라서 “Delay(100000)” 함수를 실행하는데 소비되는 Cycle수는 866,768이며 MCU의 동작 Clock에 따라 시간으로 환산해 계산할 수 있다.
일반적으로 Data Log Breakpoint는 특정 메모리(특정 변수)의 접근에 따른 기록을 확인하기 위해 사용되나 위의 방법과 같이 특정 코드의 수행 시간을 확인 할 수 있도록 활용할 수 있따. 이 예제와 같은 방법으로 시간 측정을 하기위해 변수는 Static 또는 전역 변수를 사용해야 한다. ARM 코어텍스-M3/M4 디바이스를 사용하는 경우 최대 4개까지의 Data Log Breakpoint 를 지원한다.
MCU 특화된 기능과 일반적인 기능 활용해 다양한 방법으로 구현된 실행 코드의 수행 시간을 측정할 수 있으며 측정된 코드 수행 시간은 코드의 성능 평가 항목으로도 사용할 수 있다.
작성: 이현도 IAR Systems Korea 기술지원팀 과장