简体   繁体   English

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

[英]Small Haskell program compiled with GHC into huge binary

Even trivially small Haskell programs turn into gigantic executables.即使是很小的 Haskell 程序也会变成巨大的可执行文件。

I've written a small program, that was compiled (with GHC) to the binary with the size extending 7 MB!我编写了一个小程序,它被编译(使用 GHC)为大小扩展为 7 MB 的二进制文件!

What can cause even a small Haskell program to be compiled to the huge binary?是什么导致即使是小的 Haskell 程序也被编译成巨大的二进制文件?

What, if anything, can I do to reduce this?如果有的话,我能做些什么来减少这种情况?

Let's see what's going on, try让我们看看发生了什么,试试

  $ 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)
    ...      

You see from the ldd output that GHC has produced a dynamically linked executable, but only the C libraries are dynamically linked .您从ldd output 看到 GHC 已经生成了动态链接的可执行文件,但只有 C 库是动态链接的 All the Haskell libraries are copied in verbatim.所有 Haskell 库都逐字复制。

Aside: since this is a graphics-intensive app, I'd definitely compile with ghc -O2另外:因为这是一个图形密集型应用程序,我肯定会用ghc -O2编译

There's two things you can do.你可以做两件事。

Stripping symbols剥离符号

An easy solution: strip the binary:一个简单的解决方案:剥离二进制文件:

$ strip A
$ du -hs A
5.8M    A

Strip discards symbols from the object file. Strip 丢弃 object 文件中的符号。 They are generally only needed for debugging.它们通常仅用于调试。

Dynamically linked Haskell libraries动态链接 Haskell 库

More recently, GHC has gained support for dynamic linking of both C and Haskell libraries .最近,GHC 获得了对 C 和 Haskell 库的动态链接的支持。 Most distros now distribute a version of GHC built to support dynamic linking of Haskell libraries.大多数发行版现在都发布了一个 GHC 版本,它支持 Haskell 库的动态链接。 Shared Haskell libraries may be shared amongst many Haskell programs, without copying them into the executable each time.共享的 Haskell 库可以在许多 Haskell 程序之间共享,而无需每次都将它们复制到可执行文件中。

At the time of writing Linux and Windows are supported.在撰写本文时,支持 Linux 和 Windows。

To allow the Haskell libraries to be dynamically linked, you need to compile them with -dynamic , like so:要允许动态链接-dynamic库,您需要使用 -dynamic 编译它们,如下所示:

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

Also, any libraries you want to be shared should be built with --enabled-shared :此外,您要共享的任何库都应使用--enabled-shared构建:

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

And you'll end up with a much smaller executable, that has both C and Haskell dependencies dynamically resolved.你最终会得到一个更小的可执行文件,它同时动态解析了 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...

And, voilà!而且,瞧!

$ du -hs A
124K    A

which you can strip to make even smaller:您可以将其剥离以使其更小:

$ strip A
$ du -hs A
84K A

An eensy weensy executable, built up from many dynamically linked C and Haskell pieces:一个小巧的可执行文件,由许多动态链接的 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 => ...

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.最后一点:即使在仅使用 static 链接的系统上,您也可以使用 -split- objs 为每个顶级 function 获取一个.o 文件,这可以进一步减小静态链接库的大小。 It needs GHC to be built with -split-objs on, which some systems forget to do.它需要使用 -split-objs 构建 GHC,而某些系统忘记了这样做。

Haskell uses static linking by default. Haskell 默认使用 static 链接。 This is, the whole bindings to OpenGL are copied into your program.也就是说,与 OpenGL 的整个绑定都将复制到您的程序中。 As they are quite big, your program gets unnecessarily inflated.由于它们很大,您的程序会不必要地膨胀。 You can work around this by using dynamic linking, although it isn't enabled by default.您可以通过使用动态链接来解决此问题,尽管默认情况下未启用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM