Kleines Haskell-Programm, kompiliert mit GHC in riesige Binärdateien

127

Selbst trivial kleine Haskell-Programme werden zu gigantischen ausführbaren Dateien.

Ich habe ein kleines Programm geschrieben, das (mit GHC) in die Binärdatei mit einer Größe von 7 MB kompiliert wurde!

Was kann dazu führen, dass selbst ein kleines Haskell-Programm in die große Binärdatei kompiliert wird?

Was kann ich tun, um dies zu reduzieren?

Donau-Seemann
quelle
2
Hast du versucht, es einfach auszuziehen?
Fred Foo
21
Führen Sie das Programm stripauf der Binärdatei aus, um die Symboltabelle zu entfernen.
Fred Foo
1
@ tm1rbt: Ausführen strip test. Dieser Befehl entfernt einige Debug-Informationen aus dem Programm und verkleinert es.
Fuz
8
Abgesehen davon sollten Ihre Datentypen in der 3D-Mathematikbibliothek aus Leistungsgründen strenger sein: data M3 = M3 !V3 !V3 !V3und data V3 = V3 !Float !Float !Float. Kompilieren mit ghc -O2 -funbox-strict-fields.
Don Stewart
8
Dieser Beitrag wird auf Meta diskutiert .
Patrick Hofman

Antworten:

215

Mal sehen, was los ist, versuchen Sie es

  $ 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)
    ...      

Sie sehen aus der lddAusgabe, dass GHC eine dynamisch verknüpfte ausführbare Datei erstellt hat, aber nur die C-Bibliotheken dynamisch verknüpft sind ! Alle Haskell-Bibliotheken werden wörtlich kopiert.

Nebenbei: Da dies eine grafikintensive App ist, würde ich definitiv mit kompilieren ghc -O2

Sie können zwei Dinge tun.

Symbole entfernen

Eine einfache Lösung: Entfernen Sie die Binärdatei:

$ strip A
$ du -hs A
5.8M    A

Strip verwirft Symbole aus der Objektdatei. Sie werden in der Regel nur zum Debuggen benötigt.

Dynamisch verknüpfte Haskell-Bibliotheken

In jüngerer Zeit hat GHC Unterstützung für die dynamische Verknüpfung von C- und Haskell-Bibliotheken erhalten . Die meisten Distributionen vertreiben jetzt eine Version von GHC, die die dynamische Verknüpfung von Haskell-Bibliotheken unterstützt. Freigegebene Haskell-Bibliotheken können von vielen Haskell-Programmen gemeinsam genutzt werden, ohne sie jedes Mal in die ausführbare Datei zu kopieren.

Zum Zeitpunkt des Schreibens werden Linux und Windows unterstützt.

Damit die Haskell-Bibliotheken dynamisch verknüpft werden können, müssen Sie sie -dynamicwie folgt kompilieren :

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

Außerdem sollten alle Bibliotheken, die Sie freigeben möchten, erstellt werden mit --enabled-shared:

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

Am Ende erhalten Sie eine viel kleinere ausführbare Datei, deren C- und Haskell-Abhängigkeiten dynamisch aufgelöst werden.

$ 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...

Und voilà!

$ du -hs A
124K    A

was Sie abstreifen können, um noch kleiner zu machen:

$ strip A
$ du -hs A
84K A

Eine eensy weensy ausführbare Datei, die aus vielen dynamisch verknüpften C- und Haskell-Stücken aufgebaut ist:

$ 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 => ...

Ein letzter Punkt: Selbst auf Systemen mit nur statischer Verknüpfung können Sie -split-objs verwenden , um eine .o-Datei pro Funktion der obersten Ebene abzurufen , wodurch die Größe statisch verknüpfter Bibliotheken weiter reduziert werden kann. Es muss GHC mit -split-objs erstellt werden, was einige Systeme vergessen haben.

Don Stewart
quelle
7
Wann kommt die dynamische Verknüpfung für ghc auf dem Mac?
Carter Tazio Schonwald
1
... entfernt cabal installdie installierte Binärdatei nicht standardmäßig?
hvr
1
Wenn Sie dies unter Windows tun, scheint die resultierende Datei nicht mehr ausführbar zu sein. Sie beschwert sich über das Fehlen von libHSrts-ghc7.0.3.dll
is7s
3
Funktioniert diese Binärdatei nach diesen Verfahren auf anderen Linux-Computern?
ス レ ッ. ス
1
Hallo OP von 2011! Ich komme aus der Zukunft und kann sagen, dass die unter Ubuntu 16.04 ausführbare Pandoc-Datei 50 MB Fett hat und sich nicht aufgrund von packages.ubuntu.com/zesty/pandoc ändern wird . Nachricht an sich selbst und andere in naher Zukunft: Wenden Sie sich an den Paketbetreuer und fragen Sie, ob dies enable-sharedin Betracht gezogen wurde. launchpad.net/ubuntu/+source/pandoc/+bugs
Stéphane Gourichon
11

Haskell verwendet standardmäßig statische Verknüpfungen. Dies bedeutet, dass die gesamten Bindungen an OpenGL in Ihr Programm kopiert werden. Da sie ziemlich groß sind, wird Ihr Programm unnötig aufgeblasen. Sie können dies umgehen, indem Sie eine dynamische Verknüpfung verwenden, die jedoch standardmäßig nicht aktiviert ist.

fuz
quelle
5
Sie können Bibliotheken dynamisch verknüpfen, um dies zu umgehen. Nicht sicher, warum es wichtig ist, was Standard ist, das Flag ist einfach genug.
Thomas M. DuBuisson
4
Das Problem ist, dass "alle Bibliotheken, mit denen Sie gemeinsam genutzt werden möchten, erstellt werden sollten --enabled-shared". Wenn Ihre Haskell-Plattform also Bibliotheken enthält, die erstellt wurden, ohne dass --enabled sharedSie die Basisbibliotheken neu kompilieren müssen, was sehr schmerzhaft sein kann.
Nponeccop