Подтвердить что ты не робот

Компиляция очень больших констант с GHC

Сегодня я попросил GHC скомпилировать исходный файл Haskell объемом 8 Мбайт. GHC думал об этом около 6 минут, проглотил почти 2 ГБ оперативной памяти, а затем, наконец, сдался с ошибкой из памяти.

[Как в сторону, я рад, что GHC имел здравый смысл прекратить, а не настил весь мой компьютер.]

В принципе, у меня есть программа, которая читает текстовый файл, делает какой-то причудливый синтаксический разбор, строит структуру данных и затем использует show, чтобы сбрасывать это в файл. Вместо того, чтобы включать весь синтаксический анализатор и исходные данные в моем конечном приложении, я хотел бы включить сгенерированные данные в качестве константы времени компиляции. Добавив некоторые дополнительные данные к выводу из show, вы можете сделать его действительным модулем Haskell. Но GHC, по-видимому, не любит компиляцию исходных файлов с несколькими MB.

(Самая странная часть - если вы только read возвращаете данные, на самом деле это не занимает много времени или памяти. Странно, учитывая, что оба String I/O и read предположительно очень неэффективны...)

Я смутно помню, что у других людей были проблемы с получением GHC для сбора огромных файлов в прошлом. FWIW, я попытался использовать -O0, что ускорило крушение, но не помешало ему. Итак, каков наилучший способ включения больших констант времени компиляции в программу Haskell?

(В моем случае константа является всего лишь вложенным Data.Map с некоторыми интересными метками.)

Первоначально я думал, что GHC может быть просто недовольна чтением модуля, состоящего из одной строки длиной восемь миллионов символов. (!!) Что-то делать с правилом макета или таким. Или возможно, что глубоко вложенные выражения нарушают его. Но я попытался сделать каждое подвыражение идентификатором верхнего уровня, и это не помогло. (Добавление явных сигнатур типов к каждому из них, похоже, сделало компилятор немного более счастливым.) Есть ли что-нибудь еще, что я мог бы попытаться сделать компилятор проще?

В конце концов, мне удалось создать структуру данных, которую я на самом деле пытаюсь сохранить намного меньше. (Например, 300 КБ.) Это сделало GHC намного более счастливым. (И последнее приложение намного быстрее.) Но для будущей ссылки мне было бы интересно узнать, как лучше всего подойти к этому.

4b9b3361

Ответ 1

Лучше всего, наверное, скомпилировать строковое представление вашего значения в исполняемый файл. Чтобы сделать это чистым способом, обратитесь к моему ответу в предыдущем вопросе.

Чтобы использовать его, просто сохраните выражение в 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.