Optimizing Your Application with System Trace in Shark 4

OSXDEV

Jump to: navigation, 찾기

이미 퍼포먼스를 측정하고 코드를 최적화하는 툴인 Shark를 사용하고 있다면, 당신은 어플리케이션의 퍼포먼스를 최적화하기 위해 한 걸음 더 나아갈 준비가 되어있다고 할 수 있다. 이 글에서는 Shark 4.2에서 제공하는 몇가지 파워풀한 기능들을 소개하려 한다. (Shark가 처음이라면, 아래의 For More Information에 나열되어 있는 소개 도큐멘트를 읽기 바란다.)

Shark는 몇가지 다른 측정 구성을 제공하는데, 각각은 malloc 트레이싱이나 L2 캐시 미스 등 특정 퍼포먼스를 측정하기 위해 맞추어져 있다. 여기서 우리는 Shark 4.2에서 처음 나온 중요한 새 측정 구성인 시스템 트레이스를 다룰 것이다. 시스템 트레이스와 그 아웃풋을 읽는 법을 설명한 후, 시스템 트레이스 구성에 있는 새로운 프로그래밍적인 컨트롤을 다루는 법을 보일 것이다 : 트레이스 중에 이벤트를 기록하는 커스텀 사인 포스트이다. 사인 포스트가 있으면, 쉽게 시스쳄 츠레이스를 프로그램 스테이트와 연관시킬 수 있다.

시스템 트레이스의 목적은 당신의 코드가 Mac OS X와 서로 어떻게 상호작용하는지 그리고 멀티 쓰레드 어플리케이션에서 쓰레드들이 서로간에 어떻게 관계하는지를 측정하고 이해하는 것이다. 이는 비헤비어를 옵저브할 수 있도록 함으로서 Shark의 전-시스템적 시간 프로파일링을 보완한다. 이는 통계적 프로파일링 기법보다 용이하다.

시스템 트레이스를 하면, 어플리케이션의 퍼포먼스에 대해 좀 더 자세한 뷰를 얻을 수 있으므로, 더 빠르고 더 효율적으로 만들 수 있는 추가적인 방법을 얻게 해 준다.

샤크 구하기 시작하기 전에, 최신 버전의 Shark를 가지고 있는지 확인하자.(이 글의 기준으로 4.3.1버전). Shark는 Xcode Developer Tool 패키지의 일부분으로 설치되며, 최신버전은 ADC의 Tools Downloads 페이지에서 업그레이드 가능하다. Developer Tools 패키지는 Mac OS X의 소매용 패키지에는 별도의 CD로 제공된다 - 4.2버전 이후의 버전을 가지고 있는지만 확인하면 된다.

어플리케이션을 처음 실행하면, Shark의 Help 메뉴에 있는 유저 가이드와 시간을 좀 보내도록 한다. 유저 가이드는 Shark가 제공하는 모든 기능들을 담고 있는 자세한 메뉴얼이다.

Shark는 유니버셜 바이너리의 프로파일링을 지원하며, 4.2버전부터는 Shark가 인텔과 PowerPC 프로세서 모두의 ISA 도큐멘트를 포함한다. ISA 도큐멘트는 Shark의 Help메뉴에서 찾아볼 수 있다.

목차

[편집] 시스템 트레이스 사용하기

시스템 트레이스 구성을 통해 당신의 코드가 운영체제와 상호작용하는 방식을 볼 수 있다. 당신의 어플리케이션이 멀티쓰레드라면, 시스템 트레이스 구성은 각각의 쓰레드가 상호작용하는 것도 보여준다.

당신의 코드가 유저 영역과 커널 영역의 경계를 넘나들때, 시스템 트레이스는 그것을 기록한다. 유저 영역에서 커널 영역으로 넘어오는 것은 다음의 세가지 방식이 있다:

  • 시스템 콜
  • 가상 메모리 fault
  • 비동기적 하드웨어 인터럽트

우리가 시스템 트레이스의 예제를 보기전에, 시스템 트레이스 구성의 디폴트 매개변수들을 살펴보자. 먼저, Shark의 메인 윈도우에 있는 팝업 버튼에서 시스템 트레이스 프로파일을 선택한다. 다음으로, Config 메뉴에서 Show Mini Config Editor를 선택한다( 키보드 단축키는 커맨드-쉬프트-C). 그림 1은 시스템 트레이스의 Configuration Editor를 보여준다.

그림:A_tl_optshark4_DefaultSystemTraceConfig.jpeg

그림 1: 디폴트 시스템 트레이스 구성

시스템 트레이스는 샘플된 프로파일이 아니다. 그보다 차라리 프로파일 중에 작동중인 모든 프로세스에서 발생하는 커널 영역 경계를 넘는 것을 전부 기록하는 정확한 트레이스이다. 시스템에 부하가 걸리면, 트레이스는 굉장히 방대해지므로, 디폴트 설정은 10초 혹은 1,000,000 트레이스 만을 기록하도록 제한한다. 좀 더 장기간의 트레이스가 필요하다면, 설정을 변경하거나, 필요한 매개변수로 새로운 시스템 트레이스를 만들면 된다.

이제 시스템 트레이스 프로파일을 살펴보자: 그림:A_tl_optshark4_SystemTraceAll.jpeg 그림 2:모든 프로세스를 보여주는 시스템 트레이스

