2012-10-04 21 views
13

Duplicar posible:
Small Haskell program compiled with GHC into huge binary¿Por qué los archivos ejecutables de Haskell/GHC son tan grandes en tamaño de archivo?

Hace poco se dio cuenta de lo grande que son ejecutables Haskell. Todo a continuación fue compilado en GHC 7.4.1 con -O2 en Linux.

  1. Hello World (main = putStrLn "Hello World!") es más de 800 KiB. Al ejecutar strip, se reduce el tamaño del archivo a 500 KiB; incluso agregar -dynamic a la compilación no ayuda mucho, dejándome con un ejecutable eliminado alrededor de 400 KiB.

  2. La compilación de un ejemplo muy primitivo que involucra a Parsec arroja un archivo de 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 puede ser una biblioteca grande, pero yo sólo estoy usando un pequeño subconjunto de la misma, y ​​de hecho el código del núcleo optimizado generado por el anterior es drásticamente menor que el ejecutable:

    $ ghc -O2 -ddump-simpl -fforce-recomp test.hs | wc -c 
    49190 (bytes) 
    

    Por lo tanto, no es el caso que una gran cantidad de Parsec se encuentre realmente en el programa, que fue mi suposición inicial.

¿Por qué los ejecutables son de un tamaño tan enorme? ¿Hay algo que pueda hacer al respecto (excepto el enlace dinámico)?

+0

@DanielWagner La otra pregunta está ciertamente relacionada, pero incluso utilizando las técnicas descritas, Hello World sigue siendo enorme. Además, ¿por qué el código de núcleo pequeño, que debería contener todo el programa, se vuelve tan grande cuando se compila? – David

+2

Hay un sistema de tiempo de ejecución bastante grande. – augustss

+2

@David: el núcleo no contiene todo el programa a menos que todo esté en línea, lo que es bastante improbable. Por lo tanto, va a vincularse en Parsec, y a menos que haya compilado eso con '-split-objs' (consulte [respuesta relacionada] (http://stackoverflow.com/a/9198223/98117)), tendrá que enlazar en todo ello. – hammar

Respuesta

3

Tengo entendido que si utiliza una sola función del paquete X, , todo el paquete se vincula de forma estática. No creo que GHC realmente enlace función por función. (A menos que uses el truco de "objetos divididos", que "tiende a enloquecer al engarce").

Pero si estás enlazando dinámicamente, eso debería solucionarlo. Así que no estoy seguro de qué sugerir aquí ...

(Estoy bastante seguro de que vi una publicación en un blog cuando apareció el enlace dinámico, lo que demuestra Hello World compilado en un binario de 2 KB. Obviamente no puedo encontrar este blog publicar ahora ... grr.)

Considere también la optimización de módulos cruzados. Si está escribiendo un analizador Parsec, es probable que GHC incorpore todas las definiciones del analizador y las simplifique hasta el código más eficiente. Y, por supuesto, tus pocas líneas de Haskell han producido 50 KB de Core. ¿Debería ser 37 veces más grande al compilar en código de máquina? No lo sé. Quizás puedas intentar mirar el código STG y Cmm producido en los próximos pasos. (Lo siento, no recuerdo las opciones del compilador de la parte superior de mi cabeza ...)

+0

Ese no es realmente el caso. Depende del sistema En la mayoría de los sistemas con enlace estático GHC usa "objetos divididos", de modo que obtienes un objeto por función. –

+0

@DonStewart Pero necesitas habilitar split-objs en la configuración de cabal para obtener tus bibliotecas instaladas en cabal, construidas con objetos divididos, ¿no? –

11

para reducir eficazmente el tamaño del ejecutable producido por Glasgow Haskell Compiler usted tiene que centrarse en

  • uso de vinculación dinámica con la opción -dynamic pasada a ghc para que el código de los módulos no se agrupe en el ejecutable final mediante el uso de bibliotecas compartidas (dinámicas). ¡Se requiere la existencia de versiones compartidas de estas bibliotecas de GHC en el sistema!
  • eliminando las informaciones de depuración del ejecutable final (f.E.por la herramienta tira de binutils de GNU)
  • la eliminación de las importaciones de los módulos no utilizados (no esperar ganancias en la vinculación dinámica)

El ejemplo simple hola mundo tiene el tamaño final 9 KiB y de prueba Parsec sobre 28 KiB (tanto Ejecutables Linux de 64 bits) que considero bastante pequeño y aceptable para una implementación de lenguaje de tan alto nivel.

+0

Hello World es solo 9 KiB si conecto con '-dynamic'. En el caso de Parsec, tuve problemas para instalar la versión dinámica ('cabal install parsec --enable-shared --reinstall' los resultados en él quejándose de que no tengo" librerías dyn para el paquete 'mtl-2.1.1 '" , pero eso haría otra pregunta. En cualquier caso, gracias. – David

Cuestiones relacionadas