Size Matters/ru

From Free Pascal wiki
Jump to navigationJump to search

Deutsch (de) English (en) français (fr) русский (ru) 中文(中国大陆) (zh_CN)

Введение

Данный документ посвящен, размерам бинарных файлов и действиям для уменьшения их. В течение многого времени наблюдается большая путаница с размерами бинарных файлов в FPC и Lazarus. Прежде чем задавать вопрос в рассылке, пожалуйста, прочтите этот документ.

Основной причиной создания данного документа является необходимость объяснить причины больших размеров бинарных файлов и способы уменьшения их размеров.


Реальные размеры бинарных файлов Free Pascal/Lazarus

  • все файлы мене 1МБ не считаются проблемой.
    • Убедитесь, что файлы скомпилированы с опциями вырезание отладочной информации и умное связывание, прежде чем смотреть размер, и что все библиотеки скомпилированы также с опцией умное связывание.
    • Не использован упаковщик UPX если вы имеете веские причины не использовать его (смотрите ниже). Размер файла мене важен, чем нагрузка на процессор и оперативную память при распаковке бинарных файлов.
  • Для маленького приложения важную роль играет системное RTL. Однако даже самостоятельные приложения размером 100КБ могут быть доведены до размера менее 50КБ.
    • Создание приложения под Windows использующего только WinAPI размером 20КБ не проблема.
    • Модуль Sysutils содержит код интернационализации, текстовые сообщения об ошибках, обработку исключительных ситуаций и т.д. Включение данного модуля в программу увеличит её размер на 40-100КБ.
  • Изначально приложение Lazarus под Windows имеет размер около 500КБ, но размер быстро вырастает до 1,5МБ при использовании большого количества виджетов.
    • Размер файла получается на много больше, чем у аналогичной программы, скомпилированной в Delphi. Большой размер файла является платой за кроссплатформенность.
    • Наступает момент, когда дополнительный код больше не вносит зависимостей от LCL и размер файла начинает расти медленнее.
    • 1,5МБ – это теоретический предел размера файла. Конечный размер зависит только от вашего стиля создания графического интерфейса пользователя и числа различных графических элементов, которые вы используете.
    • В Lazarus приложениях большая часть не является бинарным кодом в основном это таблицы строк.
  • В Linux/FreeBSD простые программы имеют больший размер, чем соответствующие откомпилированные GCC. Это происходит, потому что FPC не использует динамически подключаемые библиотеки (которые можно легко увидеть, используя "ldd ./xx").
  • 64-разрядные программы всегда будут больше, чем программы для x86. На платформах RISC также генерируется немного больший код.

Почему же такие большие бинарные файлы?

Ответ: Они не являются большими.

Если вы считаете большими то

  • возможно, вы не настроили FPC правильно, или
  • имеете не реалистичное представление о размере кода
  • вы пытаетесь сделать то, для чего FPC не предназначен.

Последний пункт менее вероятен из трёх. Я собираюсь рассмотреть все случаи в следующих параграфах.

Разве плохо, когда бинарные файлы большие?

Конечно размер это величина относительная. Но можно с уверенностью сказать, что вряд ли кто будет беспокоиться насчёт большого исполняемого фала в несколько мегабайт или даже в десятки мегабайт, если приложение является сложным.

Однако есть несколько случаев, когда желателен минимальный размер приложения:

  1. приложения для встраиваемых систем (имеются в виду не системы на основе встраиваемого ПК, который имеет дисковое пространство в десятки МБ)
  2. распространение приложения через медленные каналы связи (например, автообновление программы через модем)
  3. соревнования, проверка эффективности (известные языковые войны)

Обратите внимание, что часто цитируемым заблуждением является то, что большие исполняемые файлы работают медленнее. Это не так.

Встраиваемые системы

Хотя Free Pascal возможно использовать для встраеваемых систем, конечные релизы в большей степени ориентированы для создания приложений общего назначения. Для специализированных целей люди обычно создают теневые проекты на основе FPC как, например, существуют специализированные версии некоторых дистрибутивов Linux. Загруженная и без того команда FPC не занимается специализированными системами тем более что половина серьёзных разработчиков программ под встраиваемые системы создаёт свои собственные теневые проекты основанные на FPC.

Рассылка через модем