그림 2에서 보이는 것처럼, Shark는 세가지 방식으로 시스템 트레이스를 디스플레이 할 수 있다: Summary(써머리) 뷰, Trace(트레이스) 뷰 그리고 Timeline(타임라인) 뷰이다. 써머리 뷰는 분석을 시작하기에 제일 좋은 뷰이다.

써머리 뷰 써머리 뷰에서, 우리의 시스템 트레이스 예제의 위쪽 절반은 실행중인 프로세스들이 어디에 시간을 소비하는디에 대한 총체적인 내역을 보여준다. 이 예제의 경우, 시스템은 10초 분량의 프로파일에서 82.5%가 idel 상태였다. 12%정도의 시간이 유저 영역에서 사용되었고, 1.9%는 시스템 호출을 위해 사용되었다. 이렇게 써머리는 모든 프로세스를 보여주는 것으로 시작한다. 윈도우의 왼쪽 아래에 있는 Process 팝업 버튼을 이용해 특정 프로세스를 선택할 수 있다. 총체적인 써머리 데이타 밑에는, 다른 탭 뷰가 있어서 다양한 이벤트 타입에 대해 세가지 서로 다른 개별 써머리를 보여준다 : Scheduler summary(스케쥴러 써머리), System Call summary(시스템 호출 써머리), 그리고 Virtual Memory Fault summary(가상 메모리 폴트 써머리).

써머리 뷰: 스케쥴러 스케쥴러 써머리 탭은 선택된 프로세스에서 모든 쓰레드에 대한 스케쥴링 비헤비어의 윤곽을 보여준다(위에서 보이는 설정이 모든 프로세스에 대한 스케쥴러 써머리를 보여준다). 삼각형을 클릭하면 프로세스의 모든 쓰레드를 볼 수 있다. 스케쥴러 써머리 뷰는 어플리케이션의 쓰레드들이 효율적으로 동작하는 지를 확인할 수 있게 해준다. 예를 들어, CPU-바운드 코드를 쓰레드 했다면 몇 밀리세컨드 정도의 작업량을 기대할 것이다(마이크로 세컨드가 아닌). 스케쥴러 써머리 뷰가 이런 경우를 보이지 않는다면, 이것은 작업 쓰레드가 자신의 계산을 마치고 대기상태로 들어가는 것을 의미한다. 이것은 동기화의 간접비용이 멀티 쓰레딩의 이점을 넘어서는 경우이다.

스케쥴러 써머리는 4개의 서로 다른 메트릭스에 대한 데이타를 보여준다.

  • Busy Time: 쓰레드가 동작한 전체 시간 ( 유저 영역 시간에 시스템이 idle이 아닌 시간을 더한 것).
  • User Time: 쓰레드가 유저 영역에서 동작한 시간.
  • System Time: 쓰레드가 커널이나 드라이버 코드에서 보낸 시간.
  • Priority: 스케쥴러가 사용한 쓰레드 우선순위.

아웃라인 밑에, 오른쪽 아래에 있는 팝업 버튼을 사용해 4개의 메트릭스중 하나를 선택한다.

네개의 모든 메트릭스에 대해, Total, Avg, Min, Max 컬럼이 세션 중 전체, 평균, 최소, 최대값을 디스플레이 한다. Busy Time 메트릭이 선택되면, 인터벌 컬럼은 쓰레드가 전하기 위해 스케쥴된 시간을 숫자로 나타낸다.

써머리 뷰: 시스템 호출 시스템 호출 써머리 탭은 각각의 시스템 호출에서 사용한 시간을 보여준다. 게다가, 시스템 호출 써머리 뷰는 시스템 호출을 당신의 코드와 연관시키도록 한다. 예를 들어, 시스템 호출 써머리 뷰가 당신의 코드가 시스템 호출을 만들기 위해 사용되고 있다는 것을 보여주면, 그것을 이용해서 어떻게 그리고 언제 당신의 어플리케이션이 그 정보를 요구하는지를 알고 최적화 하는 데 사용할 수 있다. 아마 시스템에 반복적인 호출을 만들어 내는 것 보다 결과값을 캐시하는 것이 낫다는 것을 발견할 수 있을 것이다.

그림 3은 Core Image Fun House라는 프로세스가 선택된 시스템 호출 써머리 뷰를 보여준다. 그림:A_tl_optshark4_SystemCallSummaryFunHouse.jpeg 그림3:Core Image Fun House 프로세스의 시스템 호출 써머리

Shark의 시간 프로파일처럼, 시스템 호출 써머리는 헤비 뷰 또는 트리 뷰 모드로 볼 수 있다. Advanced Setting 드로어를 사용해 시스템 호출 써머리를 커스터마이즈 할 수 있다. Advanced Setting을 열려면, Shark의 뷰메뉴에서 Show Advanced Settings를 선택한다. Weight By 팝업 버튼에서 다음 메트릭스중 하나를 선택하여 뷰를 커스터마이즈 할 수 있다:

  • CPU Time: 시스템 호출이 프로세서를 위해 소비한 총 시간
  • Wait Time: 시스템 호출이 블록된 상태로 리소스나 이벤트를 기다리며 소비한 총 시간.
  • Total Time: 시스템 호출에 사용된 총 시간
  • Count: 총 시스템 호출 횟수

