用GHC编译非常大的常量

今天,我要求GHC编译一个8MB的Haskell源文件。 GHC想了大约6分钟,吞下了近2GB的内存,然后最终放弃了内存不足的错误。

[顺便说一下,我很高兴GHC有理由放弃而不是整个电脑。]

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

(最奇怪的是,如果你只是read数据,它实际上并不需要太多的时间和内存。奇怪的是,考虑到String I / O和read都被认为是非常低效的......)

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

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

起初,我认为GHC可能不满意阅读由800万字符长的一行组成的模块。 (!!)与布局规则等有关。 或者也许这种深深嵌套的表情让它感到不安。 但我试图让每个子表达式都成为顶级标识符,这没有任何帮助。 (然而,向每个人添加明确的类型签名确实使编译器稍微高兴一些。)还有什么可以尝试使编译器的工作更简单吗?

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


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

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


我试着做类似的东西来存储实际的常量,但是因为将值嵌入到.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。


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

链接地址: http://www.djcxy.com/p/1153.html

上一篇: Compiling very large constants with GHC

下一篇: Why Haskell uses bottom instead of null in partial functions?