些細な小さなHaskellプログラムでさえ、巨大な実行可能ファイルに変わります。
私は、7 MBを拡張するサイズのバイナリに(GHCを使用して)コンパイルされた小さなプログラムを作成しました。
小さなHaskellプログラムでさえ巨大なバイナリにコンパイルされる原因は何ですか?
これを減らすために、もしあれば、私は何ができますか?
些細な小さなHaskellプログラムでさえ、巨大な実行可能ファイルに変わります。
私は、7 MBを拡張するサイズのバイナリに(GHCを使用して)コンパイルされた小さなプログラムを作成しました。
小さなHaskellプログラムでさえ巨大なバイナリにコンパイルされる原因は何ですか?
これを減らすために、もしあれば、私は何ができますか?
何が起こっているか見てみましょう、試してみてください
$ 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)
...
出力から、ldd
GHC が動的にリンクされた実行可能ファイルを生成したことがわかりますが、動的にリンクされているのは C ライブラリだけです! すべての Haskell ライブラリは逐語的にコピーされます。
余談: これはグラフィックスを多用するアプリなので、間違いなく次のようにコンパイルします。ghc -O2
できることは 2 つあります。
シンボルの除去
簡単な解決策: バイナリを削除します:
$ strip A
$ du -hs A
5.8M A
Strip は、オブジェクト ファイルからシンボルを破棄します。通常、これらはデバッグにのみ必要です。
動的にリンクされた Haskell ライブラリ
最近では、GHC がC ライブラリと Haskell ライブラリの両方の動的リンクをサポートするようになりました。現在、ほとんどのディストリビューションは、Haskell ライブラリの動的リンクをサポートするようにビルドされた GHC のバージョンを配布しています。共有 Haskell ライブラリは、毎回実行可能ファイルにコピーすることなく、多くの Haskell プログラム間で共有できます。
執筆時点では、Linux と Windows がサポートされています。
Haskell ライブラリを動的にリンクできるようにするには、次-dynamic
のように でコンパイルする必要があります。
$ ghc -O2 --make -dynamic A.hs
また、共有したいライブラリは次のようにビルドする必要があります--enabled-shared
。
$ cabal install opengl --enable-shared --reinstall
$ cabal install glfw --enable-shared --reinstall
そして、C と Haskell の両方の依存関係が動的に解決される、はるかに小さい実行可能ファイルになります。
$ 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...
そして、ほら!
$ du -hs A
124K A
さらに小さくするために剥がすことができます:
$ strip A
$ du -hs A
84K A
多くの動的にリンクされた C と Haskell の部分から構築された、非常に弱い実行可能ファイル:
$ 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 => ...
最後のポイント: 静的リンクのみのシステムでも、-split-objs を使用して、トップレベル関数ごとに 1 つの .o ファイルを取得できます。これにより、静的にリンクされたライブラリのサイズをさらに縮小できます。-split-objs をオンにして GHC をビルドする必要がありますが、これを忘れているシステムもあります。
Haskell はデフォルトで静的リンクを使用します。つまり、OpenGL へのバインディング全体がプログラムにコピーされます。それらは非常に大きいため、プログラムは不必要に膨張します。デフォルトでは有効になっていませんが、動的リンクを使用することでこれを回避できます。