DNA 포럼 API 서비스 모음 DNA Lens

구글 퍼포먼스툴의 사용법


  • 김민수(CTO본부 기반기술팀), 2008년 2월

초록(Abstract)

서론 #


구글에서 개발하여 자사의 여러 인프라 소프트웨어에 적용한 기술중에 공개되고 있는 CPU 프로파일링 해석과 고속의 쓰레드 지원하는 캐쉬 malloc을 구현하는 오픈소스프로젝트의 툴입니다.

인스톨 #


google-perftools 는 이쪽에서부터 다운로드할 수 있습니다.

배포판별로 바이너리 패키지도 있지만 여기서는 소스를 다운로드해 컴파일 합니다. 현재는 최신버전이 0.93 이지만 과거 glibc 버전과의 호환성 문제가 발생할수 있기 때문에 안전하게 0.8을 써보겠습니다. 컴파일은 다른 패키지와 똑같이 ./configure 하여 make 하면 끝입니다. make install 하여 인스톨 할 때만 su하여 root로 해주세요.

$ tar xvfz google-perftools-0.8.tar.gz
$ cd google-perftools-0.8
$ ./configure
$ make
$ su
$ make install

이것으로 인스톨은 완료입니다.

Cpu profiler의 사용 #


사용하는 라이브러리는 libprofiler.so 를 링크하고 인클루드 파일은 google/profiler.h 입니다.

아래는 컴파일하려는 샘플 코드입니다. 정확한 프로파일링을 위하여 컴파일 되는동안 최적화 되거나 변경되지 않도록 asm volatile("sync") 를 사용하였습니다. 물론 다른 코드도 비교하여 계산하는데 적당한 코드라면 관계없습니다.

#include <iostream>
#include <google/profiler.h>
using namespace std;

void func1 ()
{
    for (int i = 0; i < 10000; i++)
        for (int j = 0; j < 1000; j++)
            asm volatile ("sync");
}
void func2 ()
{
    for (int i = 0; i < 10000; i++)
        for (int j = 0; j < 2000; j++)
            asm volatile ("sync");
}
void func3 ()
{
    for (int i = 0; i < 10000; i++)
        for (int j = 0; j < 3000; j++)
            asm volatile ("sync");

    func1 ();
    func2 ();

}


int main (int argc, void** argv)
{
    //ProfilerStart ("prof.out");
    func3 ();
    //ProfilerStop ();

    return 0;
}

프로그램 전체의 프로파일을 취하고 싶을 때 #


공유 라이브러리로서 libprofiler.so 를 링크해 컴파일 합니다. 소스에 대한 수정은 이것 뿐입니다. -g 옵션을 붙여 디버그 정보를 부가할 필요도 없습니다. 실행시에 라이브러리에의 패스가 필요해서 환경 변수의 LD_LIBRARY_PATH 를 지정합니다.

csh 의 경우
$ setenv LD_LIBRARY_PATH /usr/local/lib

bash 의 경우
$ export LD_LIBRARY_PATH=/usr/local/lib

추가로 프로파일을 취하는 트리거로서 환경 변수 CPUPROFILE 을 설정합니다. 뒤에는 측정 결과를 쓰는 파일명입니다.

csh 의 경우
$ setenv CPUPROFILE prof.out

bash 의 경우
$ export CPUPROFILE=prof.out

이로써 준비는 끝입니다. 프로그램을 실행하면 프로파일 결과가 지정한 파일에 써집니다.

$ ./a.out
PROFILE: interrupts/evictions/bytes = 439/0/508

프로그램의 특정 부분만의 프로파일을 취하고 싶을 때 #


공유 라이브러리로서 libprofiler.so 를 똑같이 링크하고, 측정하고 싶은 곳을 ProfilerStart ("file name") 와 ProfilerStop () 으로 둘러싸면 됩니다.

ProfilerStart ("prof.out");
// 여기가 측정 대상
ProfilerStop ();

prof.out 은 프로파일 결과를 써내는 파일명입니다. 실행시에 라이브러리에의 패스가 없기 때문에 환경 변수로 역시 LD_LIBRARY_PATH 를 지정합니다.

csh 의 경우
$ setenv LD_LIBRARY_PATH /usr/local/lib

bash 의 경우
$ export LD_LIBRARY_PATH=/usr/local/lib

이것으로 준비는 끝입니다. 프로그램을 실행하면 프로파일 결과가 지정한 파일에 써내집니다.

$ ./a.out
PROFILE: interrupts/evictions/bytes = 454/0/536

프로파일 결과의 해석 #


측정 결과는 prof.out 로 합니다. 해석 프로그램은 pprof 라는 이름입니다. 실행한 프로그램과 프로파일 결과를 인자로 하여 실행해 주세요.

$ ./pprof a.out prof.out
(pprof)

prompt가 표시되어 멈춥니다. 사용방법은 help 커멘드를 실행하면 나옵니다. 종료할 때는 당연하듯 quit 입니다. 해석 결과를 표시하기 위해서 top 커멘드를 사용합니다.

 (pprof) top
