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

123 linker haskell ghc glfw static-linking

即使是简单的小型Haskell程序也会变成巨大的可执行文件.

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

甚至可以将一个小的Haskell程序编译成巨大的二进制文件?

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

Don*_*art 209

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

  $ 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)
    ...      
Run Code Online (Sandbox Code Playgroud)

您可以从ldd输出中看到GHC已生成动态链接的可执行文件,但只有C库是动态链接的!所有Haskell库都是逐字复制的.

旁白:因为这是一个图形密集型应用程序,我肯定会编译 ghc -O2

你可以做两件事.

剥离符号

一个简单的解决方案:剥离二进制:

$ strip A
$ du -hs A
5.8M    A
Run Code Online (Sandbox Code Playgroud)

Strip会丢弃目标文件中的符号.它们通常仅用于调试.

动态链接Haskell库

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

在撰写本文时,支持Linux和Windows.

要允许Haskell库动态链接,您需要使用它们编译它们-dynamic,如下所示:

 $ ghc -O2 --make -dynamic A.hs
Run Code Online (Sandbox Code Playgroud)

此外,您要共享的任何库都应该使用以下内容构建--enabled-shared:

 $ cabal install opengl --enable-shared --reinstall     
 $ cabal install glfw   --enable-shared --reinstall
Run Code Online (Sandbox Code Playgroud)

而且你最终会得到一个小得多的可执行文件,它可以动态地解析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...
Run Code Online (Sandbox Code Playgroud)

而且,瞧!

$ du -hs A
124K    A
Run Code Online (Sandbox Code Playgroud)

您可以剥离甚至更小:

$ strip A
$ du -hs A
84K A
Run Code Online (Sandbox Code Playgroud)

一个可爱的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 => ...
Run Code Online (Sandbox Code Playgroud)

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

  • Mac用户需要提供代码或现金. (111认同)
  • 什么时候动态链接是因为在mac上到达ghc? (7认同)
  • 这些二进制文件在这些程序之后会在其他Linux机器上运行吗? (3认同)

fuz*_*fuz 11

Haskell默认使用静态链接.也就是说,将整个OpenGL绑定复制到程序中.因为它们非常大,所以你的程序会被不必要地夸大.您可以使用动态链接解决此问题,但默认情况下不启用它.

  • 您可以动态链接库以解决此问题.不确定为什么重要的是默认,标志很简单. (5认同)
  • 问题是"你想要共享的任何库都应该用`--enabled-shared`构建",所以如果你的Haskell平台带有没有`--enabled shared`构建的库,你必须重新编译可以的基础库.很痛苦. (3认同)