써마리 뷰: 가상 메모리 폴트 시간 프로파일을 이용해서 가상 메모리 폴트를 찾아내는 것은 어려운 일이 될 수 있다. 그러나 시스템 트레이스 VM 폴트 써머리 뷰는 어떤 코드가 가상 메모리 폴트를 일으켰는지를 정확하게 보여줄 수 있다. 예를 들어, VM 폴트 써머리 뷰는 당신의 코드가 루프에서의 얼록과 딜록을 자주 하면서 0으로 채워진 폴트를 지나치게 만들어 낸다는 것을 보여줄 수 있다. 그러면 당신은 루프 내에서 매번 조그만 버퍼를 얼록하고 프리하는 대신 하나의 큰 메모리 버퍼를 만드는 방식으로 코드를 최적화 할 수 있다.

그림 4는 Core Image Fun House 프로세스에 대한 VM 폴트 써머리를 보여준다. 그림:a_tl_optshark4_VMFaultsSummaryFunHouse.jpeg Figure 4:Core Image Fun house에 대한 VM 폴트 써머리

다섯가지 타입의 가상 메모리 폴트가 있다.

  • Page In: 디스크로부터 읽어들이는 상주하지 않는(nonresident) 페이지.
  • Page Out: 디스크에 쓰여진 페이지.
  • Zero Fill: zero fill on demand가 잘못된 것으로 마크된 페이지.
  • Copy on Write: 공유된 페이지가 수정되었다.
  • Page Cache Hit: 메모리 상주, 그러나 언맵드 페이지가 잘못 되었다.

VM 폴트 써머리는 폴트의 유형별로 데이타를 그룹짓는다. 예제에서, Core Image Fun House 프로세스는 3.2ms를 Page Cache Hit 폴트에 사용했다는 것을 알 수 있다. 삼각형을 클릭하면, 이 유형의 폴트를 발생시킨 정확한 시스템 호출을 볼 수 있다. VM 폴트 써머리는 Advanced Settings 드로어를 이용해 커스터마이즈 할 수 있다. 시스템 호출 써머리와 동일한 메트릭스를 선택할 수 있다 (CPU Time, Wait Time, 등). VM 폴트에서는, Advanced Setting 드로어에 Size라는, 폴트에 영향을 받는 바이트의 양을 보여주는 추가 메트릭이 있다.

트레이스 뷰 트레이스 뷰는 세션중에 발생한 모든 이벤트를 보여준다. 이벤트는 쓰레드 인터벌이 될 수도 있고, 시스템 호출 혹은 가상 메모리 폴트가 될 수도 있다. 이벤트는 스케쥴러, 시스템 호출, VM 폴트 탭을 통해 각각 볼 수 있다.

트레이스 뷰: 스케쥴러 쓰레드가 실행한 기간을 쓰레드 실행 인터벌이라 부른다. 스케쥴러 트레이스는 세션중 발생한 모든 쓰레드 인터벌을 보여준다. 특정 프로세스, 쓰레드 그리고 CPU에 초점을 맞추기 위해서 그리드 아래의 팝업 버튼들을 이용해 뷰를 필터할 수 있다. 그림 5는 Core Image Fun House프로세스를 위한 스케쥴러 트레이스 뷰를 보여준다.

그림:a_tl_optshark4_SchedulerTraceFunHouse.jpeg 그림 5: Core Image Fun House 프로세스를 보여주는 스케쥴러 트레이스

스케쥴러 트레이스 뷰는 각각의 쓰레드 인터벌에 할당된 고유의 인덱스를 그 인터벌 동안 사용한 시간 내역과 함께 보여준다. 이 예제에서, 쓰레드 인터벌 인덱스 211 동안, Core Image Fun house 프로세스는 564.2 마이크로 초를 유저 영역에서 보냈으며, 36.6 마잌로 초를 시스템 영역에서 보냈다. Δt 컬럼은 쓰레드가 마지막으로 스케쥴 된 때로부터의 시간 경과를 보여준다. 마지막 Reason 컬러은 쓰레드의 수명이 끝난 이유를 보여준다.

트레이스 뷰: 시스템 호출 시스템 호출 트레이스는 세션에서의 시스템 호출 이벤트를 보여준다, 그림 6은 Core Image Fun house프로세스의 시스템 호출 트레이스의 예를 보여준다.

그림:a_tl_optshark4_SystemCallsTraceFunHouse.jpeg 그림 6:Core Image Fun House 프로세스의 시스템 콜 트레이스 뷰 보기

시스템 호출은 쓰레드 인터벌동안 발생하지만, 한 인터벌에서 시작해서 다른 인터벌에 끝날 수도 있다. 이러한 경우 뷰의 인터벌 컬럼은 각각의 시스템 호출 이벤트에 대해 시작과 끝 쓰레드 실행 인터벌을 보여준다. 쓰레드 실행 인터벌 번호는 다시 스케쥴 트레이스 뷰의 인덱스 컬럼과 대응한다. 인터벌 컬럼에 하나의 숫자만 디스플레이 되면, 시스템 호출은 동일한 인터벌에서 시작되고 끝이 난 것이다. 호출 이벤트가 트레이스 세션이 시작되기 전에 이미 시작되었다면, 쓰레드 실행 인터벌의 시작부분은 "?"로 보여질 것이다. 유사하게, 호출이 세션이 정지된 후 끝난다면, 쓰레드 실행 인터벌의 끝이 "?"가 될 것이다.

표의 오른쪽 아래에 있는 버튼을 클릭하여 시스템 호출을 위한 호출 스텍을 전환할 수 있다.

트레이스 뷰: VM 폴트

