Невелика програма Haskell, складена разом з GHC, у величезну бінарну


127

Навіть тривіально малі програми Haskell перетворюються на гігантські виконувані файли.

Я написав невелику програму, яка була складена (з GHC) у двійкову, розмір якої перевищує 7 Мб!

Що може спричинити компіляцію навіть невеликої програми Haskell до величезного бінарного файлу?

Що, якщо що, я можу зробити, щоб зменшити це?


2
Ви спробували просто зняти його?
Фред Фоо

21
Запустіть програму stripна двійковому, щоб видалити таблицю символів.
Фред Фоо

1
@ tm1rbt: запустіть strip test. Ця команда видаляє деяку інформацію про налагодження з програми та робить її меншою.
фуз

8
Окрім того, ваші типи даних у бібліотеці математики 3D повинні бути суворішими з міркувань продуктивності: data M3 = M3 !V3 !V3 !V3і data V3 = V3 !Float !Float !Float. Компілювати з ghc -O2 -funbox-strict-fields.
Дон Стюарт

8
Цей пост обговорюється на мета .
Патрік Хофман

Відповіді:


215

Подивимося, що відбувається, спробуй

  $ 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

Можна зробити дві речі.

Зачистки символів

Просте рішення: зніміть бінарне:

$ strip A
$ du -hs A
5.8M    A

Стрипка відкидає символи з об’єктного файлу. Вони, як правило, потрібні лише для налагодження.

Динамічно пов'язані бібліотеки Haskell

Зовсім недавно GHC отримала підтримку динамічного зв’язку бібліотек C і Haskell . Більшість дистрибутивів зараз поширюють версію GHC, побудовану для підтримки динамічного зв’язку бібліотек Haskell. Спільні бібліотеки 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 , щоб отримати один .o файл на функцію верхнього рівня, що може додатково зменшити розмір статично пов'язаних бібліотек. Для цього потрібно створити GHC з -split-objs, що деякі системи забувають робити.


7
коли відбувається динамічне посилання через надходження ghc на mac?
Carter Tazio Schonwald

1
... не cabal installзнімає встановлений бінарний файл за замовчуванням?
hvr

1
це робиться в Windows, здається, що отриманий файл не запускається, він скаржиться на відсутність libHSrts-ghc7.0.3.dll
is7s

3
чи буде цей двійковий файл працювати на інших машинах Linux після цих процедур?
ア レ ッ ク ス

1
Привіт ОП від 2011 року! Я з майбутнього , і можу сказати , що pandoc виконуваний на Ubuntu 16.04 є 50MB жиру , і це не буде оновлено на основі packages.ubuntu.com/zesty/pandoc . Повідомлення найближчому майбутньому та іншим: зв’яжіться з технічним обслуговувачем пакета та запитайте, чи enable-sharedбуло враховано. launchpad.net/ubuntu/+source/pandoc/+bugs
Stéphane Gourichon

11

Haskell використовує статичні зв'язки за замовчуванням. Ось, цілі прив’язки до OpenGL копіюються у вашу програму. Оскільки вони досить великі, ваша програма надмірно завищена. Ви можете подолати це за допомогою динамічного зв’язку, хоча воно не включено за замовчуванням.


5
Ви можете динамічно зв’язувати бібліотеки, щоб обійти це. Не впевнений, чому має значення те, що за замовчуванням, прапор досить простий.
Thomas M. DuBuisson

4
Проблема полягає в тому, що "будь-які бібліотеки, з якими ви хочете поділитися, повинні бути створені --enabled-shared", тому якщо ваша платформа Haskell постачається з вбудованими бібліотеками, --enabled sharedвам не доведеться перекомпілювати базові бібліотеки, що може бути досить болісно.
nponeccop
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.