可能重复:
Small Haskell program compiled with GHC into huge binary
最近我注意到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,这是我最初的假设。
为什么这么大的可执行文件?我能做些什么(动态链接除外)?
答案 0 :(得分:12)
要有效减少格拉斯哥Haskell编译器生成的可执行文件的大小,您必须关注
-dynamic
选项的动态链接,因此模块代码不会通过利用共享(动态)库捆绑到最终的可执行文件中。需要在系统中存在这些GHC库的共享版本!简单的hello world示例的最终大小为9 KiB,Parsec测试大约为28 KiB(均为64位Linux可执行文件),我认为这种语言很小,可以接受这种高级语言实现。
答案 1 :(得分:3)
我的理解是,如果您使用包X中的单个函数,整个包将静态链接。我不认为GHC实际上是逐个函数链接。 (除非你使用“拆分对象”黑客攻击,否则“会使链接器出现问题”。)
但如果你是动态链接,那应该解决这个问题。所以我不确定在这里建议什么...
(我很确定当动态链接首次出现时,我看到了一篇博文,证明Hello World编译为2KB二进制文件。显然我找不到这篇博文现在 ... grr。 )
还要考虑跨模块优化。如果您正在编写Parsec解析器,GHC可能会内联所有解析器定义并将其简化为最有效的代码。而且,当然,你的几行Haskell已经产生了50KB的Core。在编译为机器代码时,它应该增加37倍吗?我不知道。您也许可以尝试查看后续步骤中生成的STG和Cmm代码。 (对不起,我不记得头顶的编译器标志......)