IT story

GHC로 컴파일 된 스몰 하스켈 프로그램

hot-time 2020. 7. 13. 07:59
반응형

GHC로 컴파일 된 스몰 하스켈 프로그램


사소한 작은 Haskell 프로그램조차도 거대한 실행 파일로 바뀝니다.

나는 작은 프로그램을 작성했는데 (GHC로) 7MB 크기의 바이너리로 컴파일되었습니다!

작은 Haskell 프로그램조차도 거대한 바이너리로 컴파일 할 수있는 원인은 무엇입니까?

이것을 줄이려면 어떻게해야합니까?


무슨 일이 일어나고 있는지 보자

  $ du -hs A
  13M   A

  $ file A
  A: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), 
     dynamically linked (uses shared libs), for GNU/Linux 2.6.27, not stripped

  $ ldd A
    linux-vdso.so.1 =>  (0x00007fff1b9ff000)
    libXrandr.so.2 => /usr/lib/libXrandr.so.2 (0x00007fb21f418000)
    libX11.so.6 => /usr/lib/libX11.so.6 (0x00007fb21f0d9000)
    libGLU.so.1 => /usr/lib/libGLU.so.1 (0x00007fb21ee6d000)
    libGL.so.1 => /usr/lib/libGL.so.1 (0x00007fb21ebf4000)
    libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007fb21e988000)
    libm.so.6 => /lib/libm.so.6 (0x00007fb21e706000)
    ...      

당신은에서 볼 lddGHC는 동적으로 링크 된 실행 파일을 생산하고 있지만, 출력 에만 C 라이브러리를 동적으로 연결되어 있습니다 ! 모든 Haskell 라이브러리는 그대로 복사됩니다.

따로 : 이것은 그래픽 집약적 인 응용 프로그램이기 때문에 분명히 컴파일 할 것입니다. ghc -O2

당신이 할 수있는 두 가지가 있습니다.

스트리핑 기호

쉬운 해결책 : 바이너리를 제거하십시오.

$ strip A
$ du -hs A
5.8M    A

Strip은 오브젝트 파일에서 심볼을 버립니다. 일반적으로 디버깅에만 필요합니다.

동적으로 연결된 Haskell 라이브러리

보다 최근에 GHC는 C 및 Haskell 라이브러리의 동적 연결을 지원했습니다 . 대부분의 배포판은 이제 Haskell 라이브러리의 동적 연결을 지원하도록 구축 된 GHC 버전을 배포합니다. 공유 Haskell 라이브러리는 매번 실행 파일에 복사하지 않고 많은 Haskell 프로그램간에 공유 될 수 있습니다.

작성 당시 Linux 및 Windows가 지원됩니다.

Haskell 라이브러리를 동적으로 링크하려면 다음과 -dynamic같이 컴파일해야합니다 .

 $ ghc -O2 --make -dynamic A.hs

또한 공유하려는 라이브러리는 다음을 사용하여 빌드해야합니다 --enabled-shared.

 $ cabal install opengl --enable-shared --reinstall     
 $ cabal install glfw   --enable-shared --reinstall

그리고 C와 Haskell 종속성이 동적으로 해결되는 훨씬 작은 실행 파일이 생깁니다.

$ ghc -O2 -dynamic A.hs                         
[1 of 4] Compiling S3DM.V3          ( S3DM/V3.hs, S3DM/V3.o )
[2 of 4] Compiling S3DM.M3          ( S3DM/M3.hs, S3DM/M3.o )
[3 of 4] Compiling S3DM.X4          ( S3DM/X4.hs, S3DM/X4.o )
[4 of 4] Compiling Main             ( A.hs, A.o )
Linking A...

그리고!

$ du -hs A
124K    A

which you can strip to make even smaller:

$ strip A
$ du -hs A
84K A

An eensy weensy executable, built up from many dynamically linked C and Haskell pieces:

$ ldd A
    libHSOpenGL-2.4.0.1-ghc7.0.3.so => ...
    libHSTensor-1.0.0.1-ghc7.0.3.so => ...
    libHSStateVar-1.0.0.0-ghc7.0.3.so =>...
    libHSObjectName-1.0.0.0-ghc7.0.3.so => ...
    libHSGLURaw-1.1.0.0-ghc7.0.3.so => ...
    libHSOpenGLRaw-1.1.0.1-ghc7.0.3.so => ...
    libHSbase-4.3.1.0-ghc7.0.3.so => ...
    libHSinteger-gmp-0.2.0.3-ghc7.0.3.so => ...
    libHSghc-prim-0.2.0.0-ghc7.0.3.so => ...
    libHSrts-ghc7.0.3.so => ...
    libm.so.6 => /lib/libm.so.6 (0x00007ffa4ffd6000)
    librt.so.1 => /lib/librt.so.1 (0x00007ffa4fdce000)
    libdl.so.2 => /lib/libdl.so.2 (0x00007ffa4fbca000)
    libHSffi-ghc7.0.3.so => ...

One final point: even on systems with static linking only, you can use -split-objs, to get one .o file per top level function, which can further reduce the size of statically linked libraries. It needs GHC to be built with -split-objs on, which some systems forget to do.


Haskell uses static linking by default. This is, the whole bindings to OpenGL are copied into your program. As they are quite big, your program gets unnecessarily inflated. You can work around this by using dynamic linking, although it isn't enabled by default.

참고URL : https://stackoverflow.com/questions/6115459/small-haskell-program-compiled-with-ghc-into-huge-binary

반응형