BLOG ARTICLE 튜닝 | 1 ARTICLE FOUND

  1. 2008.03.23 Shark를 이용한 성능 최적화

사용자 삽입 이미지
샤크는 프로그램의 성능 측정 도구(프로파일러)입니다. 성능 튜닝은 사냥꾼의 동물적인 감각이 필요하고 상어가 먹이를 추적하는데 가장 앞선 동물이라 샤크라는 이름을 지었다고 합니다.

벌레들을 잡아 먹는 사마귀(mantis)로 이름을 지은 버그 추적 시스템처럼 상어(shark)도 재미있는 이름 같습니다.



0. 준비
실행은 /Developer/Applications/Performans Tools/Shark를 클릭하거나, Xcode의 Debug 메뉴에서 Lauch Using Performans Tool에서 Shark를 클릭하여 실행 합니다.

만약 해당 디렉토리에 Shark가 없다면 Xcode 설치시 CHUD Tools를 선택하지 않았기 때문입니다. Xcode 설치 파일을 이용해서 Shark를 설치하면 됩니다.

1. 테스트 코드 작성

테스트를 위해서 Xcode에서 아래와 같은 샘플코드를 작성합니다.

#import <Foundation/Foundation.h>

@interface Musik1 : NSObject {
}
@end
@implementation Musik1
-(void) run {
    int i, n;
    for (i = 0; i < 100; i++) {
        n = i * i;   
    }
}
@end

@interface Musik2 : NSObject {
}
@end
@implementation Musik2

 - (void) run {
    int i, n;
    for (i = 0; i < 1000; i++) {
        n = i * i;   
    }
}
@end

void Run()
{
    int i;
       
    for(i = 0; i < 10000; i++) {
        Musik1 *musik1 = [[Musik1 alloc] init];
        Musik2 *musik2 = [[Musik2 alloc] init];
       
        [musik1 run];
        [musik2 run];
   
        [musik1 release];
        [musik2 release];
    }
}

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // insert code here...
    Run();
   
    [pool release];
   
    return 0;
}

무식하게 돌아가는 코드를 작성해 보자 하고 생각없이 이름을 지었는데, 그냥 변경없이 포스팅에 사용하기로 했습니다. 나중에 다른 툴에서도 예제로 사용하기 위해 클래스를 두개 만들고 메모리를 할당하고 해제하는 코드를 넣었습니다.

소스는 10000번 돌면서 Musik1과 Musik2를 생성 해제 하면서 각각의 run 메소드가 실행됩니다. Musik1의 run 메소드는 다시 100번 돌고 Musik2는 더 무식해서 1000번 돌도록 하였습니다.

2. Shark 실행
이제 빌드를 하고 Xcode의 Debug 메뉴에서 Lauch Using Performans Tool/Shark를 클릭합니다. 아래와 같이 Shark가 실행된 모습을 보실 수 있습니다.

사용자 삽입 이미지

샤크에서 좌측의 Start 버튼을 클릭합니다. 아래와 같은 창이 뜨면 원하시는 옵션을 선택한 후 [OK]를 클릭합니다.

사용자 삽입 이미지

이제 프로그램이 런칭되어 분석을 시작합니다. 30초 내에 프로그램이 종료하지 않으면 자동으로 종료하고 분석된 결과를 보여줍니다. Config 메뉴에서 View Mini Config Editor를 선택하시면 실행 전에 제한시간과 다른 샘플링 옵션을 변경하실 수 있습니다.

사용자 삽입 이미지
재밌는 것은 분석중에는 아이콘에서 상어의 모습이 붉은 색으로 변합니다. 맹렬히 먹이감을 쫓고 있는 것 같습니다. 무섭습니다.


3. 샘플링 데이터 확인
1) 프로파일 브라우져
완료되면 아래와 같이 분석된 결과를 보여 줍니다. 우측 하단의 View 항목에서 표현 방식을 변경할 수 있습니다. Heavy는 항목별로 Tree는 말 그대로 트리 구조로 결과를 보여 줍니다. 아래의 이미지를 보시면 둘의 차이점을 확인하실 수 있습니다. 각 컬럼은 아래와 같습니다.

  • self - 행당 항목의 비율
  • Total - 하위 항목을 모두 포함한 전체 비율
  • Library - Symbol이 포함된 라이브러리
  • Symbol  - 심볼 (함수명)

사용자 삽입 이미지

