정말 오랜만에 포스팅을 하네요. 그동안 좀 정신없이 살았더랍니다.
포스팅하려고 쌓아놓은 꺼리들이 많은데요...
그중에 어플리케이션 성능 튜닝에 관한 팁을 한가지 올려보고자 합니다.
MSDN에 보면 좋은 자료가 있습니다. (찾아보면 번역본도 있습니다.)
http://msdn.microsoft.com/en-us/magazine/cc337887.aspx
제가 작성한 건 위의 글 내용 중 포인트만 추려놓은 거라고 보시면 됩니다.
성능 튜닝의 포인트는 병목을 찾아내는 것입니다. 그래서 DB튜닝을 할때는 SQL Profiler로 악성 쿼리를 찾아내지요. 20:80 법칙이라는 말이 가장 잘 들어맞는 분야가 바로 성능 튜닝이 아닌가 싶습니다.
어플리케이션의 병목을 찾기 위해 많이 쓰는 방법이 함수 별로 실행시간을 측정하여 로그로 기록하는 것입니다.
예를 들면, 함수 시작지접에서 TickCount를 기록해두고, 빠져나갈 때 GetTickCount하여 그 차이값을 글로벌 임시변수에 계속 더하는 거죠. 실행 끝나고 나서 로그를 확인해보면 어떤 함수에서 어느정도 시간이 소요되었는지 대충 알 수 있습니다.
약간 원시적이긴 하지만, 병목 찾는 작업이란게 기본적으로는 저 범주를 벗어나지 않습니다. 다만 저런 작업을 자동으로 해주는 툴을 사용하면 더 편해진다는 것 뿐이지요.
VisualStudio에도 "Profiler"라는 기능을 통해 이런 작업이 가능합니다. VisualStudio2005 에서 부터 지원하구요. VisualStudio2008에서는 더 정확하고 세밀한 분석이 가능하다고 합니다. 단, "Team Edition" 에서만 사용할 수 있습니다.
이 포스트의 내용은 VisualStudio2005 에서 테스트하였습니다.
VisualStudio 메뉴 중 Tools > Performance Tools > Performance Wizard 를 선택합니다.
(VisualStudio 2008의 경우 Analyze 메뉴에 있다고 하네요)
아래와 같이 Performance Wizard 화면이 뜹니다. 여기서 성능 측정할 Target Project를 선택합니다. 솔루션 내에 검사대상 모듈이 여러개라면 실행모듈 (EXE) 프로세스를 선택합니다.
그 다음엔 Profile할 방법을 정하라고 하네요.
두가지 방법이 있습니다.
Instrumentation : 특정 모듈(특정 DLL)에 대해 보다 상세하게 검사합니다. Win32 API 레벨까지 검사되지만, 무지 느려지고, 빌드 옵션도 틀려지는 등 약간 귀찮은 문제들이 있습니다.
일단 "Sampling"을 선택하고 넘어갑니다.
완료 화면이네요. 여기서 Finish 를 클릭하면 아래와 같은 "Performance Explorer" 창이 열립니다.
여기서 첫번째 빨간 네모 안의 "Play"버튼을 누르면 어플리케이션이 실행되면서 Profiling이 시작됩니다. 두번째 빨간 네모 안에 있는 "Stop"버튼이 클릭되거나 실행이 종료되면 Profiling도 끝나게 됩니다. 만약 "Play" 버튼이 비활성화 되어있다면, "Targets" 모듈이 실행파일이 아닌 경우입니다.
만약 DLL만 있고 실행모듈이 없어서 "Play"버튼이 비활성화 되어있다면 위와 같이 Project나 Binary를 추가로 등록해줍니다. 사실 "Sampling"을 선택한 경우엔 실행모듈("EXE")만 등록해주면 됩니다.
이 테스트는 Debug 모드에서 진행했지만, 진짜로 성능 병목을 찾고자 한다면 Release 모드로 해야겠죠.
프로파일이 끝나면 아래와 같은 레포트가 나타납니다.
레포트 아래쪽에 여러가지 탭이 보입니다. 이중 "Call Tree"를 선택합니다.
오호. 이런 화면이 나오는군요. 각 컬럼의 의미는 대략 이렇습니다.
Exclusive Percent : 해당 함수 및 해당 함수에서 호출된 함수가 몇 %의 자원을 사용했는지가 나타납니다. Tree를 펼치면서 이 항목이 가장 높은 함수를 찾아들어가면 병목을 찾을 수 있습니다.
Inclusive Percent : 해당함수에 의해 호출된 함수가 아닌 해당 함수에서만 소모된 자원을 의미합니다. Inclusive Percent가 높지만 Exclusive Percent가 낮다면 병목은 해당 함수가 아닌 해당함수에서 호출된 함수중에 있다고 볼 수 있습니다.
위의 화면에서 "_mainCRTStartup"을 펼친 후 하위노드 중 가장 많은 자원을 사용한 "UnknownFrame(s)"를 펼칩니다. 새로 펼쳐진 노드 중 가장 많은 자원을 사용한 _tmainCRTStartup"을 펼칩니다. (자원을 많이 사용했다는 것은 "Inclusive Percent"가 높다는 의미입니다.)
이렇게 Call Tree를 펼쳐나가다 보면 결국 아래와 같이 병목 지점을 찾아낼 수 있습니다. 함수 이름을 지우고 나니... 별로 남은게 없군요. ㅋㅋㅋ
이번에는 특정 모듈에 대해 자세히 살펴보는 "Instrumentation"을 테스트해보겠습니다.
위의 Performance Wizard 에서 "Instrumentation"을 선택합니다. 혹은 "Performance Explorer"에서 방법을 변경해도 됩니다.
그러고 나서 "Play"를 시키면 아래와 같은 오류가 뜹니다.
원인은 이거죠. 상세 프로파일링을 위해 빌드 옵션에 "/PROFILE"옵션을 추가해줘야 합니다.
Linker 옵션에서 "Advanced"를 보면 "Profile" 항목이 있습니다.
이제 같은 방식으로 돌려서 결과를 얻습니다.
"Instrumentation"할 때는 Target 모듈이 등록되어야 하는데, 만약 Target 모듈이 DLL이라면 DLL과 Host가 함께 등록되어야 합니다.
위와 같이 필요한 모듈들을 선택해줍니다.
Host 용 EXE와 분석 대상 dll이 함께 등록되었네요.
결과가 나왔습니다.
어떤가요? 호출된 API들의 횟수와 소요시간 등이 출력되었습니다. 이만하면 병목찾는건 어렵지 않겠죠.
성능이 문제가 되는 상황이라면 반드시 병목이 존재한다고 봐도 됩니다. 병목이 찾아진다면 개선방법도 있게 마련이죠. 어디가 병목인지 찾아내기만 한다면 오히려 해결은 어렵지 않습니다.
'C++' 카테고리의 다른 글
설계 문서 작성시 TIP(?) (3) | 2011.02.16 |
---|---|
Visual Studio 2005 단축키 (3) | 2011.01.06 |
Side-by-Side 문제 관련 (4) | 2010.02.11 |
Caller Verification을 통한 Dll Injection 차단 (0) | 2010.01.20 |
C++ Calling Conventions 관련된 내용 정리 (0) | 2010.01.19 |