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


127

甚至很小的Haskell程序也变成了巨大的可执行文件。

我编写了一个小程序,该程序(使用GHC)编译为二进制文件,大小扩展了7 MB!

哪怕是很小的Haskell程序也可以编译成巨大的二进制文件?

我可以做些什么来减少这种情况?


2
您是否尝试过剥离?
弗雷德·富

21
strip在二进制文件上运行程序以删除符号表。
弗雷德·富

1
@ tm1rbt:运行strip test。此命令从程序中删除一些调试信息,并使其更小。
2011年

8
顺便说一句,由于性能原因data M3 = M3 !V3 !V3 !V3,您在3D数学库中的数据类型应更严格:和data V3 = V3 !Float !Float !Float。用编译ghc -O2 -funbox-strict-fields
唐·斯图尔特

8
这个帖子在meta上讨论。
Patrick Hofman 2014年

Answers:


215

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

  $ 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库的动态链接的支持。现在,大多数发行版都发行了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

一个讨厌的可执行文件,由许多动态链接的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,有些系统会忘记这样做。


7
动态链接何时会在Mac上到达ghc?
卡特·塔齐奥·雄瓦尔德

1
... cabal install默认情况下不会剥离已安装的二进制文件吗?
hvr

1
在Windows上这样做似乎会使生成的文件无法运行,它抱怨缺少libHSrts-ghc7.0.3.dll
is7s 2011年

3
完成这些步骤后,该二进制文件将在其他Linux机器上运行吗?
アレックス

1
您好,2011年开始!我来自未来,可以告诉我们Ubuntu 16.04上的pandoc可执行文件有50MB的存储空间,并且不会根据packages.ubuntu.com/zesty/pandoc进行更改。给即将来临的自己和其他人的消息:与软件包维护者联系,询问是否enable-shared考虑过。launchpad.net/ubuntu/+source/pandoc/+bugs
斯特凡纳·古里科

11

Haskell默认使用静态链接。也就是说,将与OpenGL的整个绑定复制到您的程序中。由于它们很大,因此程序不必要地膨胀。您可以通过使用动态链接来解决此问题,尽管默认情况下未启用它。


5
您可以动态链接库来解决此问题。不确定默认值为何重要,该标志足够简单。
Thomas M. DuBuisson,2011年

4
问题是“您要共享的任何库都应使用--enabled-shared” 来构建,因此,如果Haskell平台附带的库是在无需--enabled shared重新编译基本库的情况下构建的,那可能会很麻烦。
nponeccop 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.