VM폴트 트레이스는 세션 동안 발생한 가상 메모리 폴트 이벤트를 보여준다. 그림 7은 Core Image Fun House의 선택된 프로세스에 대한 세션의 VM 폴트 트레이스를 보여준다: 그림:a_tl_optshark4_VMFaultTraceFunHouse.jpeg 그림 7:Core Image Fun House 프로세스의 VM 폴트 트레이스 뷰 보기

시스템 호출을 트레이스해 감에 따라, 각각의 VM폴트 이벤트에는 고유 인덱스가 할당된다. 가상 메모리 폴트는 쓰레드 실행 인터벌 중에 일어나고, 인터벌 컬럼에 표시된다. 다시, 인터벌 컬럼에 있는 값은 스케쥴 트레이스 뷰의 인덱스 컬럼에 대응된다. VM 폴트 테이블은 발생된 폴트의 유형과 그것을 처리하는데 걸린 시간을 보여준다. 표의 오른쪽 아래에 있는 버튼을 클릭하여 모든 폴트 이벤트의 호출 스텍을 전환할 수 있다. 세가지 트레이스 뷰 테이블들(스케쥴러, 시스템 호출, VM 폴트)의 어떤 이벤트든지 더블클릭하면 타임라인 뷰의 이벤트로 즉시 보내진다. 이 부분을 이제 이야기 하려 한다.

타임라인 뷰 타임라인 뷰는 시스템 트레이스 세션중에 발생한 모든 이벤트들을 정확한 위치에 보여준다. 수평축은 시간을 나타내고, 모든 이벤트 - 쓰레드 실행 인터벌, 시스템 호출 혹은 VM폴트 - 들은 언제 그리고 어디에서 발생되었는지를 정확히 보여준다. 타임라인 뷰의 모든 줄들은 쓰레드에 대응한다. 타임라인을 수평으로 스크롤하면 각 쓰레드에 딸린 이벤트들을 볼 수 있다. 다음 그림은 타임라인 뷰의 예를 보여준다. 이전의 그림들과 다르게, 그림 8은 세션중 실행된 모든 프로세스들을 보여준다. 그림:a_tl_optshark4_TimelineTraceAll.jpeg 그림 8:모든 프로세스를 보여주는 타임라인 뷰

타임라인 뷰: 네비게이션 타임라인 테이블위의 줌 슬라이더를 이용하면, 뷰를 확대할 수 있다. 줌 인하려면, 슬라이더를 오른쪽으로 드래그한다. 줌 아웃하려면, 왼쪽으로 드래그 한다. 챠트위에서 클릭-드래그하여 사각형을 만들면 그 안으로 줌 된다. 옵션-클릭하면 다시 줌 아웃된다.

예제 뷰에서 타임라인은 확대되고 왼쪽으로 스크롤 되어 시스템 트레이스 세션의 맨 첫 부분을 보여준다. 쓰레드 0x20D0318에서 시스템의 마흐커널 프로세스를 볼 수 있다. 그 쓰레드는 0x27A7C60 쓰레드의 Shark가 실행가능하도록 먼저 실행되어 있다.

타임라인 뷰: 쓰레드 라인과 이벤트 라벨 각 색깔의 바는 쓰레드 살행 인터벌을 나타낸다. 다른 색깔의 바는 각각 시스템에서의 CPU를 나타낸다. 쓰레드 실행 인터벌을 따라, 시스템 호출과, 가상 메모리 폴트, 인터럽트, 그리고 사인 포스트 이벤트가 발생했다면 그것도 표시된다(사인 포스트 이벤트는 이 글의 뒷 부분에서 다룰 것이다.). 타임라인에서 색깔있는 바를 클릭함으로서 개별 쓰레드 실행 인터벌에 대한 데이타를 얻을 수 있다.

쓰레드 실행 인터벌 중에 발생한 이벤트들은 컬러 바를 따라 특별 아이콘으로 표기된다. 시스템 호출은 색깔있는 전화기 아이콘으로 표기된다. 서로 다른 유형의 가상 메모리 폴트들은 아이콘의 세트로 표기된다. Shark의 파일메뉴의 Show Advanced Setting 아이템을 선택하면 각 아이콘의 의미를 알 수 있다. 타임라인 뷰의 Advanced Setting 드로어는 다양한 이벤트 타입을 필터링 할 수 있게 해준다. 한 이벤트에 대한 특정한 데이타만 보이는 뷰를 열려면, 이벤트의 아이콘을 클릭하면 된다. 아이콘 아래의 검은 라인은 이벤트의 시작과 끝을 가리킨다. 앞에서 썼듯이, 이벤트의 시작과 끝은 다른 쓰레드 실행 인터벌일 수 있다.

그림 9는 타임라인 뷰를 위한 Advanced Setting 드로어와 각 이벤트 타입을 나타내는 아이콘을 보여준다. 그림:a_tl_optshark4_TimelineViewAdvancedSettings.jpeg

그림 9:타임라인 뷰 Advanced Setting 드로어

[편집] 사인 포스트로 시스템 트레이스 인스트루먼팅

CHUD 프레임워크를 이용해 Shark를 프로그래밍적으로 컨트롤 할 수 있다. Xcode 프로젝트에서 커스텀 빌드 설정을 만들어서 프로그램적인 컨트롤을 곧바로 Xcode와 통합할 수도 있다. CHUD 프레임워크는 Shark를 다양한 방법으로 컨트롤 할 수 있게 해주는, 직선적인 API이다. 이번 섹션에서는, Shark의 시스템 트레이스 구성에 대한 프로그래밍적 컨트롤에 집중한다.

