2012-10-04 10 views
13

Eventuali duplicati:
Small Haskell program compiled with GHC into huge binaryPerché i file eseguibili Haskell/GHC sono così grandi in file?

Recentemente ho notato come grandi eseguibili Haskell sono. Tutto sotto è stato compilato su GHC 7.4.1 con -O2 su Linux.

  1. Ciao Mondo (main = putStrLn "Hello World!") è oltre 800 KiB. L'esecuzione di strip riduce la dimensione del file a 500 KiB; anche aggiungere -dynamic alla compilation non aiuta molto, lasciandomi un eseguibile spogliato di circa 400 KiB.

  2. La compilazione di un esempio molto primitivo che coinvolge Parsec produce un file MiB 1.7.

    -- 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 può essere una biblioteca più grande, ma sto usando solo un piccolo sottoinsieme di esso, e in effetti il ​​codice di base ottimizzato generato dal sopra è notevolmente più piccolo del file eseguibile:

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

    Pertanto, non è il caso che una quantità enorme di Parsec sia effettivamente trovata nel programma, che era la mia ipotesi iniziale.

Perché gli eseguibili di dimensioni così grandi? C'è qualcosa che posso fare al riguardo (eccetto il collegamento dinamico)?

+0

@DanielWagner L'altra domanda è certamente correlata, ma anche utilizzando le tecniche descritte lì Hello World è ancora enorme. Inoltre: perché il piccolo codice di base, che dovrebbe contenere l'intero programma, diventa così grande quando viene compilato? – David

+2

C'è un sistema runtime piuttosto grande. – augustss

+2

@David: il core non contiene l'intero programma a meno che tutto non sia stato allineato, il che è piuttosto improbabile. Quindi collegherà in Parsec e, a meno che non lo abbiate creato con '-split-objs' (vedi [risposta correlata] (http://stackoverflow.com/a/9198223/98117), dovrà collegarsi in tutto. – hammar

risposta

3

La mia comprensione è che se si utilizza una singola funzione del pacchetto X, , l'intero pacchetto viene collegato in modo statico. Non credo che GHC colleghi effettivamente funzione per funzione. (A meno che non si usi l'hacking "split objects", che "tende a strangolare il linker".)

Ma se si sta collegando dinamicamente, questo dovrebbe risolvere il problema. Quindi non sono sicuro di cosa suggerire qui ...

(Sono quasi sicuro di aver visto un post sul blog quando è uscito il collegamento dinamico, dimostrando Hello World compilato su un binario da 2 KB. Ovviamente non riesco a trovare questo blog post ora ... grr.)

Considerare anche l'ottimizzazione tra moduli. Se stai scrivendo un parser Parsec, è probabile che GHC inserisca tutte le definizioni del parser e le semplifichi fino al codice più efficiente. E, sicuramente, le tue poche righe di Haskell hanno prodotto 50KB di Core. Dovrebbe essere più grande di 37 volte durante la compilazione su codice macchina? Non lo so. Potresti forse provare a guardare il codice STG e Cmm prodotto nei passaggi successivi. (Scusate, non ricordo il compilatore fuori dalla parte superiore della mia testa ...)

+0

In realtà non è così. Dipende dal sistema. Sulla maggior parte dei sistemi con collegamenti statici GHC utilizza "oggetti divisi", in modo da ottenere un oggetto per funzione. –

+0

@DonStewart Ma è necessario abilitare split-objs nella configurazione della cabala per far sì che le librerie installate sulla cabina siano costruite con gli oggetti divisi, vero? –

11

per ridurre efficacemente dimensioni del file eseguibile prodotto da Glasgow Haskell Compiler è necessario concentrarsi su

  • uso di collegamento dinamico con l'opzione -dynamic passata a ghc in modo che il codice dei moduli non venga inserito nell'eseguibile finale utilizzando librerie (dinamiche) condivise. È richiesta l'esistenza di versioni condivise di queste librerie GHC nel sistema!
  • rimozione delle informazioni di debug del file eseguibile finale (ad es.da strumento striscia di binutils di GNU)
  • rimozione importazioni di moduli non utilizzati (non aspettatevi guadagni a collegamento dinamico)

Il semplice esempio ciao mondo ha la dimensione finale 9 KiB e prova Parsec circa 28 KiB (entrambi Eseguibili per Linux a 64 bit) che trovo abbastanza piccolo e accettabile per un'implementazione linguistica di così alto livello.

+0

Hello World è solo 9 KiB se collego con '-dynamic'. Nel caso Parsec ho avuto problemi nell'installare la versione dinamica ('cabal install parsec --enable-shared --reinstall' ha come risultato la cabal lamentando che non ho" dyn libraries per il pacchetto 'mtl-2.1.1 '" ma quello farebbe un'altra domanda, in ogni caso grazie. – David

Problemi correlati