В случае автоматического обновления программы передавать исполняемый файл необходимо в сжатом виде, и я не думаю, что передача вашего приложения в сжатом виде на скорости 56 бод займёт более 5 минут.

Если вы думаете распространять свою программу через сеть общего пользования. Вы не думайте, что пользователи модемов будут скачивать её т.к. пользователи с модемным доступом чаще всего заказывают программы на CD дисках или получают их на CD в приложении к журналу.

Соревнования

Другая причина получения файлов малого размера это попытки соревноваться с другими языками программирования и компиляторами (т.н. языковые войны). Однако это больше похоже на решение головоломки, которая не относится к ответственной разработке программного обеспечения.

Неправильная конфигурационного файла компилятора

Я не собираюсь объяснять все аспекты файла конфигурации, так как этот документ не претендует на роль руководства пользователя. В этом документе изложен только обзор возможных настроек. Читайте документацию и ЧАВО по сборке для получения более полной информации.

Вообще, существует несколько причин, из-за которых бинарные файлы получаются больше, чем ожидалось. Данный документ описывает только наиболее распространённые причины в порядке убывания их вероятности:

  1. Исполняемый файл по-прежнему содержит отладочную информацию.
  2. При компиляции не была использована опция умное связывание для всех модулей.
  3. Исполняемый файл содержит модули, у которых в разделах инициализации используется много кода.
  4. Вы используете статическое связывание внешних библиотек в место того чтобы использовать динамическое связывание.
  5. Оптимизация не включена или включена не для всех модулей.
  6. Файл проекта Lazarus (.lpr) содержит модули пакетов в секции uses (эти модули автоматически добавляет lazarus)

В будущем использование внешних ссылок на библиотеку времени выполнения FPC и/или Lazarus может существенно изменить эту картину. Конечно, тогда вам придется распространять с программой большую библиотеку с RTL, и следить за версиями данной библиотеки. Библиотека времени выполнения появится ещё не скоро, так что сейчас трудно оценить какой размер исполняемых файлов будет при использовании данной библиотеки. Особенно потому что динамическое связывание требует дополнительный размер сопоставимый с размерами общими .lib файлами.

Отладочная информация

Free Pascal использует отладчик GDB и линкер LD. Они работают с отладочной информацией в бинарном виде, будь то stabs или dwarf. Люди часто видят бинарные файлы Lazarus, размер которых, например 40МБ. Правильный размер должен быть около 6МБ, остальное это отладочная информация.

Отладочная информация stabs является очень громоздкой, но имеет преимущество в том, что она является независимой от формата бинарного файла. Со временем данный вид отладочной информации будет заменён на dwarf на всех платформах кроме самых старых. Отладочная информация dwarf является более компактной.

Существует частая путаница с отладочной информацией, которая обусловлена работой внутренней утилиты strip во многих версиях бинарных утилит (binutils) под Win32. Кроме того некоторые версии утилиты strip под Win32 не полностью удаляют отладочную информацию из бинарных файлов порожденных FPC. Часто устанавливая параметр (в Lazarus/IDE или в командной строке FPC) -Xs думают, что вся отладочная информация удалится из бинарного файла, а зачастую это не так. FPC был адаптирован к данной ситуации, но это произошло только в версиях после 2006 года.

Если вы сомневаетесь в автоматическом удалении отладочной информации, то всегда делайте удаление автоматически и если вы компилируете под Windows, всегда имейте несколько разных версий утилиты strip. Не используйте утилиту strip из неизвестного вам релиза, так как после своей работы утилита может оставить не рабочий исполняемый файл. Используйте утилиту из основных выпусков cygwin или mingw или хотя бы от бета версий данных пакетов.

Бинарные файлы с удалённой отладочной информацией должны быть только в окончательном релизе приложения, который предназначен для конечного пользователя. Так как удаление отладочной информации не позволит вам получить то место программы, в котором произошла ошибка и невозможно будет установить точку останова в коде.

Отладчик GDB позволяет сохранять отладочную информацию в отдельном файле. Это означает, что отладочная информация не повлияет на размер вашего бинарного файла, и ещё вы сможете произвести отладку полученных бинарных файлов. Создаётся дополнительный файл .dbg, в который помещается вся отладочная информация приложения. Вам не нужен это файл для запуска и использования приложения он используется только отладчиком. Поскольку вся отладочная информация находится вне двоичного файла, вы не получите эффект уменьшения двоичного файла при использовании утилиты strip.