타임라인뷰에 대한 앞에서의 논의에서, 사인 포스트라고 하는 커스텀 이벤트를 만들 수 있다는 것을 봤다. 사인 포스트를 이용하면, 시스템 트레이스를 프로그램 상태와 연관시킬 수 있다. 당신의 프로젝트에 어떻게 하면 Shark에 대한 프로그램적 컨트롤을 붙일 수 있는지를 보여주는 예제를 살펴보자. 그 후, 우리가 커스텀 사인 포스트 이벤트를 정의하고 어플리케이션이 Shark로 프로파일 될 때 그것이 어떻게 보이는 지를 보일 것이다.

샤크의 프로그램적 컨트롤을 위한 커스텀 빌드 컨피겨레이션 만들기 사용할 예제 어플리케이션은 Core Imge Fun House이며 /Developer/Examples/Quartz/CoreImage/FunHouse 폴더에서 찾아볼 수 있다. Xcode를 통해 이 프로젝트를 열고, 다음과 같은 방법으로 CHUD 프레임워크를 더한다:

  1. Group and Files 구역에서, 프로젝트 노드를 오른쪽 클릭하고 Add> Existing Frameworks를 선택한다.
  2. /System/Library/PrivateFrameworks폴더를 찾아가서 CHUD.framework를 선택한다.
  3. Group and Files 구역에서, CHUD.frameworks 노드를 Frameworks 폴더로 드래그한다.

그후, Shark를 컨트롤 하기 위한 새로운 빌드 구성을 더한다.

  1. Group and Files 구역에서 프로젝트 노드를 클릭한다.
  2. 인스펙터 윈도우를 연다
  3. 인스펙터 윈도우에서, Configurations 탭을 클릭한다.
  4. Development 빌드 설정을 선택한다.
  5. Duplicate를 클릭한다. 새로운 빌드 구성이 만들어졌다.
  6. 새 구성의 이름을 "Development[Instrumented]"로 바꾼다.

이제 Development 빌드 구성의 복제본인 "Development [Instrumented]"를 만들었다. 인스펙터 윈도우의Configurations 탭은 그림 10과 같아야 한다. 그림:a_tl_optshark_DevelopmentInstrumentedBuildConfiguration.jpeg

그림 10: Development [Instrumented] 빌드 구성

Xcode의 Active Build Configuration 팝업 버튼에서 "Development[Instrumented]" 빌드 구성을 클릭한다.

다음으로, 이 구성이 언제 액티브 일지를 설정할 수 있게 해주는 프리프로세서 매크로를 더한다

  1. Group and Files 구역에서, 프로젝트의 FunHouse 타겟 노드를 선택한다
  2. 인스펙터 윈도우를 연다.
  3. Build 탭을 클릭한다
  4. Configuration 팝업 버튼에서, "Development [Instrumented]" 빌드 구성을 선택한다. 리스트를 Preprocessor Macros 세팅으로 스크롤 한다.
  5. DEVELOPMENT_INSTRUMENTED 프리프로세서 매크로를 더한다

인스펙터 윈도우는 그림 11과 같아야 한다. 그림:a_tl_optshark4_DevelopmentInstrumentedMacro.jpeg

그림 11: Development [Instrument] Preprocessor Macro

커스텀 빌드 구성을 더하는 것은 꼭 필요하지는 않지만, 그렇게 하면 Shark에 대한 프로그래밍적인 컨트롤을 위해 특화된 빌드를 만들 수 있다. 코드에서, 언제 "Development [Instrumented]" 구성이 활성상태인지를 DEVELOPMENT_INSTRUMENTED 매크로를 검사해서 알아낼 수 있다.

Note: Xcode의 Zero Link와 "Development [Instrumented]" 빌드 구성에 있는 Fix and Continue 옵션을 꺼야한다. Zero Link와 Fix and Continue는 퍼포먼스 측정보다는 개발과 디버깅에서 중요한 부분이다.

다음으로, Fun House 어플리케이션을 위해 두개의 커스텀 사인 포스트 이벤트를 만들자. 이것 또한 Xcode 프로젝트에 합쳐질 수 있다.

포인트 사인 포스트 이벤트 정의하기 사인 포스트 이벤트는 심플 텍스트 파일에 정의되어 있다. 이벤트 정의를 넣을 새 파일을 프로젝트레 더한다.

  1. Xcode의 File 메뉴에서 Choose New File을 선택한다
  2. 리스트에서 Select Empty File in Project를 선택한다
  3. Next를 클릭한다
  4. File Name 텍스트 박스에 "FunHouseSharkEvents.text"를 입력한다.
  5. Finish를 클릭한다

사인 포스트 정의 파일은 하나 이상의 이벤트 정의를 가지며, 그들 각각은 헥사 값 뒤에 이벤트 이름이 붙어있다. 우리가 만드는 사인 포스트 이벤트 파일에, 다음 코드 라인을 타이핑 한다:

0x0 FunHouseAwakeFromNib
0x1 FunHouseTerminate

이벤트의 값은 0x0에서 0x3FFF 범위내에 있어야 한다. Shark는 이 범위 밖에 있는 이벤트 정의 코드는 무시한다. 몇 개의 이벤트 정의 파일을 만들 수 있지만, 그 값이 중복되지 않도록 주의해야 한다.

