김윤수의 이상계를 꿈꾸며  
대문으로
도라홈스쿨 | 태그 | 지역태그 | 미디어로그 | 방명록 | 관리자 | 새글 쓰기   
 
C++ 이야기 서른번째: boost::shared_ptr performance
[공지1] 영문 블로그를 옮겼습니다. 주소는 http://codeguru.textcube.com 입니다. 새로운 내용을 올리진 않고, 당분간 이곳에 올린 내용들을 영어로 옮기는 작업을 할 것 같습니다. 제 영문 블로그도 많이 응원해 주세요~
[공지2] 최근 네이버에서 공개한 나눔고딕 글꼴을 적용했습니다. 나눔고딕이 없으신 분은 맑은 고딕, 윤디자인고딕, AppleGothic 순으로 적용됩니다. 나눔고딕이 없으신 분은 여기에서 다운 받으시면 됩니다
[공지3] 제 글을 복사하고 싶으신 분이나 저작권 정보를 알고 싶으신 분은 이 글을 읽어 보세요
[공지4] 각종 Open Source License 번역 시작
최근 제가 진행하던 프로젝트에서 boost::shared_ptr를 상당히 많이 사용해 왔었는데, 성능이 좋게 나오질 않아서 혹시나 해서 오늘 성능을 간단하게 측정해 봤더니 속도 차이가 상당히 많이 나는군요. 성능이 느리겠거니 했는데 상상 이상이었습니다.(실은 이 문제 때문에 한바탕 홍역을 치뤘죠. ㅠ.ㅠ)

다음은 테스트 프로그램입니다.

$ cat shared.cpp
#include <iostream>
#include <boost/shared_ptr.hpp>

using namespace std;
using namespace boost;

class AClass
{
public:
    void Op() const
    {
        ++i;
    }

    int Get() const
    {
        return i;
    }

private:
    mutable int i;
};

#ifdef SHARED_PTR
void Foo(const shared_ptr<AClass>& p)
{
    p->Op();
}
#elif defined(SHARED_PTR_COPY)
void Foo(shared_ptr<AClass> p)
{
    p->Op();
}
#else
void Foo(AClass* p)
{
    p->Op();
}
#endif

int
main(int argc, char* argv[])
{
#if defined(SHARED_PTR) || defined(SHARED_PTR_COPY)
    shared_ptr<AClass> p(new AClass());
#else
    AClass* p = new AClass();
#endif

    int maxCnt = 1000000000;
    if (argc >= 2)
        maxCnt = atoi(argv[1]);

    for (int i = 0; i < maxCnt; ++i)
    {
        Foo(p);
    }

    cout << p->Get() << endl;

    return 0;
}

이 테스트 프로그램은 세 가지 경우를 테스트하기 위한 것입니다.

1. raw pointer를 사용하는 경우: SHARED_PTR  또는 SHARED_PTR_COPY 가 정의되지 않은 경우입니다.
2. shared_ptr을 사용하는 경우: SHARED_PTR 이 정의된 경우입니다. Foo()를 호출할 때, const shared_ptr reference를 넘기기 때문에 복사가 일어나질 않습니다.
3. shared_ptr을 쓰면서 복사를 하는 경우: SHARED_PTR_COPY가 정의된 경우입니다. Foo()를 호출할 때, shared_ptr 객체를 넘기기 때문에 복사가 한 번 발생하고 이때, shared_ptr의 복사 생성자가 수행되면서 내부적으로 reference count를 증가시키고, 다시 함수에서 리턴될 때, reference count를 감소시키게 됩니다. 그리고 reference count 증가 및 감소는 보통 atomic operation으로 수행되기 때문에 보통의 증가/감소보다는 속도가 느린 걸로 알고 있습니다.

이 세가지에 대해 컴파일러 최적화 옵션 및 BOOST 옵션을 약간 달리 하면서 실행을 해 보았습니다. 다음은 테스트를 위한 Makefile 입니다.

$ cat Makefile
all: shared1 shared2 shared3 shared_copy1 shared_copy2 shared_copy3 plain1 plain2 plain3

#CFLAGS=-pg -g
#LDFLAGS=-pg -g
# CASE #1
CFLAGS1=
# CASE #2
CFLAGS2=-O3
# CASE #3
CFLAGS3=-O3 -DBOOST_SP_DISABLE_THREADS
LDFLAGS=

shared1: shared1.o
    g++ $(LDFLAGS) -o shared1 shared1.o

shared_copy1: shared_copy1.o
    g++ $(LDFLAGS) -o shared_copy1 shared_copy1.o

plain1: plain1.o
    g++ $(LDFLAGS) -o plain1 plain1.o

shared1.o: shared.cpp
    g++ $(CFLAGS1) -c -o shared1.o -DSHARED_PTR shared.cpp

shared_copy1.o: shared.cpp
    g++ $(CFLAGS1) -c -o shared_copy1.o -DSHARED_PTR_COPY shared.cpp

