24

今天我让 GHC 编译了一个 8MB 的 Haskell 源文件。GHC 考虑了大约 6 分钟,吞噬了将近 2GB 的 RAM,然后最终以 out-of-memory 错误放弃。

[顺便说一句,我很高兴 GHC 有很好的判断来中止而不是让我的整个 PC 瘫痪。]

基本上,我有一个程序可以读取文本文件,进行一些花哨的解析,构建数据结构,然后将show其转储到文件中。我不想将整个解析器和源数据包含在我的最终应用程序中,而是将生成的数据作为编译时常量包含在内。通过在 的输出中添加一些额外的东西show,你可以使它成为一个有效的 Haskell 模块。但是 GHC 显然不喜欢编译多 MB 的源文件。

(最奇怪的部分是,如果您只是read返回数据,它实际上并不需要太多时间或内存。奇怪的是,考虑到StringI/O 和read据说都非常低效......)

我隐约记得过去其他人在让 GHC 编译大文件时遇到了麻烦。FWIW,我尝试使用-O0,它加速了崩溃但并没有阻止它。那么在 Haskell 程序中包含大型编译时常量的最佳方法是什么?

(在我的例子中,常量只是嵌套Data.Map了一些有趣的标签。)

最初,我认为 GHC 可能只是不喜欢阅读由一行 800 万字符组成的模块。(!!)与布局规则等有关。又或许是深藏不露的表情让它心烦意乱。但是我尝试让每个子表达式都成为顶级标识符,但这并没有帮助。(但是,为每个类型添加显式类型签名确实似乎使编译器更快乐。)还有什么我可以尝试使编译器的工作更简单的吗?

最后,我能够使我实际上试图存储的数据结构更小。(比如,300KB。)这让 GHC 更快乐。(最终应用程序要快得多。)但为了将来参考,我很想知道解决这个问题的最佳方法是什么。

4

2 回答 2

5

您最好的选择可能是将您的值的字符串表示形式编译到可执行文件中。要以干净的方式执行此操作,请参阅我在上一个问题中的回答

要使用它,只需将您的表达式存储在其中myExpression.exp并启用扩展名,该表达式将“存储为字符串文字”在可执行文件中。read [litFile|myExpression.exp|]QuasiQuotes


我尝试做类似的事情来存储实际常量,但它失败的原因与将值嵌入.hs文件中的原因相同。我的尝试是:

Verbatim.hs

module Verbatim where

import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Language.Haskell.Meta.Parse

readExp :: String -> Q Exp
readExp = either fail return . parseExp

verbatim :: QuasiQuoter
verbatim = QuasiQuoter { quoteExp = readExp }

verbatimFile :: QuasiQuoter
verbatimFile = quoteFile verbatim

测试程序:

{-# LANGUAGE QuasiQuotes #-}
module Main (main) where

import Verbatim

main :: IO ()
main = print [verbatimFile|test.exp|]

该程序适用于小test.exp文件,但在这台计算机上大约 2MiB 时已经失败。

于 2013-05-03T14:29:48.340 回答
1

有一个简单的解决方案——你的文字应该有 type ByteString。有关详细信息,请参阅https://github.com/litherum/publicsuffixlist/pull/1

于 2013-05-16T09:47:13.223 回答