Shark가 사인 포스트 이벤트를 인식할 수 있게 하려면, 사인 포스트 이벤트 정의 파일을 다음 폴더로 복사해야 한다:~/Library/Application Support/Shark/KDebugCodes. 다음과 같이 FunHouse 타겟에 Copy Files 빌드 단계를 추가함으로서 Xcode에서 이것을 할 수 있다: -1. Groups and Files 영역에서, FunHouse 타겟을 선택한다 -2. 타겟에 오른쪽 클릭을 하고 Add> New Build Phase > New Copy Files Build Phase를 선택한다. Copy Files 인스펙터 윈도우가 열릴 것이다. -3. Destination 팝업 버튼에서, Absolute Path를 선택한다. -4. Full Path 텍스트 박스에 $(HOME)/Library/Application Support/Shark/KDebugCodes를 입력한다.

Copy Files 인스펙터는 그림 12와 같아야 한다.

그림:a_tl_optshark4_CopyFilesInspector.jpeg 그림 12:Copy Files 인스펙터 -5. 인스펙터 윈도우를 닫는다 -6. Group and Files 영역에서, FunHouseSharkEvents.text 파일을 클릭하고 이제 막 더해진 Copy Files 빌드 단계로 드래그 한다. 여기에서, Xcode는 사인 포스트 파일을 프로젝트의 일부로 취급한다. 타겟이 빌드되면, Xcode는 빌드 프로세스의 일부로서 적당한 위치에 그 파일을 복사한다.

사인포스트 이벤트에 대한 인스트루먼팅 코드 이제 사인 포스트 이벤트에 대해 좀 더 자세히 살펴보자. Shark는 두 가지 유형의 사인 포스트 이벤트를 레코드 할 수 있다: - 포인트 이벤트: 포인트 이벤트는 한 번 발생하는 이벤트를 마크한다. - 인터벌 이벤트: 인터벌 이벤트는 시작점과 끝점을 가지는 이벤트를 마크한다.

CHUD 프레임워크 함수 chudRecordSignPost() 호출에 의해 모든 사인 포스트 이벤트는 기록된다. 함수의 용법은

int chudRecordSignPost(unsigned code, chud_signpost_t type, unsigned arg1,  unsigned arg2, unsigned arg3, unsigned arg4);

함수의 첫번째 매개변수는 사인 포스트 파일에 정의된 이벤트 코드 값이다. 두번째 매개변수는 사인 포스트의 타입이다. 두가지 타입중 하나이다: 포인트 이벤트에 대해서는 chudPointSignPost, 인터벌 이벤트의 시작점을 마크하기 위해서는 chudBeginIntervalSignPost. 인터벌 이벤트의 끝은 chudEndIntervalSignPost 값을 넘겨서 마크한다. 남은 4개의 매개변수는 당신이 직접 쓰는 것이다. 그 매개변수들에 유용한 값들을 연결시킬 수 있으며, 그것들은 Shark에서 이벤트를 인스펙트 할때 디스플레이 될 것이다.

두 포인트 이벤트를 만들기 위해 앞서 정의된 두개의 이벤트를 사용할 것이다: 첫번째 이벤트는 정확히 awakeFromNib 메소드가 호출될 때 나타날 것이다. 두번째는 정확히 applicationWillTerminate가 호출될 때 호출될 것이다. 두개의 사인 포스트를 두개의 다른 소스파일에 레코드 할 것이다. 먼저, FunHouseApplication.m파일을 열고 CHUD 프레임워크 헤더를 더한다.

#import <CHUD/CHUD.h>

다음으로, 이벤트를 숫자 값이 아닌 이름으로 참조하기 위해 #define프리프로세서 매크로를 더한다.

#define FunHouseAwakeFromNib 0x0

이제 awakeFromNib 메소드의 시작부분에 다음 라인을 더하자.

 - (void)awakeFromNib
 {
 #ifdef DEVELOPMENT_INSTRUMENTED
 chudInitialize();	  
 chudRecordSignPost(FunHouseAwakeFromNib, chudPointSignPost, 0, 0, 0, 0);
#endif
// ...
}

이 코드는 CHUD프레임워크를 초기화하고, FunHouseAwakeFromNib 이벤트에 대해 사인 포스트 코드 0과 함께 chudRecordSignPost()를 호출한다.

FunHouseTerminate 사인 포스트는 FunHouseAppDelegate.m에 레코딩 된다. 파일을 연 후, #import <CHUD/CHUD.h>헤더를 더한다. 다시, 이벤트의 숫자값에 대해 프리프로세서 매크로를 더한다

#define FunHouseTerminate 0x1

마지막으로 applicationWillTerminate메소드의 시작부분에 다음 코드를 추가한다.

- (void)applicationWillTerminate:(NSNotification *)aNotification
{
#ifdef DEVELOPMENT_INSTRUMENTED
 chudRecordSignPost(FunHouseTerminate, chudPointSignPost, 0, 0, 0, 0);
 chudCleanup();
#endif
// ...
}

이 코드는 FunHouseTerminate 이벤트에 해당하는 사인 포스트 코드 0x1을 기록한다. 어플리케이션을 빌드해 본다 - 이제 거의 다 왔다.