Для размещения отладочной информации отдельно от исполняемого файла необходимо при компиляции приложения указать параметр -Xg (данный параметр используется по умолчанию в Lazarus версии 0.9.27 и выше). При компиляции с внешним файлом отладочной информации приложение с пустой формой под Win32 займёт около 1МБ, а файл .dbg будет около 10МБ.

Умное связывание (smartlink)

(основная статья: Размер файла и умное связывание)

Базовый принцип умного связывания (smartlink) прост и широко известен – не связывать то, что не используется. В свою очередь умное связывание благоприятно действует на размер бинарного файла.

Однако компилятор – просто программа, которая не обладает интеллектом, чтобы знать то, что в коде используется. Алгоритм определения того кода который не используется следующий:

  • Компилятор делит код на так называемые "секции".
  • После чего компоновщик определяет, какие секции используются, используя правило "если на секцию не ссылается код, то секция может быть удалена".

В приведенном выше алгоритме есть несколько проблем:

  • наследуемые виртуальные методы вызываются через таблицу виртуальных методов (ТВМ). Компоновщик GNU не может проследить последовательность вызовов через эти таблицы, таким образом, все виртуальные методы, не используемые в коде, выводятся в бинарный файл;
  • каждый строковый ресурс имеет ссылку на соответствующую константу в коде, таким образом, все строковые ресурсы помещаются в бинарный файл (по этой причине модуль sysutils занимает столько места);
  • символы, экспортируемые из бинарного файла (это справедливо не только для библиотек) должны быть сохранены. Данное ограничение обусловлено необходимостью сохранения таблиц экспорта для разделяемых библиотек;
  • также сохраняются все имена публикуемых (published) свойств и методов классов для использования в RTTI. К этим свойствам и методам обращение происходит во время выполнения программы (например, при загрузке формы из ресурсов) поэтому они должны обязательно присутствовать в исполняемом файле;

Другой важный побочный эффект, который логичен, но часто забываем, состоит в том, что этот алгоритм свяжет все части кода, которые используются в секциях инициализации и завершения модулей, даже если никакой функционал этих модулей не будут использоваться. Так что будьте внимательны и не подключайте те модули, которые не будут использоваться.

Во всяком случае, большинство проблем с использованием умного связывания для получения меньшего размера файла вытекает из того факта, что необходимо компилировать ВСЕ МОДУЛИ И ДАЖЕ RTL с параметром умное связывание.

Причина этого проста. Компоновщик LD может "умно" связывать только те .o файлы, которые были откомпилированы с данной возможностью. Это означает, что для каждого модуля должен быть обработан соответствующий .o файл (а затем все эти .o файлы заархивированы в .a файл). Эта операция занимает дополнительное время (и оперативную память для компоновщика) таким образом, операция умное связывание дополнительна и должна применяется только в релизе приложения.

Зачастую те, у кого возникают проблемы с умным связыванием используют версии RTL/LCL которые не откомпилированы с smartlink. Решение этого вопроса является полная перекомпиляция RTL/FCL c smartlink (-CX) см. buildfaq.

В будущем, когда компилятор будет генерировать код с включенным умным связыванием по умолчанию, данная проблема отпадёт. Это станет возможно в двух случаях:

  • Во-первых, компоновщик GNU LD теперь может выполнять smartlink с более мелкими секциями (по крайней мере, в Unix) с использование параметра --gc-sections.
  • Во-вторых, появление встроенного отладчика FPC (начиная с версии 2.1.1) для всех платформ Windows (Win32/Win64/WinCE).

Работа компоновщика GNU LD с параметром --gc-section имеет ещё много проблем, потому что схема размещение ассемблерного кода и таблиц ещё не исследована и использование данного параметра ещё не протестировано. Это является обычной проблемой с GNU утилитами, которых новые реализации не протестированы (а иногда и не ещё и не осуществлены, смотрите стандарт DWARF).

