用GHC编译的小Haskell程序变成了巨大的二进制文件

即使是小的Haskell程序也变成了巨大的可执行文件。

我已经编写了一个小程序,这个程序是用GHC编译的,大小为7MB。

什么可以导致一个小的Haskell程序被编译成巨大的二进制文件?

如果有的话,我能做些什么来减less这种情况?

让我们看看发生了什么,试试

  $ 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) ... 

您从ldd输出中看到,GHC已经生成了一个dynamic链接的可执行文件,但只有C库是dynamic链接的 ! 所有的Haskell库都是逐字拷贝的。

另外:因为这是一个graphics密集型的应用程序,我一定会用ghc -O2编译

有两件事你可以做。

剥离符号

一个简单的解决scheme:去掉二进制文件

 $ strip A $ du -hs A 5.8MA 

带从目标文件丢弃符号。 它们通常只用于debugging。

dynamic链接的Haskell库

最近,GHC已经获得了对C和Haskell库的dynamic链接的支持。 大多数发行版现在分发一个GHC版本,用于支持Haskell库的dynamic链接。 共享的Haskell库可以在许多Haskell程序之间共享,而不需要每次将它们复制到可执行文件中。

在编写的时候,Linux和Windows都支持。

为了让Haskell库dynamic链接,你需要用-dynamic来编译它们,就像这样:

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

另外,任何你想共享的库都应该使用--enabled-shared来构build:

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

最后你会得到一个小得多的可执行文件,它具有dynamic解决的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, Ao ) Linking A... 

而且,瞧!

 $ du -hs A 124K A 

你可以剥下更小的:

 $ strip A $ du -hs A 84K A 

一个eensy weensy可执行文件,由许多dynamic链接的C和Haskell构件组成:

 $ 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 => ... 

最后一点:即使在只有静态链接的系统上,也可以使用-split-objs ,为每个顶层函数获得一个.o文件,这可以进一步减less静态链接库的大小。 它需要使用-split-objs来构buildGHC,有些系统会忘记这样做。

Haskell默认使用静态链接。 这是,整个绑定到OpenGL被复制到您的程序。 因为它们相当大,你的程序会不必要的膨胀。 您可以通过使用dynamic链接来解决这个问题,尽pipe默认情况下它没有启用。