사인 포스트 이벤트 보기 Shark를 시작하고, 시스템 트레이스 구성을 선택한다. 그 구성을 조금 고칠 필요가 있다. 디폴트 시스템 트레이스는 10초만 샘플하기 때문이다. Shark의 Config 메뉴에서 Show Mini Config Editor를 선택한다. Shark의 메인 윈도우가 시스템 트레이스 구성을 위한 추가적인 3개의 매개변수를 디스플레이 한다: Start Delay, Time Limit 그리고 Sample Limit. Time Limit를 45초로 늘리고 Sample Limit 체크박스를 끈다. 우리가 트레이스를 그렇게 오래 하지는 않을 꺼지만, Fun House를 시작하고 끌 수 있을 정도의 충분한 시간은 되어야 한다.

Shark의 메인 윈도우에서, Start 버튼을 클릭한다.

이제 Core Image Fun House 어플리케이션을 실행한다. 프로그램이 시작되면 바로 파일 열기 다이얼로그가 뜨고 몇개의 파일이 디스플레이 되어있을 것이다. 파일 하나를 선택하고 Open을 누른다.

우리는 스타트업과 셧다운 이벤트만을 기록하려 하므로, Fun House 어플리케이션을 나간다.

Shark의 메인 윈도우에서, Stop 버튼을 누른다.

시스템 트레이스 윈도우가 열리면, 트레이스 탭을 클릭한다. Sign Posts라는 추가적인 탭이 더해진 것을 볼 수 있다. 그림 13은 그 추가적인 탭을 보여준다: 그림:a_tl_optshark4_FunHouseTraceSignPosts.jpeg 그림 13: Fun House 트레이스의 사인 포스트 탭

Shark는 두개의 사인 포스트 이벤트를 기록했다. 추가적인 4개의 매개변수는 표 아래에 디스플레이 된다. 테이블의 줄을 더블클릭하면 타임라인 뷰의 사인 포스트로 바로 넘어갈 수 있다. 그림 14는 타임라인에서의 FunHouseAwakeFromNib 사인 포스트를 보여준다. 사인 포스트 아이콘을 더블클릭해서 이벤트에 대한 자세한 정보를 디스플레이 할 수 있다. 그림:a_tl_optshark4_FunHouseAwakeFromNibTimeline.jpeg 그림 14: 포인트 사인 포스트 이벤트를 보이는 Fun House 타임라인

인터벌 사인 포스트 만들기 인터벌 사인 포스트는 코드의 섹션에 마크하게 해 주므로, 직접 시스템 트레이스 타임라인을 프로그램 상태와 연관시킬 수 있다. Fun House 어플리케이션은 츠렌지션 효과를 플레이하기 위해 타이머를 사용한다. 다음 예제에서는, 사인 포스트를 이용해 타이머의 시작과 끝에 마크하기 위해 코드를 다루는 법을 보인다. 인터벌 사인 포스트가 각각의 타이머 이벤트의 시작과 끝에 마크하도록 코드를 다룰 것이다,

인터벌 사인 포스트 이벤트 정의하기 첫번째 단계는 프로젝트에 있는 택스트 파일에 새로운 사인 포스트 이벤트 코드를 더하는 것이다. FunHouseSharkEvents.text 파일을 열고 이벤트 2와 3을 더하자:

0x0 FunHouseAwakeFromNib
0x1 FunHouseTerminate
0x2 FunHouseTimer
0x3 FunHouseAutoTimer

FunHouseTimer 사인 포스트는 이펙트 타이머의 시작과 끝에 마크할 것이다. FunHouseAutoTimer 사인 포스트는 autoTimer 메소드에 있는 코드 섹션을 감쌀 것이다. 이는 특정 코드 블럭에서 사용한 시간을 보여줄 것이다. 인터벌 사인 포스트 이벤트를 위한 인스트루먼팅 코드 장면전환 효과의 타이머 코드는 EffectStackController.m 파일에 있다. 그 파일을 열어 프레임워크를 사용하기 위해 필수적인 수정을 하고 사인 포스트를 위한 프리프로세서 매크로를 정의한다:

  1. 다음 코드를 더한다 #import<CHUD/CHUD.h>
  2. 프리프로세서 매크로를 더한다

#define FunHouseTimer       0x2
#define FunHouseAutoTimer   0x3

startTimer메소드를 넣는다. 메소드를 수정해서 타이머 사인 포스트의 시작을 기록하도록 한다.

// start the transition timer
- (void)startTimer
{
   if (!timer)
   {
#ifdef DEVELOPMENT_INSTRUMENTED
 chudRecordSignPost(FunHouseTimer, chudBeginIntervalSignPost, 0, 0, 0, 0);
#endif
       timer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:@selector (autoTimer:) 
       	userInfo:nil repeats:YES];
       [timer retain];
   }
}

stopTimer메소드를 넣는다. 인터벌 사인 포스트의 끝을 기록하도록 수정한다.

// stop the transition timer
- (void)stopTimer
{
   if (timer)
   {
#ifdef DEVELOPMENT_INSTRUMENTED
 chudRecordSignPost(FunHouseTimer, chudEndIntervalSignPost, 0, 0, 0, 0);
#endif
       [timer invalidate];
       [timer release];
       timer = nil;
   }
}

autoTimer 메소드를 넣는다. 코드를 수정하여 타이머가 작동될 때마다 포인트 사인 타이머를 만들도록 한다:

