即使是微不足道的Haskell程序也会变成巨大的可执行文件。
我编写了一个小程序,使用GHC编译后,大小扩展到7 MB!是什么原因导致即使是小的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
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
您可以剥离它以使其更小:
$ strip A
$ du -hs A
84K A
一个非常小的可执行文件,由许多动态链接的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,但某些系统会忘记这样做。
cabal install
不会剥离已安装的二进制文件吗? - hvrldd
命令的是 otool -L
。 - James McMahonHaskell默认使用静态链接。这意味着OpenGL的所有绑定都会被复制到您的程序中。由于它们非常庞大,您的程序会不必要地膨胀。您可以通过使用动态链接来解决此问题,尽管它并不是默认启用的。
strip
з®ЛеЇПдї•еИ†йЩ§дЇМињЫеИґжЦЗдїґдЄ≠зЪДзђ¶еПЈи°®гАВ - Fred Foostrip test
。该命令会从程序中移除一些调试信息,使其变得更小。 - fuzdata M3 = M3 !V3 !V3 !V3
和data V3 = V3 !Float !Float !Float
。编译时请使用ghc -O2 -funbox-strict-fields
。 - Don Stewart