【问题标题】:Small Haskell program compiled with GHC into huge binary用 GHC 编译成巨大二进制文件的小型 Haskell 程序
【发布时间】:2011-09-01 05:00:49
【问题描述】:

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

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

什么会导致即使是一个小的 Haskell 程序也被编译成巨大的二进制文件?

如果有的话,我可以做些什么来减少这种情况?

【问题讨论】:

  • 你试过只是剥离它吗?
  • 在二进制文件上运行程序strip以删除符号表。
  • @tm1rbt:运行strip test。此命令从程序中删除一些调试信息并使其更小。
  • 顺便说一句,出于性能原因,您在 3D 数学库中的数据类型应该更严格:data M3 = M3 !V3 !V3 !V3 和 data V3 = V3 !Float !Float !Float。使用ghc -O2 -funbox-strict-fields编译。
  • 这篇文章在meta讨论。

标签: haskell linker ghc static-linking glfw


【解决方案1】:

让我们看看是怎么回事,试试

  $ 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 获得了对dynamic linking of both C and Haskell libraries 的支持。大多数发行版现在分发一个 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 => ...

最后一点:即使在只有静态链接的系统上,您也可以use -split-objs,为每个顶级函数获取一个 .o 文件,这可以进一步减少静态链接库的大小。它需要使用 -split-objs 构建 GHC,而某些系统忘记了这样做。

【讨论】:

  • mac 上的 ghc 动态链接何时到达?
  • ...cabal install 默认不剥离已安装的二进制文件?
  • 在 Windows 上这样做似乎会使生成的文件无法运行,它抱怨缺少 libHSrts-ghc7.0.3.dll
  • 在这些程序之后,这个二进制文件是否可以在其他 Linux 机器上运行?
  • 嗨,OP 从 2011 年开始!我来自未来,可以说 Ubuntu 16.04 上的 pandoc 可执行文件有 50MB 的大小,并且不会基于 packages.ubuntu.com/zesty/pandoc 进行更改。给近期的自己和其他人的信息:联系包维护者并询问是否考虑了enable-shared。 launchpad.net/ubuntu/+source/pandoc/+bugs
【解决方案2】:

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

【讨论】:

  • 您可以动态链接库来解决这个问题。不知道为什么默认值很重要,标志很简单。
  • 问题是“你想要共享的任何库都应该用--enabled-shared构建”所以如果你的Haskell平台带有没有--enabled shared构建的库,你必须重新编译可以会很痛苦。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-14
  • 1970-01-01
  • 2011-11-28
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多