用 GHC 编译成巨大的二进制文件的小 Haskell 程序 [英] Small Haskell program compiled with GHC into huge binary

查看:26
本文介绍了用 GHC 编译成巨大的二进制文件的小 Haskell 程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Even trivially small Haskell programs turn into gigantic executables.

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

I've written a small program, that was compiled (with GHC) to the binary with the size extending 7 MB!

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

What can cause even a small Haskell program to be compiled to the huge binary?

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

What, if anything, can I do to reduce this?

推荐答案

看看怎么回事,试试

  $ 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 库都是逐字复制的.

You see from the ldd output that GHC has produced a dynamically linked executable, but only the C libraries are dynamically linked! All the Haskell libraries are copied in verbatim.

另外:由于这是一个图形密集型应用程序,我肯定会使用 ghc -O2

您可以做两件事.

剥离符号

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

An easy solution: strip the binary:

$ strip A
$ du -hs A
5.8M    A

Strip 从目标文件中丢弃符号.它们通常只用于调试.

Strip discards symbols from the object file. They are generally only needed for debugging.

动态链接的 Haskell 库

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

More recently, GHC has gained support for dynamic linking of both C and Haskell libraries. Most distros now distribute a version of GHC built to support dynamic linking of Haskell libraries. Shared Haskell libraries may be shared amongst many Haskell programs, without copying them into the executable each time.

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

At the time of writing Linux and Windows are supported.

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

To allow the Haskell libraries to be dynamically linked, you need to compile them with -dynamic, like so:

 $ ghc -O2 --make -dynamic A.hs

此外,您想要共享的任何库都应使用 --enabled-shared 构建:

Also, any libraries you want to be shared should be built with --enabled-shared:

 $ cabal install opengl --enable-shared --reinstall     
 $ cabal install glfw   --enable-shared --reinstall

您最终会得到一个更小的可执行文件,它同时动态解析了 C 和 Haskell 依赖项.

And you'll end up with a much smaller executable, that has both C and Haskell dependencies dynamically resolved.

$ 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

您可以将其剥离以使其更小:

which you can strip to make even smaller:

$ strip A
$ du -hs A
84K A

一个 teensy 可执行文件,由许多动态链接的 C 和 Haskell 部分构建:

An eensy weensy executable, built up from many dynamically linked C and Haskell pieces:

$ 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,有些系统忘记这样做.


One final point: even on systems with static linking only, you can use -split-objs, to get one .o file per top level function, which can further reduce the size of statically linked libraries. It needs GHC to be built with -split-objs on, which some systems forget to do.

这篇关于用 GHC 编译成巨大的二进制文件的小 Haskell 程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