用GHC编译成的小Haskell程序变成了巨大的二进制文件
即使是普通的小型Haskell程序也会变成巨大的可执行文件。
我写了一个小程序,它被编译(使用GHC)到大小为7 MB的二进制文件!
什么会导致一个小的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)
...
您从ldd
输出中看到GHC已生成动态链接的可执行文件,但只有C库被动态链接! 所有的Haskell库都是逐字拷贝的。
另外:因为这是一个图形密集型应用程序,我一定会用ghc -O2
编译
有两件事你可以做。
剥离符号
简单的解决方案:去掉二进制文件:
$ strip A
$ du -hs A
5.8M A
条带丢弃来自目标文件的符号。 它们通常只用于调试。
动态链接的Haskell库
最近,GHC已经获得了对C和Haskell库的动态链接的支持。 大多数发行版现在分发构建的GHC版本,以支持Haskell库的动态链接。 共享的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
你可以剥下更小的:
$ strip A
$ du -hs A
84K A
一个eensy weensy可执行文件,由许多动态链接的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文件,这可以进一步减少静态链接库的大小。 它需要使用-split-objs来构建GHC,而某些系统会忘记这样做。
Haskell默认使用静态链接。 这是,整个绑定到OpenGL被复制到您的程序。 由于它们相当大,你的程序会被不必要地夸大。 您可以通过使用动态链接解决此问题,但默认情况下它未启用。
链接地址: http://www.djcxy.com/p/5689.html上一篇: Small Haskell program compiled with GHC into huge binary