ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C++ 성능이야기 – MFC CString 인자로 사용하기
    개발/C/C++ 2007.04.27 20:01

    MFC로 어플리케이션을 만들 경우 CString 에 대한 의존도가 상당히 높습니다. 하지만 CString 을 남용함으로써 성능 저하를 일으키는 경우가 많은데 오늘은 그 중 CString 문자열을 함수로 전달할 때 발생하는 성능관련 이슈를 확인하려고 합니다

     

    1.     테스트의 목적
    문자열 함수 처리에 많이 사용되는 CString 을 다른 함수로 전달 할 경우 가장 좋은 성능을 가진 함수 인자 선언 TYPE 을 찾기 위함.

     

    2.     테스트 환경

    1)     컴파일러 - VISUAL STUDIO .NET 2005

    2)     PC 사양 - Pentium® D CPU 3.19GHz, 2GB RAM

     

    3.     테스트 코드 인자로 들어온 텍스트를 DUMMY CString 할당하고 간단한 자연수 연산을 한 뒤 리턴 하는 함수

    // 인자로CString 레퍼런스전달

    int CPerfTestDlg::testObject(const CString& sText)

    {

               CString sData = sText;

               //sData.MakeUpper();

               int i = 10 * 100;

               return i;

    }

     

    4.     테스트용 함수선언

    CASE

    방법

    사용 함수 헤더

    호출방식

    1

    CString Reference 사용

    int testObject(const CString& sText);

    CString sTest = "abcd";

    int nRet = testObject(sTest);

    2

    CString 객체 전달

    int testObject2(CString sText);

    CString sTest = "abcd";

    int nRet = testObject(sTest);

    3

    CString à const char 로 변환

    int testObject3(const char* lpText);

    CString sTest = "abcd";

    int nRet = testObject(sTest);

    4

    상수를 const char 사용

    int testObject3(const char* lpText);

    nRet = testObject3("abcd");

     

    5.     테스트 결과(각 함수를 1,000,000번 호출)

    CASE

    방법

    결과(ms)

    성능차

    1

    CString Reference 사용

    68

    100%

    2

    CString 객체 전달

    140

    205.88%

    3

    CString à const char 로 변환

    411

    604.41%

    4

    상수를 const char 사용

    412

    605.88%

     

    6.     결론

    1)     CString 을 함수의 인자로 전달 할 경우 Reference Point 로 전달하는 것이 가장 빠름

    2)     CString const char 로 변환하는 작업은 상당히 많은 시간이 걸리며 CString 을 값으로 전달하는 방법 ‘2’번보다 오히려 성능이 떨어짐(const char* 로 변환하기 위한 operation에 상당한 시간이 걸림을 확인)

    3)     대부분의 함수 사용하는 곳에서 CString 문자열을 넘긴다고 가정할 경우 const char* 를 인자로 사용하기 보다 CString* CString&를 사용하는 것이 성능향상에 도움이 됨(일반적인 문자열 전달은 const char* 가 맞지만 현실과 성능은 늘 괴리가 있다 ㅠ_)

     

    7.     부록(?) – 실제 코드

    void CPerfTestDlg::OnBnClickedButton2()

    {

               // TODO: Add your control notification handler code here

               char szText[100];

               DWORD dwStartTime = timeGetTime();

               int nRet;

               CString sTest = "abcd";

               for (int i = 0; i < 1000000; i++)

               {

                         nRet = testObject(sTest);

               }

               sprintf_s(szText, sizeof(szText),"testObject Ellapsed->%ld",timeGetTime() - dwStartTime);

               m_ResultList.AddString(szText);

     

               dwStartTime = timeGetTime();

               for (int i = 0; i < 1000000; i++)

               {

                         nRet = testObject2(sTest);

               }

               sprintf_s(szText, sizeof(szText),"testObject2 Ellapsed->%ld",timeGetTime() - dwStartTime);

               m_ResultList.AddString(szText);

     

               dwStartTime = timeGetTime();

               for (int i = 0; i < 1000000; i++)

               {

                         nRet = testObject3(sTest);

               }

               sprintf_s(szText, sizeof(szText),"testObject3 Ellapsed->%ld",timeGetTime() - dwStartTime);

               m_ResultList.AddString(szText);

     

               dwStartTime = timeGetTime();

               for (int i = 0; i < 1000000; i++)

               {

                         nRet = testObject3("abcd");

               }

               sprintf_s(szText, sizeof(szText),"testObject5 Ellapsed->%ld",timeGetTime() - dwStartTime);

               m_ResultList.AddString(szText);

     

    }

     

    int CPerfTestDlg::testObject(const CString& sText)

    {

               CString sData = sText;

               int i = 10 * 100;

               return i;

    }

    int CPerfTestDlg::testObject2(CString sText)

    {

               CString sData = sText;

               int i = 10 * 100;

               return i;

    }

    int CPerfTestDlg::testObject3(const char* lpText)

    {

               CString sData = lpText;

               int i = 10 * 100;

               return i;

     

     

    댓글 4

    • 프로필사진

      const char *로 넘길 때 시간이 오래걸리는 것은 testObject3()에서 CString 객체를 만들 때에 문자열을 복사하기 때문이 아닌지요? CString의 = 연산자가 효율적으로 구현되어서 (내부를 제대로 보지는 않았지만.. 메모리를 공유하고 뭐 그렇게 쓰는 것 같습니다만..) CString간의 복사 연산이 const char *의 대입 연산보다 훨씬 빠릅니다. 테스트 함수에서 문자열 복사를 하신 것은 아마도 컴파일러 최적화를 막기 위해서 그렇게 하신 것 같습니다만, 최적화 옵션을 끄시고 파라미터만 넘기면 별 차이 없다는 것을 발견하실 듯.
      그리고, CString의 포인터나 참조를 넘기지 않고 객체 자체를 넘겨도 별로 큰 오버헤드 걸리지 않는 것으로 알고 있습니다. (문자열을 일일이 복사하지 않기 때문이지요.)
      또, const char * 등으로 변환하는 연산자도 정의되어 있고, GetString()이라는 멤버 함수도 있습니다.(둘 모두 같은 동작) 따라서, 캐스팅을 하거나 GetString()을 사용하면 CString이 내부적으로 가지고 있는 m_pszData라는 char 배열의 포인터를 리턴합니다. 함수 호출의 overhead(만일 컴파일러가 잘 만들어졌다면 알아서 inline으로 할테니 이 overhead도 없을 것입니다.)만 있을 뿐이죠.

      2007.07.16 13:22
      • 프로필사진

        예리하게 지적해 주셨네요 ^^;
        님의 말씀대로 다음 한줄
        CString sData = sText;
        대신
        printf("%s", (const char*)sText);

        와 같이 테스트 해 본 결과
        Reference 로 전달 - 324ms
        객체를 전달 - 405ms
        const char* 전달 - 327ms
        상수전달 - 324ms

        로 나왔습니다.
        이번에는 상수전달이 객체전달보다 더 나은 성능을 보이네요
        말씀하신대로 인수 전달시 오버헤더 보다 인수로 받은 값을 CString 에 대입할때 대입연산자에서 우측 인자에 따라 성능의 차이를 보이는걸로 보입니다. 제가 미처 파악하지 못한 부분 지적해 주셔서 감사합니다.

        2007.07.16 18:06 신고
    • 프로필사진

      오래된 글에 답글 남기려니 쑥스럽군요. 폴리비님의 글에서도 이미 대략 지적이 되어있습니다만..

      CString은 생각보다 잘 짜여진 클래스입니다. 그래서 CString을 객체 그대로 넘겨도 오버헤드가 그렇게 크지 않은데요. 특히 원실험결과 3, 4번의 경우, 즉 직접 CString 객체 생성보다 세배 정도 빠릅니다. 이건 실제 CString을 객체 그대로 넘겨도 문자열을 카피하는 것이 아니라 공유를 합니다. 그리고 이 스트링에 변화가 가해지면 fork를 해서 그 때서야 카피를 합니다. 일종의 copy-on-write 방식이죠. 그래서 단순히 읽기 전용으로 CString 객체를 그대로 넘길 때, 흔히 생각하기 쉬운 객체 내용 복사는 일어나지가 않습니다. 그래도 CString 객체를 만들고 부수는데 들어가는 오버헤드가 들어가겠죠. 그래서 저도 CString을 넘길 때는 reference로 넘깁니다.

      const char*에 대한 지적은 폴리비님께서 잘 해주셨네요. (PCXSTR)이라는 연산자 오버로딩이 되어있고 단순히 m_pszData를 리턴하는 것으로 구현되어있습니다.

      2007.07.22 02:00
      • 프로필사진

        이전에 잘못된 가정으로 적은 글이 계속 문제가 되어 쑥스럽네요 ^^

        저는 CString 을 그다지 좋아하지 않습니다.
        회사내 프로젝트에서 워낙 CString 을 과신하고 많이 사용해 와서 성능이나 프로그램 비정상종료 등에서 이런저런 폐해가 많았거든요..
        특히 프로젝트를 Visual C++ 6.0 에서 VS .NET 2003 으로 바꿀 당시에는 프로젝트 곳곳에서 CString 을 잘못 사용하여 비정상종료하는 일이 많아 한참 동안 잘못된 코드를 찾느라 고생했었습니다.
        CString 내부 소스가 .NET 으로 가면서 변경되면서 이전에는 죽지 않았던 코드가 죽어 나갔거든요
        물론 잘못 코딩한 개발자가 더 문제였지만, CString 오용으로 인한 프로젝트에 문제가 너무 많아서 저는 CString 보다는 일반 char* 나 STL 의 string 을 선호하는 편입니다.

        CString 의 복사 연산자가 복사의 경우 내용 복사가 일어나지 않는다는 사실은 오늘 처음 알았습니다. 말씀하신대로라면 객체의 생성과 소멸정도의 부담 외에는 큰 오버헤더는 없겠네요.

        풀리비 님이나 object 님들처럼 깊이 있는 식견을 가지신 분들이 답변 달아 주시니 제 글이 더욱 부끄러워 지는 거 같습니다. 다음부터는 좀더 잘 신경써서 포스트해야겠네요.
        댓글 감사합니다.

        2007.07.22 08:51 신고
Designed by black7375.