Total: 441 samples
     220  49.9%  49.9%      220  49.9% func3
     147  33.3%  83.2%      147  33.3% func2
      74  16.8% 100.0%       74  16.8% func1
       0   0.0% 100.0%      441 100.0% generic_start_main
       0   0.0% 100.0%      221  50.1% _init
       0   0.0% 100.0%      441 100.0% __libc_start_main
       0   0.0% 100.0%      441 100.0% main

이와 같이 수행시간이 오래 걸린 함수로부터 차례로 표시됩니다.

이 결과는 그래프로서 예쁜 그림으로 표시할 수도 있습니다. 추가로 doxygen 에서도 사용하는 graphviz 라고 하는 어플리케이션이 필요합니다. (커멘드명은 dot ). 일단 quit 하여 pprof 의 prompt를 종료합니다

 (pprof) quit

커멘드 라인으로부터 pprof 를 기동해 닷 형식의 파일을 출력합니다.

$ pprof --dot a.out prof.out > prof.dot
Dropping nodes with <= 2 samples; edges with <= 0 abs(samples)

가끔 에러 메세지가 나옵니다만 신경쓰지 않아도 됩니다. 그럼 graphviz (dot) 를 사용해 그래프를 만들어 그래픽 파일(png)을 출력합니다.

$ dot -T png prof.dot > prof.png

그래픽 파일을 보려면 적당한 프로그램을 사용하면 되며 여기서는 kview 를 사용했습니다.

$ kview prof.png

http://dna.daum.net/wiki/imgs/custom/Google-profout.png

결과는 이러한 그래프를 얻을 수 있습니다. 그 함수가 전체의 몇%의 시간을 사용하고 있을지가 한눈에 그림으로 보입니다. 노드의 크기가 처리 시간에 비례하고 있기 때문에 쉽게 어디서 병목현상이 생기는지도 알수 있습니다.

TCMALLOC #


http://dna.daum.net/wiki/imgs/custom/mallocs.png

Google Perftools 은 위에서의 사용처럼 퍼포먼스 측정용의 프로파일러로 유명합니다만, 그 중에는 TCMalloc(Thread Caching Malloc)이라고 하는 고속의 쓰레드를 지원하여 캐쉬하는 malloc 라이브러리도 포함되어 있습니다.

TCMalloc의 페이지를 보면 벤치마크의 결과로 특히 multi-thread 어플리케이션에서 효과를 발휘해서 속도와 메모리를 절약하고 있습니다. 그러나, malloc 은 유저랜드의 근간을 이루는 중요한 라이브러리의 하나이므로 교체하면 치명적인 문제가 발생할수 있습니다만, 그런만큼 반대로 효과도 있는 것이기에 해볼만합니다.

사용법 #


사용법은 단지 위의 TCMalloc 의 페이지에서는 PRELOAD 로 tcmalloc 라이브러리만 지정하라고 적혀있습니다. 하지만 Ubuntu 7.04 에서는 ptherad_key_create 문제가 발생하고 있기 때문에 다음과 같이 하여야 합니다.

LD_PRELOAD="/usr/lib/libgthread-2.0.so.0:/usr/lib/libtcmalloc.so" firefox

아니면
export LD_PRELOAD="/usr/lib/libgthread-2.0.so.0:/usr/lib/libtcmalloc.so"

그 외의 리눅스 배포판에서는 /usr/lib/libtcmalloc.so 만 쓰면 됩니다. 더 확실하게 사용하려면 /etc/ld.so.preload 에 추가해 놓으면 항상 사용 할 수 있습니다.

사용결과 #


TCMalloc 이 효과가 있을 것 같은 multi-thread 화 되고 있는 어플리케이션을 ps axm등으로 해서 찾아 보면 mysql, firefox, thunderbird, eclipse (java) 과 같이 비교적 대규모의 쓰레드를 사용하는 어플리케이션이 많은 듯 합니다. 우선 shell 의 rc 설정하여 평상시에 리눅스 데스크탑으로 사용해 본 결과로는 적어도 브라우저와 eclipse, firefox 정도는 가벼워진 것 같은 생각이 듭니다. 특히 여러개의 어플리케이션을 시작할때는 무겁다는 느낌이 나름대로 없어진거 같은 느낌입니다.

그리고는 현재 시험해보지는 않습니다만 web 서버로 apache를 워커쓰레드 버전으로 하면 mysq과 같이 효과가 나올지도 않을까 생각이 듭니다.

현재 저희 회사 서비스에 쓰이고 있는 데이터베이스 미들웨어 Santa 의 메모리 사용 문제에 대한 가장 유력한 문제해결책으로 떠오르며 사내에서 아직 내부 테스트중이며 곧 그에 대한 자세한 결과를 추가하겠습니다.

결론 #

  • 퍼포먼스 결과에 대한 수치를 정확하게 측정하여 어디에서 문제가 생기는지 찾을수 있고 대규모의 메모리 사용문제에 대해 보다 효율적인 캐쉬 방식을 도입할수 있습니다.