위를 보시면 실행시간의 64.1%를 차지하는 [Musik2 run]을 중점적으로 점검해야 됨을 알 수 있습니다. [Musik2 run] 항목을 더블클릭하면 아래와 같이 해당 소스가 오픈됩니다. 우측 상단의 버튼들을 클릭하면 원본 소스, 어셈블리 또는 같이 볼수 있습니다.
 
2) 소스코드 브라우져
사용자 삽입 이미지

코드에서 직접 부하를 확인할 수 있습니다. [i] 아이콘이 있는 곳은 해당 코드에 대한 도움말이 있습니다. 아이콘을 클릭하면 전체 내용을 볼 수 있습니다. 아래는 Both 버튼을 클릭하여 원본 소스와 어셈블리를 같이 보여 주는 모습니다.

사용자 삽입 이미지

run 함수에서 'n=i * i(진한 갈색 부분)'에서 실행속도의 64.2%를 소비하고, 나머지 for 루프를 위한 'i++, i < 1000(연한 갈색)'에서 나머지를 소비하고 있는 것을 알 수 있습니다.

좌측의 'n = i * i' 에 해당되는PPC 어셈블리 코드를 간단히 살펴 보겠습니다. (우측 하단의 [Asm Help]를 클릭하시면 어셈블리 명령어 셋에 관한 간단한 설명을 보실 수 있습니다)

lwz r2, 28(r30) // 28(r30)의 메모리 값(i)을 r2 레지스터로 불러 옵니다.
lwz r0, 28(r30) // 28(r30)의 메모리 값(i)을 r0 레지스터로 불러 옵니다.
mullw r0,r2,r0 // r2, r0을 곱한 값을 다시 r0에 저장합니다.
stw r0, 24(r30) // 레지스터 r0의 값을 28(r30)의 메모리(n)에 저장합니다.

위는 제 PPC의 어셈블리 모습이며 인텔맥에서는 아래와 같습니다.
 
L3:
    .stabd    68,0,8
    movl    -16(%ebp), %eax
    imull    -16(%ebp), %eax
    movl    %eax, -12(%ebp)
    .stabd    68,0,7
    leal    -16(%ebp), %eax
    addl    $1, (%eax)
L2:
    cmpl    $99, -16(%ebp)

4) Chart 뷰
이제 Chart를 클릭하여 함수의 호출 경로가 기록되어 있는 call stack을 확인해 보겠습니다. Chart에서는 1ms마다 call stack을 기록한 내용을 그래프와 목록으로 확인할 수 있습니다. 그래프 또는 목록을 클릭하면 좌측에서 해당 시간의 call stack의 내용을 확인할 수 있습니다.
사용자 삽입 이미지

"우선 여기서 알수 있는 것은 제 맥이 엄청 느리다는 것입니다. 대부분의 맥에선 아래의 샘플링 시간이 보시는 것 보다 적게 나올 것입니다. 혹시 사용하는 맥이 너무 빨라 Shark가 붉게 변하는 모습을 못 보셨으면 반복 횟수를 늘리거나 다른 코드를 넣어 무식이의 run을 더 무식하게 만드시면 됩니다."

초기에 스택이 큰 것은 기동 시 초기화 루틴에서 부르는 것들입니다. 전체적으로 5로 나타나는 것은 대부분의 로드가 [Musik1(또는 2) run]일어 나기 때문에 위이미지의 좌측에서 보시는 것 처럼 start > _start > main > Run > [Musik1(또는 2) run]의 5단계이기 때문입니다.

우측 하단 View에서 CPU를 선택할 수 있는데 저는 PPC라 1개가 나오지만 인텔 코어듀오가 장착된 맥에선 2개의 CPU가 나올 것입니다.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Shark에서는 샘플링된 데이터를 파일로 저장할 수 있습니다. 현재 데이터를 저장하고 최적화를 수행하고 다시 Shark를 실행하여 이전 데이터와 비교해보며 최적화의 결과를 확인할 수 있습니다.

Shark에 대한 자세한 내용은 ADCShark User Guide 를 참조하시고, Shark 이외에 /Developer/Applications/Performans Tools/ 디렉토리에서 유용한 다른 툴들도 확인하실 수 있습니다.

'Xcode 2 > Tip' 카테고리의 다른 글

주석내의 __MyCompanyName__ 변경  (2) 2008.03.04
다이알로그 윈도우 구현  (2) 2008.01.09
Xcode에서 디버깅 작업  (6) 2007.12.20
Xcode에서 Flex 개발하기  (2) 2007.12.08
Xcode에서 파이어폭스 플러그인 컴파일 하기  (0) 2007.12.04
AND