// called by the transition timer every 1/30 second
// this animates the transitions in sequence - one after another
- (void)autoTimer:(id)sender
{
   int count, i, transitionIndex;
   CIFilter *f;
   NSString *type;
   NSDictionary *attr;
   double now;
   float transitionValue, value, lastTimeValue;

#ifdef DEVELOPMENT_INSTRUMENTED
 chudRecordSignPost(FunHouseAutoTimer, chudBeginIntervalSignPost, 0, 0, 0, 0);
#endif                
// ...

autoTimer 메소드의 끝에 다음의 코드를 더한다:

// ...
   // let core image recompute the view
   [_inspectingCoreImageView setNeedsDisplay:YES]; // force a redisplay

#ifdef DEVELOPMENT_INSTRUMENTED
 chudRecordSignPost(FunHouseAutoTimer, chudEndIntervalSignPost, 0, 0, 0, 0);
#endif
// ...

인터벌 사인 포스트 기록하기 Fun House 어플리케이션을 빌드하고 실행한다. 샘플 이미지 하나를 선택하고, 이벤트 스택에 "flash" 장면전환을 선택한 후, 타겟으로 다른 이미지를 선택한다.

Shark를 시작하고, 시스템 트레이스를 샘플 제한 없이 45초동안 실행하도록 다시 설정한다. Start 버튼을 눌러 샘플링을 시작한다.

Fun House로 돌아가서, 플래시 장면전환 효과를 실행시키기 위해 플레이 버튼을 누른다. 효과가 끝나면, Shark로 돌아와 시스템 트레이스를 멈춘다. 무슨 일이 일어났나 보자.

인터벌 사인 포스트 보기 그림 15는 트레이스 뷰의 사인 포스트 탭을 보여준다. 그림:a_tl_optshark4_FunHouseTraceIntervalSignPosts.jpeg 그림 15:인터벌 사인 포스트 이벤트를 보여주는 Fun House 트레이스 뷰

트레이스 뷰 사인 포스트 탭은 처음부터 끝까지 효과 타이머가 실행된 총 시간을 보여주는 FunHouseTimer 사인 포스트를 가지고 있다. FunHouseWutoTimer 사인 포스트의 발생도 보인다. 각 사인 포스트들은 하나의 타이머 이벤트와 대응한다. FunHouseAutoTimer 사인 포스트는 autoTimer메소드의 chudBeginIntervalSignPost와 chudEndIntervalSignPost에 의해 둘러쌓인 코드 블럭에서 소비한 총 시간을 알려준다. 사인 포스트 중 하나를 더블클릭하면 타임라인 뷰의 이벤트로 바로 연결시켜준다.

트레이스 사인 포스트 탭은 각각의 타이머 이벤트에서 소비한 시간에 대해 엄청난 양의 정보를 보여준다. 테이블의 컬럼은 다음 정보를 디스플레이 한다.

  • Index: 쓰레드 인터벌 인덱스.
  • Interval: 쓰레드 인터벌 인덱스의 시작과 끝.
  • Process: 이벤트가 발생한 프로세스.
  • Thread: 이벤트가 발생한 쓰레드.
  • Name: 사인 포스트 이벤트 파일에 정의된 사인 포스트 이벤트의 이름.
  • CPU Time: 인터벌 사인 포스트 이벤트 동안의 활성 CPU 타임.
  • Wait Time: 인터벌 사인 포스트 이벤트 동안의 대기 시간.

각각의 타이머 이벤트에 특화된 정보를 기록하기 위해 chudRecordSignPost함수의 추가적인 매개변수 4개를 이용할 수 있다. 그 매개변수들은 트레이스 사인 포스트 뷰에 보여지며, 트레이스를 프로그램 상태와 최대한 미세하게 연관시킬 수 있도록 한다.

프로그래밍적인 컨트롤에 대한 좀 더 자세한 정보는 Shark의 유저 가이드를 보라. 덧붙이자면, CHUD 프레임워크의 헤더파일들은 각 함수마다 주석이 잘 붙어져 있고 설명도 되어있다.

[편집] 종합

당신의 코드가 Mac OS X와 어떻게 상호작용 하는지를 이해하는 것은 최적화에 있어서 또한 중요하다. Shark의 시스템 트레이스 기능은 쓰레드 스케쥴링, 시스템 호출 그리고 가상 메모리 폴트를 볼 수 있게하는 음지의 비헤비어를 만들어 준다. Shark는 .시스템 호출과 가상 메모리 폴트에 대해 그것을 발생시킨 코드를 바로 밝혀내어 당신의 코드중에 최적화나 재디자인으로 이득을 얻을 수 있는 부분을 보여주는 유일한 툴이다.

프로그래밍적인 컨트롤과 커스텀 사인 포스트의 사용으로, 시스템 트레이스를 당신의 프로그램 상태로 쉽게 연결시킬 수 있다. 인터벌 사인 포스트를 이용하면, 사인 포스트 마커가 둘러싸고 있는 특정 종류의 동작이 얼마나 시간을 잡아먹는지에 대한 데이타를 얻을 수 있다.

Shark의 시스템 트레이스의 무엇보다 중요한 테마는 시스템 비헤비어와 그 비헤비어를 유발한 당신의 코드를 보여주는 것이다.

[편집] 부가정보

- [샤크 4로 어플리케이션 최적화 하기]

- [샤크로 최적화 하기: 작은 노력, 큰 보답]

- [샤크 이용하기] from the Performance Overview document

- [유니버셜 바이너리 프로그래밍 가이드라인 소개, 제2판]