为什么Haskell / GHC可执行文件在文件大小上如此之大?
可能重复:
用GHC编译成的小Haskell程序变成了巨大的二进制文件
最近我注意到Haskell可执行文件的大小。 下面的所有内容都是在GHC 7.4.1上用Linux上的-O2
编译的。
Hello World( main = putStrLn "Hello World!"
)超过800 KiB。 在其上运行strip
可将文件大小减少到500 KiB; 即使在编译时添加-dynamic
编译也没有什么帮助,只剩下大约400 KiB的可执行文件。
编译一个涉及Parsec的非常原始的例子会产生一个1.7 MiB文件。
-- File: test.hs
import qualified Text.ParserCombinators.Parsec as P
import Data.Either (either)
-- Parses a string of type "x y" to the tuple (x,y).
testParser :: P.Parser (Char, Char)
testParser = do
a <- P.anyChar
P.char ' '
b <- P.anyChar
return (a, b)
-- Parse, print result.
str = "1 2"
main = print $ either (error . show) id . P.parse testParser "" $ str
-- Output: ('1','2')
Parsec可能是一个更大的库,但我只使用它的一小部分,事实上上面生成的优化核心代码比可执行文件要小得多:
$ ghc -O2 -ddump-simpl -fforce-recomp test.hs | wc -c
49190 (bytes)
因此,在程序中实际找到大量的Parsec并不是这种情况,这是我最初的假设。
为什么这样庞大的可执行文件? 有什么我可以做的(除了动态链接)?
为了有效减小格拉斯哥Haskell编译器生成的可执行文件的大小,您必须关注
-dynamic
选项的动态链接,所以模块代码不会通过使用共享(动态)库而被捆绑到最终的可执行文件中。 系统中这些GHC库的共享版本的存在是必需的! 简单的hello world示例的最终大小为9 KiB,Parsec测试的大小为28 KiB(均为64位Linux可执行文件),我发现它非常小并且可以用于这样的高级语言实现。
我的理解是,如果您使用包X中的单个函数,整个包会静态链接。我不认为GHC实际上按函数链接。 (除非你使用“拆分对象”破解,“这往往会让连接器失控”)。
但是,如果你动态链接,那应该解决这个问题。 所以我不确定在这里提出什么建议...
(我敢肯定,当动态链接第一次出现时,我看到了一篇博客文章,展示了Hello World编译为2KB二进制文件。显然,我现在找不到这篇博客文章...... grr。)
考虑跨模块优化。 如果您正在编写Parsec解析器,GHC很可能会将所有解析器定义内联并将其简化为最高效的代码。 当然,你的几行Haskell产生了50KB的Core。 编译为机器码时,它应该增大37倍吗? 我不知道。 您也许可以尝试查看下一步中生成的STG和Cmm代码。 (对不起,我不记得编译器标志关闭了我的头顶...)
链接地址: http://www.djcxy.com/p/33285.html