web-dev-qa-db-fra.com

Petit programme Haskell compilé avec GHC en énorme binaire

Même les petits programmes Haskell sont banals et se transforment en de gigantesques exécutables.

J'ai écrit un petit programme, qui a été compilé (avec GHC) en binaire avec une taille s'étendant sur 7 Mo!

Qu'est-ce qui peut provoquer la compilation d'un petit programme Haskell dans l'énorme binaire?

Que puis-je faire pour réduire cela?

120
user181351

Voyons voir ce qui se passe, essayez

  $ du -hs A
  13M   A

  $ file A
  A: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), 
     dynamically linked (uses shared libs), for GNU/Linux 2.6.27, not stripped

  $ ldd A
    linux-vdso.so.1 =>  (0x00007fff1b9ff000)
    libXrandr.so.2 => /usr/lib/libXrandr.so.2 (0x00007fb21f418000)
    libX11.so.6 => /usr/lib/libX11.so.6 (0x00007fb21f0d9000)
    libGLU.so.1 => /usr/lib/libGLU.so.1 (0x00007fb21ee6d000)
    libGL.so.1 => /usr/lib/libGL.so.1 (0x00007fb21ebf4000)
    libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007fb21e988000)
    libm.so.6 => /lib/libm.so.6 (0x00007fb21e706000)
    ...      

Vous voyez à partir de la sortie ldd que GHC a produit un exécutable lié dynamiquement, mais seules les bibliothèques C sont liées dynamiquement! Toutes les bibliothèques Haskell sont copiées textuellement.

A part: comme il s'agit d'une application gourmande en graphiques, je compilerais certainement avec ghc -O2

Vous pouvez faire deux choses.

Suppression des symboles

Une solution simple: dépouiller le binaire:

$ strip A
$ du -hs A
5.8M    A

Strip supprime les symboles du fichier objet. Ils ne sont généralement nécessaires que pour le débogage.

Bibliothèques Haskell liées dynamiquement

Plus récemment, GHC a pris en charge liaison dynamique des bibliothèques C et Haskell . La plupart des distributions distribuent désormais une version de GHC conçue pour prendre en charge la liaison dynamique des bibliothèques Haskell. Les bibliothèques Haskell partagées peuvent être partagées entre de nombreux programmes Haskell, sans les copier à chaque fois dans l'exécutable.

Au moment de la rédaction, Linux et Windows sont pris en charge.

Pour permettre aux bibliothèques Haskell d'être liées dynamiquement, vous devez les compiler avec -dynamic, ainsi:

 $ ghc -O2 --make -dynamic A.hs

De plus, toutes les bibliothèques que vous souhaitez partager doivent être construites avec --enabled-shared:

 $ cabal install opengl --enable-shared --reinstall     
 $ cabal install glfw   --enable-shared --reinstall

Et vous vous retrouverez avec un exécutable beaucoup plus petit, qui a les deux dépendances C et Haskell résolues dynamiquement.

$ ghc -O2 -dynamic A.hs                         
[1 of 4] Compiling S3DM.V3          ( S3DM/V3.hs, S3DM/V3.o )
[2 of 4] Compiling S3DM.M3          ( S3DM/M3.hs, S3DM/M3.o )
[3 of 4] Compiling S3DM.X4          ( S3DM/X4.hs, S3DM/X4.o )
[4 of 4] Compiling Main             ( A.hs, A.o )
Linking A...

Et voilà!

$ du -hs A
124K    A

que vous pouvez décaper pour les rendre encore plus petits:

$ strip A
$ du -hs A
84K A

Un exécutable Eensy Weensy, construit à partir de nombreuses pièces C et Haskell liées dynamiquement:

$ ldd A
    libHSOpenGL-2.4.0.1-ghc7.0.3.so => ...
    libHSTensor-1.0.0.1-ghc7.0.3.so => ...
    libHSStateVar-1.0.0.0-ghc7.0.3.so =>...
    libHSObjectName-1.0.0.0-ghc7.0.3.so => ...
    libHSGLURaw-1.1.0.0-ghc7.0.3.so => ...
    libHSOpenGLRaw-1.1.0.1-ghc7.0.3.so => ...
    libHSbase-4.3.1.0-ghc7.0.3.so => ...
    libHSinteger-gmp-0.2.0.3-ghc7.0.3.so => ...
    libHSghc-prim-0.2.0.0-ghc7.0.3.so => ...
    libHSrts-ghc7.0.3.so => ...
    libm.so.6 => /lib/libm.so.6 (0x00007ffa4ffd6000)
    librt.so.1 => /lib/librt.so.1 (0x00007ffa4fdce000)
    libdl.so.2 => /lib/libdl.so.2 (0x00007ffa4fbca000)
    libHSffi-ghc7.0.3.so => ...

Un dernier point: même sur les systèmes avec liaison statique uniquement, vous pouvez tilisez -split-objs , pour obtenir un fichier .o par fonction de niveau supérieur, ce qui peut réduire davantage la taille des bibliothèques liées statiquement. Il faut que GHC soit construit avec -split-objs, ce que certains systèmes oublient de faire.

208
Don Stewart

Haskell utilise la liaison statique par défaut. Autrement dit, toutes les liaisons à OpenGL sont copiées dans votre programme. Comme ils sont assez gros, votre programme se gonfle inutilement. Vous pouvez contourner ce problème en utilisant la liaison dynamique, bien qu'elle ne soit pas activée par défaut.

11
fuz