레이블이 성능비교인 게시물을 표시합니다. 모든 게시물 표시
레이블이 성능비교인 게시물을 표시합니다. 모든 게시물 표시

2009년 9월 10일 목요일

C/C++에서 가독성을 희생하는 최적화는 의미없더라

피보나찌 수열의 합을 구하는 포스팅에 JAFO님께서 쓰신 답글에 제목과 같은 내용의 글이 올라왔다.
사실 이 얘기는 컴파일러의 입장에서 생각해보면 어쩌면 당연한 얘기다. 아님 말고.

아무튼, 이 뻔해보이는 내용을 컴파일러에서 어셈블리 코드로 뽑아서 확인해봤다. (Visual C++ 2003.Net 사용)
전투에 사용된 코드는 아래의 두 선수들.

  int f1=1, f2=2, f3=f1+f2;
  int iSum=0;
  while (f2<4000001)
  {
      iSum += f2;
      _tprintf(_T("%d -> %d\n"), f2, iSum);
      // 다음 3개의 항을 구함
      f1 = f2 + f3;
      f2 = f1 + f3;
      f3 = f1 + f2;
  }



  int f1=1, f2=2, f3=f1+f2, iSum=0;
  while (f2<4000001)
  {
      _tprintf(_T("%d -> %d\n"), f2, iSum+=f2);
      f3 = f1 + (f2 = (f1 = f2 + f3) + f3);
  }

다.

컴파일 된 결과는 각각 아래와 같다. (소스에서 색칠한 부분의 컴파일 결과만 발췌)


; 14   :         // 다음 3개의 항을 구함
; 15   :         f1 = f2 + f3;

    lea    eax, DWORD PTR [edi+esi]

; 16   :         f2 = f1 + f3;

    lea    esi, DWORD PTR [edi+eax]
    add    esp, 12                    ; 0000000cH
    cmp    esi, 4000001                ; 003d0901H

; 17   :         f3 = f1 + f2;

    lea    edi, DWORD PTR [esi+eax]
    jl    SHORT $L9627
    pop    edi
    pop    esi



; 12   :         f3 = f1 + (f2 = (f1 = f2 + f3) + f3);

    lea    eax, DWORD PTR [edi+esi]
    lea    esi, DWORD PTR [edi+eax]
    add    esp, 12                    ; 0000000cH
    cmp    esi, 4000001                ; 003d0901H
    lea    edi, DWORD PTR [esi+eax]
    jl    SHORT $L9627
    pop    edi
    pop    esi

다.

봐서 알 수 있듯이 똑같다.
마지막 3 행은 for() 루프의 마지막 부분이라 붙은 것인데, 이 내용까지 똑같다.

즉, 성능의 향상은 0.001%도 없으니, 혼자 만들고 읽을 코드가 아니라면 적절한 가독성을 갖도록 작성하는 것이 상책이다.

2009년 9월 6일 일요일

VS 2003.net에서 jpeglib 컴파일하는 방법

도저히 이해할 수 없는 Visual C++ 6.0의 성능에서 열변을 토한 이후 jpeglib를 VS.Net 2003에서 static library로 컴파일할 방법을 열심히 찾아봤다.
(간단한 거 만들 땐 역시 static library를 사용해서 파일 하나로 만들어야 제맛이다)

궁하면 통한다고 드디어 발견. Compiling the viewer libraries (MSVS 2003)라는 금쪽같은 포스트를 발견했다.
하지만, 현재 jpeglib의 최신버전은 7이고, 이 글에서 설명한 것은 6b라는 거.
별달리 크게 바뀐 건 없으니 그대로 따라하면 되더라.

.Net 2003에서 컴파일해서 libjpeg.lib 파일을 만드는 순서는 아래와 같다.


1. 다운받기

jpegsr7.zip을 다운받는다.
위 글의 설명에 보면 .tar.gz 파일을 받으라고 되어있는데, 아마 7부터 zip로도 배포하나보다.

다 받았으면 알아서 압축 푼다.



2. jpeglib.h 파일 수정

6b엔 몇 군데 고쳐야 되었던 것 같은데, 7에선 그닥 고칠 게 없다.
맨 앞부분에 아래와 같은 내용을 추가하면 끝.

#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#endif



3. 파일명 변경

makefile.vcMakefile로, jconfig.vcjconfig.h 로 각각 바꿔준다.



4. 대망의 컴파일

Visual Studio .NET 2003 명령 프롬프트에서 작업 디렉토리로 이동하여 컴파일한다.
nmake -f Makefile 를 입력하면 알아서 libjpeg.lib를 만들어준다.



5. 자신의 프로그램에 연결

원본 폴더에서 jconfig.h, jerror.h, jmorecfg.h, jpeglib.h, jinclude.hlibjpeg.lib 작업 폴더로 복사한다.
그리고는 libjpeg.lib를 링크시키고, 아래와 같이 jpeglib.h를 포함시키면 끝.

#include "jpeglib.h"


하지만, 이번에도 시궁창같은 현실에 직면함.

원래 굳이 잘 돌아가는 VS6 버리고 이걸 하려던 이유가 리샘플 성능 때문이었다.
똑같은 코드인데 VS.Net 2003에서 훨 빠른 거 보고 VS6을 버리고 싶어졌다.

하지만, 막상 다 모아서 돌려보니 VS.Net 2003쪽이 훨씬 느리다.
리샘플링은 더 빠를텐데, jpeg 파일 읽고 쓰는 부분이 훨 느린가보다. OTL
그런다고 내가 jpeglib를 최적화할 수도 없는 노릇이고.

덕분에 JPEG Shrinker 2.2는 세상에 나와보지도 못하고 어둠 속에 갇히는 신세가 되었다. 지못미.

사용자 삽입 이미지

결국 이 놈은 하루에 걸친 삽질에도 불구하고 아래와 같은 단점만 충실히 보유한 괴작이 되었다.

1. 2.1보다 훨씬 큰 크기

UPX로 압축한 결과가 2.1 대비 160% 이상의 파일 사이즈.
사실 이건 MFC library를 static으로 컴파일 것이 주원인이다
(쬐만한 프로그램 하나 주면서 몇십MB짜리 런타임도 받으라고 할 수는 없잖아)

2. 2.1보다 훨씬 느린 속도

똑같은 코드인지라 100% 동일하게 동작하지만, 기대와는 달리 VS.Net 2003 쪽이 VS6보다 훨 느리다.


앞에서와 달리 종합성능에선 VS.Net이 VS6에게 무참하게 짓밟힘. 읽고 쓰는데에만 시간이 2배로 걸리다니. OTL (단위:초)