plain1.o: shared.cpp
    g++ $(CFLAGS1) -c -o plain1.o -DPLAIN shared.cpp

shared2: shared2.o
    g++ $(LDFLAGS) -o shared2 shared2.o

shared_copy2: shared_copy2.o
    g++ $(LDFLAGS) -o shared_copy2 shared_copy2.o

plain2: plain2.o
    g++ $(LDFLAGS) -o plain2 plain2.o

shared2.o: shared.cpp
    g++ $(CFLAGS2) -c -o shared2.o -DSHARED_PTR shared.cpp

shared_copy2.o: shared.cpp
    g++ $(CFLAGS2) -c -o shared_copy2.o -DSHARED_PTR_COPY shared.cpp

plain2.o: shared.cpp
    g++ $(CFLAGS2) -c -o plain2.o -DPLAIN shared.cpp

shared3: shared3.o
    g++ $(LDFLAGS) -o shared3 shared3.o

shared_copy3: shared_copy3.o
    g++ $(LDFLAGS) -o shared_copy3 shared_copy3.o

plain3: plain3.o
    g++ $(LDFLAGS) -o plain3 plain3.o

shared3.o: shared.cpp
    g++ $(CFLAGS3) -c -o shared3.o -DSHARED_PTR shared.cpp

shared_copy3.o: shared.cpp
    g++ $(CFLAGS3) -c -o shared_copy3.o -DSHARED_PTR_COPY shared.cpp

plain3.o: shared.cpp
    g++ $(CFLAGS3) -c -o plain3.o -DPLAIN shared.cpp

test: test1 test2 test3

test1: shared1_test shared1_copy_test plain1_test

shared1_test: shared1
    time ./shared1 $(CNT)

shared1_copy_test: shared_copy1
    time ./shared_copy1 $(CNT)

plain1_test: plain1
    time ./plain1 $(CNT)

test2: shared2_test shared2_copy_test plain2_test

shared2_test: shared2
    time ./shared2 $(CNT)

shared2_copy_test: shared_copy2
    time ./shared_copy2 $(CNT)

plain2_test: plain2
    time ./plain2 $(CNT)

test3: shared3_test shared3_copy_test plain3_test

shared3_test: shared3
    time ./shared3 $(CNT)

shared3_copy_test: shared_copy3
    time ./shared_copy3 $(CNT)

plain3_test: plain3
    time ./plain3 $(CNT)

clean:
    rm -rf *.o *.dSYM shared1 shared2 shared3 shared_copy1 shared_copy2 shared_copy3 plain1 plain2 plain3 core* gmon.*

1. 테스트 케이스 1: 컴파일러 옵션으로 아무것도 주지 않을 경우 --> CFLGAS1 사용
2. 테스트 케이스 2: 컴파일러 옵션으로 -O3 를 준 경우 --> CFLAGS2 사용
3. 테스트 케이스 3: 컴파일러 옵션으로 -O3 -DBOOST_SP_DISABLE_THREADS 사용. BOOST_SP_DISABLE_THREADS를 켜면 reference count 값을 변경시킬 때, atomic operation을 사용하지 않게 됩니다.

이렇게 테스트 프로그램을 작성하고, 테스트를 해 봤더니 다음과 같은 결과가 나오더군요.

$ make
$ make test
time ./shared1 1000000000
1000000000
       15.66 real        15.52 user         0.04 sys
time ./shared_copy1 1000000000
1000000000
       93.33 real        92.58 user         0.24 sys
time ./plain1 1000000000
1000000000
        8.54 real         8.48 user         0.02 sys
time ./shared2 1000000000
1000000000
        2.81 real         2.79 user         0.00 sys
time ./shared_copy2 1000000000
1000000000
       37.86 real        37.62 user         0.08 sys
time ./plain2 1000000000
1000000000
        0.66 real         0.66 user         0.00 sys
time ./shared3 1000000000
1000000000
        2.84 real         2.79 user         0.00 sys
time ./shared_copy3 1000000000
1000000000
        5.74 real         5.70 user         0.01 sys
time ./plain3 1000000000
1000000000
        0.66 real         0.66 user         0.00 sys

(위 테스트는 looping overhead, function call overhead 를 정확히 측정하지 않았기 때문에 아주 정확한 수치라고는 주장할 수 없음을 미리 밝힙니다. 정확한 수치를 알아내기 위해서는 좀 더 잘 설계된 테스트 케이스를 사용해야 할 것입니다. 다만 위 테스트 결과를 사용하더라도 이 글에서 주장하고자 하는 바를 위한 충분한 근거가 되리라고 생각합니다.)

우선 테스트 케이스1와 테스트 케이스2, 테스트 케이스 3에서 공히 shared_copy > shared > plain 결과가 나온다는 걸 확인할 수 있습니다. BOOST_SP_DISABLE_THREADS 를 켜게 되면 shared_copy에만 영향을 미친다는 걸 알 수 있습니다. 이건 아까 말씀드린 대로 atomic operation이 disable되기 때문인 것으로 추측됩니다. 이상 결과로 보건대 shared_ptr을 쓰실 때는 다음과 같은 점에 유의하셔야 할 것 같습니다.