Внутренний компоновщик FPC выполняет умное связывание довольно хорошо (например, для Lazarus приложения полное умное связывание выполняется за 17 секунд на компьютере Athlon64 3700+ и используется для этого всего 250МБ оперативной памяти) но он существует только под Windows. Внутренний компоновщик также открывает возможность более продвинутого умного связывания для языка Pascal, которое требует специальных знаний по удалению неиспользуемых виртуальных методов (20% размера кода на примере Lazarus программ) и неиспользуемых ресурсов строк. Правда эти возможности пока ещё на стадии альфа тестирования и Lazarus не умеет работать с этим видом оптимизации.

Пример удаления отладочной информации утилитой strip:

strip --strip-all binary_file

Секции инициализации и завершения

Если вы включаете модуль в секцию uses, и никакие классы и функции из данного модуля не используются в программе, а модуль имеет секцию initialization и/или finalization то все функции и классы, на которые ссылается данная секция, будут помещены в выходной бинарный файл. Данное утверждение справедливо, если вы не подключали модуль, а он используется косвенно из других модулей.

Наиболее критичным является модуль sysutils. Для совместимости с Delphi в данный модуль были помещена обработка исключений времени выполнения и основные текстовые сообщения. Строки в модуле занимают довольно много места. Отказ от этого модуля приведёт к тому, что ваше приложение будет не совместима с Delphi. Так что в том случае, когда вам не нужна обработка исключительный ситуации старайтесь не использовать данный модуль, но в случае, когда вы создаёте LCL приложение вам без него не обойтись.

Статическая линковка

(основная статья: Библиотеки в Lazarus/FPC)

Для любой ОС можно сделать статическую компоновку всех используемых библиотек. Обычно это делается для простоты развёртывания программного обеспечения, но использование динамических библиотек даёт большой плюс. Так как использовать динамическую или статическую линковку это дело разработчика я только упоминаю об этом ради законченности рассказа. Вместо того чтобы делать статическую компоновку многие программисты предпочитают делать именно динамическую/раздельную компоновку.

Оптимизация

Оптимизация также позволяет немного уменьшить размер бинарного файла. Оптимизация кода уменьшает размер всего на долю процента. Для использования оптимизации необходимо указать параметр -O3.

Файлы проекта (.lpr) Lazarus

В Lazarus если добавить пакет или поместить на форму компонент из не стандартного пакета, то автоматически в файл (.lpr) добавятся модули данного проекта. По умолчанию файл lpr не открыт если вы хотите отредактировать данный файл его необходимо сначала открыть (меню Проект/Просмотреть исходный файл проекта). Необходимыми модулями являются interface,forms и модуль вашей формы все остальные можете (если конечно они вами не используются непосредственно в lpr) удалять. Смотрите ниже пример стандартного файла проекта с одной формой.

program Project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, Unit1, LResources
  { you can add units after this };

{$IFDEF WINDOWS}{$R project1.rc}{$ENDIF}

begin
  {$I project1.lrs}
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Этим вы можете сэкономить до нескольких мегабайтов если вы используете большие пакеты, такие как GLScene.

Данное поведение характерно для библиотек, которые используют большое количество кода в разделах инициализации модулей. Замечу, что нет разницы в том, где эти модули используются, будь то файл проекта или обычный файл модуля. Конечно же, smartlink пытается избавиться от неиспользуемых модулей, но по изложенным выше причинам это не всегда заканчивается удалением неиспользуемого кода.

Проблемы в версии fpc 2.2.0

Я часто встречаю на различных форумах (например Русскоязычный форум по FreePascal и Lazarus) вопросы по поводу размера исполняемых файлов скомпилированных FPC. Для объяснения причин больших размеров и способов как с ними бороться был написан данный документ (FAQ).

Однако в последнее время я заметил некоторые причины увеличения размера исполняемого файла, которые не были выше изложены. Я так подозреваю, что данные причины связаны с появлением внутреннего компоновщика в версии FPC 2.2.0 и выше. Приведу список данных причин. Замечу, что приведенный ниже список причин справедлив для настройки по умолчанию с включённым внутренним компоновщиком (linker).

  • Похоже, что FPC 2.2.0 не удаляет отладочную информацию, если компилировать проект с любым из параметров -g. Это противоречит предыдущим версиям FPC где параметр -Xs имеет больший приоритет чем -g.
  • Похоже, что FPC 2.2.0 не всегда выполняет smartlink при кросс-компиляции. Это может быть проблематично при компиляции для Windows, когда не удаляются зависимости на несуществующие функции.

