用 GHC 编译成巨大的二进制文件的小型 Haskell 程序
即使是很小的 Haskell 程序也会变成巨大的可执行文件。
我编写了一个小程序,它被编译(使用 GHC)为二进制文件,大小扩展为 7 MB!
什么会导致即使是一个小的 Haskell 程序也被编译成巨大的二进制文件?
如果有的话,我可以做什么来减少这种情况?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
让我们看看发生了什么,尝试
从
ldd
输出中看到,GHC 已生成动态链接的可执行文件,但只有 C 库是动态链接的!所有 Haskell 库均逐字复制。旁白:由于这是一个图形密集型应用程序,我肯定会使用
ghc -O2
进行编译您可以做两件事。
剥离符号
一个简单的解决方案:剥离二进制文件:
剥离会丢弃目标文件中的符号。它们通常仅用于调试。
动态链接的 Haskell 库
最近,GHC 获得了对 C 和 Haskell 库的动态链接。大多数发行版现在都分发一个 GHC 版本,该版本是为了支持 Haskell 库的动态链接而构建的。共享 Haskell 库可以在许多 Haskell 程序之间共享,而无需每次都将它们复制到可执行文件中。
在撰写本文时,支持 Linux 和 Windows。
要允许动态链接 Haskell 库,您需要使用
-dynamic
编译它们,如下所示:此外,您想要共享的任何库都应该使用
--enabled-shared 构建
:您最终会得到一个更小的可执行文件,它动态解析了 C 和 Haskell 依赖项。
而且,瞧!
您可以将其剥离以使其更小:
一个非常小的可执行文件,由许多动态链接的 C 和 Haskell 片段构建而成:
最后一点:即使在仅具有静态链接的系统上,您也可以 使用-split-objs,为每个顶级函数获取一个.o文件,这可以进一步减少静态链接库的大小。它需要使用 -split-objs 来构建 GHC,但有些系统忘记了这样做。
Let's see what's going on, try
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.Aside: since this is a graphics-intensive app, I'd definitely compile with
ghc -O2
There's two things you can do.
Stripping symbols
An easy solution: strip the binary:
Strip discards symbols from the object file. They are generally only needed for debugging.
Dynamically linked Haskell libraries
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.
At the time of writing Linux and Windows are supported.
To allow the Haskell libraries to be dynamically linked, you need to compile them with
-dynamic
, like so:Also, any libraries you want to be shared should be built with
--enabled-shared
:And you'll end up with a much smaller executable, that has both C and Haskell dependencies dynamically resolved.
And, voilà!
which you can strip to make even smaller:
An eensy weensy executable, built up from many dynamically linked C and Haskell pieces:
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.
Haskell 默认使用静态链接。也就是说,与 OpenGL 的整个绑定都会复制到您的程序中。由于它们相当大,您的程序会不必要地膨胀。您可以通过使用动态链接来解决此问题,尽管默认情况下未启用它。
Haskell uses static linking by default. This is, the whole bindings to OpenGL are copied into your program. 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.