1.  No free lunch. 좋은 게 있으면 그에 대한 비용이 있기 마련이라는 것이죠. shared_ptr는 일반 포인터에 비해 overhead가 있다는 점을 염두해 두셔야겠습니다.
2. 단순 접근하는 overhead도 2.81/0.66(테스트 케이스 2)으로 상당합니다.
3. 복사 생성자 overhead는 37.86/0.66(테스트 케이스 2)으로 더 크다는 것을 확인할 수 있습니다. BOOST_SP_DISABLE_THREADS를 켜더라도 5.74/0.66으로 여전히 크다는 것을 알 수 있습니다. 따라서 될 수 있으면 복사를 shared_ptr도 복사는 피하셔야 하겠습니다.

제가 테스트한 환경은 다음과 같습니다.

$ uname -a
Darwin MyMac.local 9.7.0 Darwin Kernel Version 9.7.0: Tue Mar 31 22:52:17 PDT 2009; root:xnu-1228.12.14~1/RELEASE_I386 i386
$ g++ --version
i686-apple-darwin9-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5564)

여러분 환경에서는 어떤 결과가 나오는지 실험해 보시고, 트랙백 한 번 남겨 주시면 감사하겠습니다. ^^

소스 코드 첨부합니다(looping overhead를 측정하기 위해 본문 소스에서 약간 더 수정했습니다).



제 글이 유익하셨다면 오른쪽 버튼을 눌러 제 블로그를 구독하세요. ->
블로그를 구독하는 방법을 잘 모르시는 분은 2. RSS 활용을 클릭하세요.
RSS에 대해 잘 모르시는 분은 1. RSS란 무엇인가를 클릭하세요.

마지막으로 제 글이 유익하셨다면 과감하게 추천 버튼 한방 부탁드립니다 ^^

Tag :


BLOG main image
이상계는 이쪽에 속해있지 않고 저쪽에 속해 있다. 이쪽에 사는 우리는 이쪽에 머무르지 않고 저쪽을 꿈꾸며 살아야 한다. 여기 오는 모든이들이 저쪽의 충만함을 이쪽에서의 매일의 삶에 경험할 수 있기를... E-mail: (yesarang) at (yahoo.co.kr)
 블로그 구독



meet me at meet me at me2DAY
E-mail 주소를 입력하세요:


 블로그 검색
Google
 Notice
me2day 개설했습니다.
블로그 스킨 바꾸었습니다
제 블로그의 성격
 분류
분류 전체보기 (341)
IT동향 (29)
사회동향 (4)
기술정리 (10)
개인 (17)
기타 (28)
기독교신앙 (1)
S/W개발 (110)
블로깅 (13)
리뷰 (8)
구글이야기 (9)
인터넷오늘은 (108)
독서 (3)
 태그 목록
인터넷오늘은 북마크 c++ 프로그래밍 sw개발 SW 개발 블로그 구글 SW 프로그래밍팁 c Programming Language S/W 개발 cpp 네이버 고수 고수팁 검색 고절가주팁 블로깅 IT 전략 iPhone 리뷰 다음 블로거 Programming Technology RSS
 달력
«   2009/07   »
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
 최근글
C++ 이야기 서른번째: boost::.. (4)
김윤수의 인터넷 오늘은 - 200..
김윤수의 인터넷 오늘은 - 200..
김윤수의 인터넷 오늘은 - 200.. (1)
C++이야기 스물아홉번째: Port..
 최근 댓글
그러게요. 저도 쓰면서 성능이..
김윤수 - 21:17
어느새 다녀갔네?
김윤수 - 21:15
아는대로 써 보는 거랑, 실제..
eslife - 07/01
좋은거 하나 배우고 갑니다. :)
파다기 - 07/01
전 미국에서 회사를 다녀서 그..
violino - 06/26
 최근 트랙백
TR1을 이용한 C++에서의 정규..
김재호의 디지털보단 아날로그
항목 39 : 가상 함수는 비공용..
최익필의 이름없는 블로그
죠커의 생각
jokka's me2DAY
검색 어뷰징 패턴 및 필터링..
made by 서보성
김윤수의 알림
yesarang's me2DAY
 Archive
2009/06
2009/01
2008/12
2008/11
2008/10
2008/09
2008/08
2008/07
2008/06
2008/05
2008/04
2008/03
 링크 모음
art.oriented
kkamagui 프로그래밍 세상
Monaca
Sutter's Mill
Yesarang's Blog[제 영문 블로..
[개인] Chester's Blog
[개인] Homo Scriptus
눈에 보이는 소프트웨어
류광의 번역 이야기
사진찍는 프로그래머
서광열의 프로그래밍 언어 이..
주네의 열린 소프트웨어
퓨처워커들의 u-Platform 이야기
필넷의 IT이야기
 방문자 통계
Total : 383,568
Today : 14
Yesterday : 29

믹시