UPX

UPX позволяет сжимать исполняемые файлы, сохраняя возможность их запуска. У UPX есть как преимущества, так и недостатки.

Преимущества:

  1. Декомпрессия сжатого файла происходит автоматически и пользователю незаметна;
  2. Если критерии накладываются именно на размер исполняемого файла, а не на размер дистрибутива, который можно сжать, например в ZIP архив;
  3. Если критическим параметром является дисковое пространство;
  4. Многие пользователи не знают об UPX и судят, о приложение именно по его конечному размеру (на многих Shareware сайтах в отзывах о программе часто её маленький размер считают плюсом). Поэтому если у других разработчиков есть программа с аналогичными функциями и с большим размерами то, скорее всего, выберут вашу (хотя можно распространять свои программы в виде SFX архивов).

Недостатки:

  1. Поскольку каждый сжатый UPX исполняемый файл содержит код декомпрессии, то степень сжатия конечного файла не так велика, как она может быть получена при использовании внешнего архиватора;
  2. При каждом запуске сжатой UPX программы происходит процесс распаковки сжатой части пред выполнением;
  3. Поскольку Windows XP теперь оснащена встроенным декомпрессором для ZIP, весь смысл использовать SFX отпадает;
  4. Сжатые UPX программы часто антивирусами считаются вредоносными программами;
  5. Сжатый двоичный код не может быть отображен в памяти Windows и должен быть загружен в память полностью. Это означает, что в память загружается не только исполняемый код, но и все ресурсы хранящееся в файле, что увеличивает размер памяти, используемый программой.

Последний пункт поясню подробнее. При запуске несжатых бинарных файлов Windows в память помещает только исполняемый код, выделяя под него окно в 4КБ (8КБ в 64 битной версии) и постоянно перегружает это окно по необходимости. Это объясняет то, что, почему при запуске приложения её исполняемый файл заблокирован (его нельзя не удалить, не переименовать). Все ресурсы (строковые/графические) бинарного файла остаются на диске и в память загружаются только по требованию. При запуске сжатого исполняемого файла он загружается в память полностью. После чего управление передаётся распаковщику, который содержится в файле. После распаковке управление передаётся непосредственно исполняемому коду. Весь код загруженного упакованного файла и ресурсы приложения остаются в памяти!

Фреймворки

Использование фреймворков уменьшает объём работы необходимой для написания приложения.

Однако фреймворк увеличивают размер полученной программы. Так как фреймворк это не просто библиотека это по большей части целая подсистема, которая реализует взаимодействие программы с внешним миром. Фреймворки разрабатываются для написания различных программ, с различным функционалом и ваше приложение получает почти весь этот функционал, даже если изначально он не требовался.

Чем больший функциональных возможностей предоставляет вам фреймворк, тем меньшее количество кода необходимо будет писать самому и заботится о таких вещах как обработка основных исключительных ситуациях.

Получаемый размер исполняемого файла пустого приложения при использовании фреймворка не вызван неэффективностью компилятора. Компилятор будит пытаться удалить неиспользуемый код автоматически, но по изложенным выше причинам у него не получится удалить его весь. Обычно фреймворки построены по такому принципу, который позволяет указать компилятору, какой код необходимо удалить во время компиляции.

Некоторые фреймворки значительно увеличиваю размер бинарного файла, а некоторые нет. Ожидаемые размеры бинарных файлов для пустых приложений при использовании разных фреймворков:

  • Без фреймворка (только RTL): +/- 25КБ;
  • Без фреймворка (только RTL+sysutils): +/- 100-125КБ;
  • MSEGUI: +/- 600КБ
  • Lazarus LCL: +/- 1000КБ;
  • Free Vision: +/- 100КБ;
  • Key Objects Library: +/- 50КБ.

В общем, использование фреймворка не является нецелесообразным. Мощный фрейморк на много увеличит размер вашего бинарного файла, но к тому же, сохранит вам уйму времени на разработку приложения. Если размер файла является критическим, то лучше использовать более лёгкие фреймворки.

Обратите внимание что, фреймворк Lazarus сравнительно тяжёл за счёт использования RTTI, а не только из-за размеров исходных файлов. Использование RTTI негативно сказывается на механизме smartlink.