Подтвердить что ты не робот

Почему исполняемые файлы Haskell/GHC настолько велики в размерах файлов?

Возможный дубликат:
Небольшая программа Haskell, скомпилированная с GHC в огромные бинарные файлы

Недавно я заметил, насколько велики исполняемые файлы Haskell. Все ниже было скомпилировано в GHC 7.4.1 с -O2 в Linux.

  • Hello World (main = putStrLn "Hello World!") - более 800 KiB. Запуск strip над ним уменьшает размер файла до 500 KiB; даже добавление -dynamic в компиляцию не очень помогает, оставляя меня с удаленным исполняемым файлом около 400 KiB.

  • Компиляция очень примитивного примера с участием Parsec дает 1,7 файла MiB.

    -- 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 может быть большой библиотекой, но я использую только крошечное подмножество, и действительно оптимизированный код ядра, созданный выше, значительно меньше, чем исполняемый файл:

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

    Следовательно, это не тот случай, когда огромное количество Parsec действительно найдено в программе, что было моим первоначальным допущением.

Почему исполняемые файлы такого огромного размера? Что-то я могу с этим сделать (кроме динамической компоновки)?

4b9b3361

Ответ 1

Чтобы эффективно уменьшить размер исполняемого файла, созданного компилятором Glasgow Haskell, вам нужно сосредоточиться на

  • использование динамической компоновки с опцией -dynamic, переданной в ghc, поэтому код модуля не будет включен в окончательный исполняемый файл, используя общие (динамические) библиотеки. Необходимо наличие общих версий этих библиотек GHC в системе!
  • удаление информации об отладке окончательного исполняемого файла (f.E. ленточным инструментом GNU binutils)
  • удаление импортных неиспользуемых модулей (не ожидайте прироста при динамической компоновке)

Простой пример hello world имеет окончательный размер 9 KiB и Parsec test около 28 KiB (оба 64-разрядных исполняемых файла Linux), которые я нахожу довольно маленькими и приемлемыми для такой языковой реализации высокого уровня.

Ответ 2

Я понимаю, что если вы используете одну функцию из пакета X, весь пакет статически связан. Я не думаю, что GHC на самом деле связывает функциональные функции. (Если вы не используете взломанные "расколотые объекты", которые "вызывают уродливость компоновщика".)

Но если вы связываете динамически, это должно исправить это. Поэтому я не уверен, что предложить здесь...

(Я уверен, что видел сообщение в блоге, когда впервые появилась динамическая компоновка, демонстрируя Hello World, скомпилированный в двоичный файл 2KB. Очевидно, я не могу найти этот пост в блоге сейчас... grr.)

Рассмотрим также кросс-модульную оптимизацию. Если вы пишете парсер Parsec, скорее всего, GHC проведет все определения парсера и упростит их до самого эффективного кода. И, конечно же, ваши несколько строк Haskell выпустили 50 КБ Core. Должно ли это получиться в 37 раз больше при компиляции в машинный код? Я не знаю. Вы могли бы попытаться взглянуть на код STG и Cmm, созданный на следующих шагах. (Извините, я не помню флагов компилятора с моей головы...)