online - haskell core download
Compilando constantes muy grandes con GHC (2)
Hay una solución simple: su literal debe tener el tipo ByteString
. Consulte https://github.com/litherum/publicsuffixlist/pull/1 para obtener más información.
Hoy le pedí a GHC que compile un archivo fuente de Haskell de 8MB. GHC lo pensó durante unos 6 minutos, tragó casi 2 GB de RAM y finalmente se rindió con un error de memoria insuficiente.
[Dejando de lado, me alegro de que GHC tuvo la sensatez de abortar en lugar de utilizar todo mi PC].
Básicamente, tengo un programa que lee un archivo de texto, hace un análisis sofisticado, construye una estructura de datos y luego usa show
para volcar esto en un archivo. En lugar de incluir el analizador completo y los datos de origen en mi aplicación final, me gustaría incluir los datos generados como una constante de compilación. Al agregar algunas cosas adicionales a la salida del show
, puede convertirlo en un módulo de Haskell válido. Pero, al parecer, GHC no disfruta compilar archivos fuente de varios MB.
(La parte más extraña es que, si acaba de read
los datos, en realidad no lleva mucho tiempo ni memoria. Extraño, considerando que tanto String
I / O de String
como la read
son supuestamente muy ineficientes ...)
Recuerdo vagamente que otras personas han tenido problemas para que GHC compile archivos enormes en el pasado. FWIW, intenté usar -O0
, lo que aceleró el bloqueo pero no lo evitó. Entonces, ¿cuál es la mejor manera de incluir grandes constantes de tiempo de compilación en un programa Haskell?
(En mi caso, la constante es solo un Data.Map
anidado con algunas etiquetas interesantes).
Inicialmente pensé que GHC podría estar descontento al leer un módulo que consta de una línea que tiene ocho millones de caracteres. (!!) Algo relacionado con la regla de diseño o algo así. O quizás que las expresiones profundamente anidadas lo trastornan. Pero intenté hacer de cada subexpresión un identificador de nivel superior, y eso no fue de ninguna ayuda. (Sin embargo, parece que agregar firmas de tipo explícito a cada uno hace que el compilador sea un poco más feliz). ¿Hay algo más que pueda intentar para simplificar el trabajo del compilador?
Al final, pude hacer que la estructura de datos que estoy tratando de almacenar fuera mucho más pequeña. (Como, 300KB). Esto hizo a GHC mucho más feliz. (Y la aplicación final es mucho más rápida.) Pero para futuras referencias, me interesaría saber cuál es la mejor manera de abordar esto.
Su mejor apuesta es probablemente compilar una representación de cadena de su valor en el ejecutable. Para hacer esto de manera limpia, consulte mi respuesta en una pregunta anterior .
Para usarlo, simplemente almacene su expresión en myExpression.exp
y read [litFile|myExpression.exp|]
con la extensión QuasiQuotes
habilitada, y la expresión se "almacenará como una cadena literal" en el ejecutable.
Intenté hacer algo similar para almacenar constantes reales, pero falla por el mismo motivo por el que incrustar el valor en un archivo .hs
. Mi intento fue:
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
Programa de prueba:
{-# LANGUAGE QuasiQuotes #-}
module Main (main) where
import Verbatim
main :: IO ()
main = print [verbatimFile|test.exp|]
Este programa funciona para pequeños archivos test.exp
, pero ya falla en aproximadamente 2MiB en esta computadora.