Как уменьшить размер файла exe
Как человеку, выросшему во времена дискет и 56 Кбит модемов, мне всегда нравились небольшие программы. Я мог поместить много небольших программ на дискету, которую носил с собой. Если программа не помещалась на моем гибком диске, я начинал думать, почему: много графики? Музыка? Программа сложная или просто раздулась?
В наши дни дисковое пространство стало настолько дешевым (а огромные флешки настолько вездесущими), что люди отказались от оптимизации размера.
Единственная область, где размер е щ е имеет значение — это передача: при передаче программы по проводу мегабайты приравниваются к секундам. Быстрое соединение на 100 Мбит может пропускать только 12 мегабайт в секунду в лучшем случае. Когда на другом конце провода находится человек, ожидающий завершения загрузки, разница между пятью и одной секундами может оказать существенное влияние на восприятие. Человек может зависеть от времени передачи либо напрямую: он загружает программу по сети, либо косвенно — бессерверная служба развертывается для ответа на веб-запрос.
Люди обычно воспринимают что-то быстрее 0,1 секунды как мгновенное. 3 секунды — примерно предел непрерывности потока пользователя, и вам было бы трудно удержать пользователя после 10 секунд.
Хотя меньший размер больше не является существенным, он всё равно лучше.
Мы создадим клон змейки:
Не интересна игровая механика? Не стесняйтесь переходить к интересным частям, где мы сжимаем игру с 65 мегабайт до 8 килобайт за 9 шагов, прокрутите до графика.
Игра будет работать в текстовом режиме, и мы используем поле рисования символов, чтобы нарисовать змею. Я уверен, что Vulcan или DirectX намного веселее, но мы справимся и с System.Console .
Начнем со структуры буфера кадров. Буфер кадров — это компонент, содержащий пиксели (или в данном случае — символы), отображаемые на экране:
Мы предоставляем методы установки отдельных пикселей, очистки и визуализации содержимого буфера кадров System.Console . Шаг рендеринга особых случаев несколько символов, так что мы получаем красочный вывод без необходимости отслеживать цвет каждого пикселя буфера кадров.
Мы не можем переборщить с размером фиксированного массива, потому что как часть структуры массив должен жить в стеке, а стеки, как правило, ограничены небольшим количеством байтов (обычно 1 Мб на поток) и 40*20*2 width*height* sizeof(char) — допустимое число.
Этот генератор не слишком хорош, но нам не нужно ничего сложного. Теперь пишем обёртку для логики игры:
Состояния, которые должна отслеживать змейка:
- Координаты каждого пикселя тела.
- Длина змейки.
- Текущее направление движения.
- Прошлое направление для случая, когда нужно нарисовать символ изгиба вместо прямой линии.
Структура предоставляет метод расширения змеи на один элемент (возвращает false, если змея уже находится на полной длине), метод HitTest для теста столкновений пикселя тела, отрисовки змейки во FrameBuffer и метод обновления положения змеи как ответ на игровой тик (возвращает false , если змея съела себя). Существует также свойство, чтобы установить направление змеи.
Мы используем тот же трюк с фиксированным массивом, что и в буфере кадров, чтобы не использовать new . Это означает, что максимальная длина змеи должна быть постоянной времени компиляции. Последнее, что нам нужно — игровой цикл:
Мы используем генератор случайных чисел для генерации случайного положения и направления змеи. Мы случайным образом размещаем еду на игровой поверхности так, чтобы она не перекрывала змею, и запускаем цикл игры. Внутри игрового цикла мы просим змею обновить свое положение и проверить, съела ли она сама себя. Затем рисуем змею, проверяем клавиатуру на ввод, тестируем змею с едой и отображаем всё на консоль. Давайте посмотрим, где мы находимся с точки зрения размера.
Я поместил игру в репозиторий GitHub, чтобы вы могли следить за ней. Файл проекта собирает игру в различных конфигурациях в зависимости от переданного при публикации свойства Mode . Чтобы создать конфигурацию по умолчанию с помощью CoreCLR, выполните:
С этой настройкой игра сжимается до 25 МБ. Хорошее сокращение, но оно далеко от нашей цели.
IL Linker имеет более агрессивные настройки, не выставляемые публично, и они могут работать дальше, но в конце концов, мы ограничимся размером самой среды выполнения CoreCLR coreclr.dll в 5,3 Мбайт. Возможно, мы зашли в тупик на пути к игре на 8 Кб?
В отличие от CoreCLR, Mono также зависит от распространяемой библиотеки среды выполнения Visual C++, недоступной в установке Windows по умолчанию: чтобы сохранить автономность приложения, нам нужно зашить эту библиотеку в приложение. Это увеличивает объем ещё на один мегабайт или около того.
Мы, вероятно, сможем сделать приложение меньше, добавив Il Linker, но тогда столкнемся с той же проблемой, что и с CoreCLR — размером среды выполнения mono-2.0-sgen.dll , он составляет 5,9 МБ. Плюс размер библиотек времени выполнения C++ поверх него. Это предел оптимизаций уровня IL.
Давайте посмотрим, где мы находимся теперь с конфигурацией CoreRT по умолчанию:
4,7 МБ. Файл пока самый маленький, но этого недостаточно.
Сейчас мы на уровне 4,3 МБ.
Я сгруппировал еще несколько вариантов компиляции в режим “сильной экономии”. Режим удаляет поддержку возможностей, важных для многих приложений, но не для нашей змейки. Мы удаляем:
Мы достигли 3,0 МБ, это 5% от начального размера, но у CoreRT есть еще один трюк.
Сейчас мы на уровне 1,2 МБ. Оверхед на рефлексию довольно значителен.
Как мы уже видели ранее, CoreRT — это набор библиотек времени выполнения в сочетании с опережающим компилятором. Что делать, если мы заменим библиотеки времени выполнения с минимальным переопределением? Мы решили не использовать сборщик мусора, и это делает работу намного более выполнимой. Начнём с простого:
Мы просто переопределили Thread.Sleep и Environment.TickCount64 (для Windows), избегая всех зависимостей от существующей библиотеки времени выполнения. Делаем то же самое для подмножества System.Console , используемого игрой:
Пересоберём игру с заменой фреймворка:
Неудивительно, что это не слишком эффективно. Заменяемые API уже относительно легки, переписывание только добавляет пару килобайт, о которых не стоит упоминать. Но это важная ступенька к последнему шагу нашего путешествия.
Оставшиеся 1,2 МБ кода и данных в игре — это поддержка вещей, которые мы не видим, но они есть, они готовы, если нам понадобятся. Есть сборщик мусора, поддержка обработки исключений, код для форматирования и печати трассировок стека на консоль, когда происходит необработанное исключение, и многие другие вещи под капотом.
Компилятор может обнаружить, что ничего этого не требуется, и избежать их генерации, но то, что мы пытаемся сделать, настолько странно, что неплохо добавить функции компилятора для его поддержки. Способ избежать этого — просто предоставить альтернативную библиотеку времени выполнения. Начнем с переопределения минимальной версии базовых типов:
/noconfig , /nostdlib , и /runtimemetadataversion — волшебные параметры, необходимыми для компиляции чего-то, определяющего System.Object . Я выбрал расширение .ilexe , потому что .exe мы используем для готового продукта.
Перестроим IL с добавленным кодом и повторно запустим ILC.
Теперь у нас есть zerosnake.obj — стандартный объектный файл, который ничем не отличается от объектных файлов, создаваемых другими нативными компиляторами, такими как C или C++. Последний шаг — связать его. Воспользуемся link.exe , он должен быть в “x64 Native Tools Command Prompt”. Возможно, вам потребуется установить средства разработки C/C++ в Visual Studio.
Имя символа __managed__Main является контрактом с компилятором — это имя управляемой точки входа программы, созданной ILC. Но команда не работает:
Некоторые из этих символов кажутся знакомыми: компоновщик не знает, где искать вызываемые API Windows. Добавим библиотеки для них:
Выглядит лучше, всего 4 неразрешенных символа:
Остальные отсутствующие символы — это помощники, которые компилятор ожидает найти в библиотеке времени выполнения. Их отсутствие обнаруживается только во время связывания, потому что эти помощники обычно реализуются в сборке, и компилятор ссылается на них только по их символическому имени в отличие от других типов и методов, необходимых компилятору и предоставленных нами выше.
После перестроения исходного кода с этими изменениями и повторного запуска ILC, связывание, наконец, будет успешным. Мы сейчас на 27 килобайтах. Игра работает!
Оставшиеся килобайты можно получить с помощью трюков, которые используют разработчики нативного кода, чтобы уменьшить свои приложения. Мы собираемся:
- Отключить инкрементное связывание.
- Обрезать информацию о релокации.
- Объединить похожие разделы внутри исполняемого файла.
- Установить внутреннее выравнивание в небольшое значение
8176 байт! Игра все еще работает и, что интересно, она полностью отлаживаема. Вы можете отключить оптимизацию в ILC, чтобы сделать исполняемый файл еще более отладочным: просто удалите аргумент --Os .
Исполняемый файл ещё содержит несущественные данные — компилятор ILC просто не предоставляет параметры командной строки, отключающие их генерацию.
Одна из этих избыточных структур данных — информация GC для отдельных методов. В CoreRT есть точный сборщик мусора, требующий, чтобы каждый метод описывал, где находятся ссылки на кучу GC в каждой инструкции тела метода.
Поскольку у нас нет сборщика мусора, эти данные не нужны. Другие среды выполнения — например Mono — используют консервативный сборщик, не требующий этих данных. Он просто предполагает, что любая часть стека и регистров процессора может быть ссылкой GC. Консервативный сборщик торгует производительностью GC ради экономии размера. Точный сборщик CoreRT также может работать в консервативном режиме, но он еще не подключен. Это потенциальное будущее дополнение, которое мы могли бы использовать, чтобы сделать программу ещё меньше. Может, однажды мы сможем сделать упрощенную версию нашей игры в 512 байт загрузочного сектора. А до тех пор — счастливого кода!
В Windows 10 появилась функция сжатия данных Compact OS. Управляет сжатием данных системная утилита compact.exe, но пользоваться ей можно только из командной строки. Воспользовавшись данной утилитой, с помощью алгоритмов сжатия Compact OS, можно сэкономить до 50% места на диске. Причем практически не теряя производительности за счет прозрачного сжатия данных. На старых жестких дисках можно даже заметить прирост производительности, потому как более маленькие файлы означают, что для чтения игр и программ требуется меньше времени. Вы также продолжаете пользоваться файлами, как будто ничего не сжимали – они не переупаковываются, как файлы Zip или Rar.
Для более комфортной работы с данной функцией сторонними разработчиками была создана программа CompactGUI , имеющая удобный графический интерфейс. Программа распространяется бесплатно, пока без поддержки русского языка, что для некоторых пользователей является существенным минусом, но и им разобраться не составит большого труда.
Программа не требует установки. Запускаем исполняемый файл CompactGUI.exe, нажимаем Select Target Folder (выбрать целевую папку).
Указываем папку с данными, которую нам нужно сжать. Далее в окне программы можно указать:
- Алгоритм (степень) сжатия (1).
- Принудительное действие на файлы (2).
- Обрабатывать скрытые и системные файлы (3).
- Действие после завершения сжатия: выключение или перезагрузка компьютера, перевод в сон (4).
Нажимаем кнопку «Compress Folder».
Эффективность сжатия будет зависеть не только от выбранного алгоритма, но и от типа сжимаемых данных. Максимальному сжатию (до 50-60%) будут подвержены файлы программных библиотек, текстовые файлы и т.д., для которых ранее не использовалось сжатие. В меньшей степени сжатию подвержены аудио, видео файлы и архивы, потому что они и так сжаты в большинстве случаев.
В окне будет показан процесс сжатия.
После завершения процесса, в окне будет предоставлена информации о том, на сколько MB или GB уменьшилась папка с данными, то есть сколько программе CompactGUI удалось освободить места на диске.
Как видно из заголовка статьи разговор будет о размере исполняемого файла при компилирование в Lazarus. Это частый вопрос у тех кто только недавно начал программировать в Lazarus. И так начнем.
Введение
Картинка для привлечения внимания
При первой компиляции в Lazarus проекта, многие удивляются размеру исполняемого файла . Ниже приведено сравнение размеров скомпилированного пустого проекта в разных средах программирования.
Среда разработки | Размер файла по умолчанию (байт) | Размер файла с оптимизацией (байт) |
---|---|---|
Lazarus | 15 210 592 | 1 818 112 |
Delphi XE7 | 11 510 970 | 2 277 376 |
Delphi 7 | 368 128 | 368 128 |
Как видно из этой таблицы не все так уж и плохо у лазаруса в плане размеров, последние версии Delphi изрядно растолстели, как осенние белки. Но все же далеко ему и до Delphi 7. В общем есть куда стремится. Если же добавить на форму кучу разных компонент то размер файла будет весьма впечатляющий.
Вот пример размера файла самой среды Lazarus собранной с кучей установленных компонентов.
Все каких то 235 мегабайт. 235 мегабайт Карл. Файл lazarus.old.exe собран как раз таки с оптимизацией. Разница в размере видна невооружённым взглядом.
Большой размер исполняемого файла обусловлен тем что компилятор включает много дополнительной отладочной информации, которая помогает отлаживать программу. Но это все требуется зачастую на этапе отладки программы, конечному же пользователя не хочется сидеть и ждать когда через пол часа закачается программа. Вот для это и нужно эту лишнюю информацию исключать.
И так что для этого нужно сделать что бы программа созданная в лазарусе не занимала много места.
Настройка параметров компилирования
Пользователям старой версии лазаруса необходимо было в настройках проекта выставлять различные галочки для исключения лишней информации из файла. Было это примерно так.
Открывалась форма параметры проекта. И на вкладках выставлялись нужные галочки.
На вкладке Компиляция и компоновка снимались галочки.
На вкладке Отладка снималась галочка "Генерировать отладочную информацию" и ставилась галочка "Вырезать символы из исполнимого файла"
Данные настройки позволяют уменьшить размер файла.
В новой же версии Lazarus, на данный момент пишу это для версии 1.4.4 все несколько упростилось.
Теперь можно сделать так. Открываем параметры проекта и нажимаем на "Параметры компиляции". Затем нажимаем кнопку рядом с режимами сборки.
Появится окно параметров сборки. Тут нажимаем кнопку "Создать режимы отладочной и конечной сборки ". В окне появится еще два режима сборки, Release и Debug, такие же режимы если в последних версиях Delphi XE?.
Режим Release будет создавать маленький исполняемый файл.
После этого на панели инструментов появится кнопка выбора режимов.
Достаточно выбрать нужный режим.
Это методы используемые из среды разработки. Теперь про дополнительные методы.
Утилита Strip.exe
В комплекте с компилятором Free Pascal имеется замечательная утилита Strip.exe, которая вычищает отладочную информацию из исполняемого файла.
Утилита запускается из командной строки с параметрами
strip.exe --strip-all project1.exe
Но это не наш метод.
Сделать можно так.
Открываем меню Сервис - Настроить внешние средства.
В открывшемся окне необходимо добавить новый инструмент. Я сделал так.
Теперь можно пользоваться утилитой strip.exe прямо из среды разработки.
Утилита upx.exe.
Так же для уменьшения размеров файла можно использовать различные упаковщики. Они позволяют существенно уменьшить размер программы. А более продвинутые позволяют установить защиту на исполняемый файл или триальный период. Одной из самых распространенных программ для сжатия файлов является UPX. Данная утилита одна из самых простых в использовании. Из командной строки это делается так.
upx.exe --best project1.exe
Но опять же все можно упростить до минимума.
Как и в предыдущем методе делаем так .
И можем пользоваться утилитой из среды разработки.
Она сжимаем файл достаточно хорошо. И не особо уменьшает скорость запуска файла. Пример работы показан на изображение.
Почти троекратное сжатие файла.
Использование LLCL - Lazarus Light LCL
Недавно появились компоненты которые позволяют существенно уменьшить размер исполняемого файла. Это компоненты LLCL этакий аналог KOL для Delphi. Это набор файлов в которых переработанных классов free pascal (поправьте если ошибаюсь) где часть кода переписана на ассемблере, что позволяет уменьшить размер занимаемый исполняемым файлом. LLCL основан на базе Light VCL replacement for Delphi 7 наборе классов для Delphi 7 .
LLCL не требует установки его в Lazarus, достаточно просто в параметрах компиляции проекта сделать на них ссылку. И при сборке будут использоватся эти классы, а не стандартные от лазаруса.
В общем я сделал так . Скачал данный компоненты. Закинул их в папку где у меня лежат компоненты от лазаря. Затем создал простой проект с формой, на ней разместил несколько компонентов.
А затем собрал проект. Несколько ниже приведены полные результаты эксперимента. Затем я использовал компоненты LLCL. Делается это так. Открывается окно параметров проекта, затем выбирается пункт пути и нажимается кнопка "Другие модули".
В появившемся окне добавляем в верхнее окно путь к папке с файлами LLCL.
Затем нажал Ок и закрыл параметры проекта. И снова собрал проект снова. Исполняемый файл стал весьма меленьким. Решил еще оптимизировать проект. Удалил стандартную иконку из проекта. И пересобрал проект. Программа стала еще меньше. И под конец сжал её upx-ом. Результаты видно тут. Это очень замечательно. Можно писать очень маленькие вирусы утилиты на лазарусе.
P.S. Дальнейшие испытания показали что при использование LLCL придется применять крайне малый набор графических компонент. Это печально.
Вот собственно и все что можно на данный момент скачать о уменьшение размера приложения в Lazarus, если появится какой либо еще новый способ то я его обязательно опишу.
Как человеку, выросшему во времена дискет и 56 Кбит модемов, мне всегда нравились небольшие программы. Я мог поместить много небольших программ на дискету, которую носил с собой. Если программа не помещалась на моем гибком диске, я начинал думать, почему: много графики? Музыка? Программа сложная или просто раздулась?
В наши дни дисковое пространство стало настолько дешевым (а огромные флешки настолько вездесущими), что люди отказались от оптимизации размера.
Единственная область, где размер е щ е имеет значение — это передача: при передаче программы по проводу мегабайты приравниваются к секундам. Быстрое соединение на 100 Мбит может пропускать только 12 мегабайт в секунду в лучшем случае. Когда на другом конце провода находится человек, ожидающий завершения загрузки, разница между пятью и одной секундами может оказать существенное влияние на восприятие. Человек может зависеть от времени передачи либо напрямую: он загружает программу по сети, либо косвенно — бессерверная служба развертывается для ответа на веб-запрос.
Люди обычно воспринимают что-то быстрее 0,1 секунды как мгновенное. 3 секунды — примерно предел непрерывности потока пользователя, и вам было бы трудно удержать пользователя после 10 секунд.
Хотя меньший размер больше не является существенным, он всё равно лучше.
Мы создадим клон змейки:
Не интересна игровая механика? Не стесняйтесь переходить к интересным частям, где мы сжимаем игру с 65 мегабайт до 8 килобайт за 9 шагов, прокрутите до графика.
Игра будет работать в текстовом режиме, и мы используем поле рисования символов, чтобы нарисовать змею. Я уверен, что Vulcan или DirectX намного веселее, но мы справимся и с System.Console .
Начнем со структуры буфера кадров. Буфер кадров — это компонент, содержащий пиксели (или в данном случае — символы), отображаемые на экране:
Мы предоставляем методы установки отдельных пикселей, очистки и визуализации содержимого буфера кадров System.Console . Шаг рендеринга особых случаев несколько символов, так что мы получаем красочный вывод без необходимости отслеживать цвет каждого пикселя буфера кадров.
Мы не можем переборщить с размером фиксированного массива, потому что как часть структуры массив должен жить в стеке, а стеки, как правило, ограничены небольшим количеством байтов (обычно 1 Мб на поток) и 40*20*2 width*height* sizeof(char) — допустимое число.
Этот генератор не слишком хорош, но нам не нужно ничего сложного. Теперь пишем обёртку для логики игры:
Состояния, которые должна отслеживать змейка:
- Координаты каждого пикселя тела.
- Длина змейки.
- Текущее направление движения.
- Прошлое направление для случая, когда нужно нарисовать символ изгиба вместо прямой линии.
Структура предоставляет метод расширения змеи на один элемент (возвращает false, если змея уже находится на полной длине), метод HitTest для теста столкновений пикселя тела, отрисовки змейки во FrameBuffer и метод обновления положения змеи как ответ на игровой тик (возвращает false , если змея съела себя). Существует также свойство, чтобы установить направление змеи.
Мы используем тот же трюк с фиксированным массивом, что и в буфере кадров, чтобы не использовать new . Это означает, что максимальная длина змеи должна быть постоянной времени компиляции. Последнее, что нам нужно — игровой цикл:
Мы используем генератор случайных чисел для генерации случайного положения и направления змеи. Мы случайным образом размещаем еду на игровой поверхности так, чтобы она не перекрывала змею, и запускаем цикл игры. Внутри игрового цикла мы просим змею обновить свое положение и проверить, съела ли она сама себя. Затем рисуем змею, проверяем клавиатуру на ввод, тестируем змею с едой и отображаем всё на консоль. Давайте посмотрим, где мы находимся с точки зрения размера.
Я поместил игру в репозиторий GitHub, чтобы вы могли следить за ней. Файл проекта собирает игру в различных конфигурациях в зависимости от переданного при публикации свойства Mode . Чтобы создать конфигурацию по умолчанию с помощью CoreCLR, выполните:
С этой настройкой игра сжимается до 25 МБ. Хорошее сокращение, но оно далеко от нашей цели.
IL Linker имеет более агрессивные настройки, не выставляемые публично, и они могут работать дальше, но в конце концов, мы ограничимся размером самой среды выполнения CoreCLR coreclr.dll в 5,3 Мбайт. Возможно, мы зашли в тупик на пути к игре на 8 Кб?
В отличие от CoreCLR, Mono также зависит от распространяемой библиотеки среды выполнения Visual C++, недоступной в установке Windows по умолчанию: чтобы сохранить автономность приложения, нам нужно зашить эту библиотеку в приложение. Это увеличивает объем ещё на один мегабайт или около того.
Мы, вероятно, сможем сделать приложение меньше, добавив Il Linker, но тогда столкнемся с той же проблемой, что и с CoreCLR — размером среды выполнения mono-2.0-sgen.dll , он составляет 5,9 МБ. Плюс размер библиотек времени выполнения C++ поверх него. Это предел оптимизаций уровня IL.
Давайте посмотрим, где мы находимся теперь с конфигурацией CoreRT по умолчанию:
4,7 МБ. Файл пока самый маленький, но этого недостаточно.
Сейчас мы на уровне 4,3 МБ.
Я сгруппировал еще несколько вариантов компиляции в режим “сильной экономии”. Режим удаляет поддержку возможностей, важных для многих приложений, но не для нашей змейки. Мы удаляем:
Мы достигли 3,0 МБ, это 5% от начального размера, но у CoreRT есть еще один трюк.
Сейчас мы на уровне 1,2 МБ. Оверхед на рефлексию довольно значителен.
Как мы уже видели ранее, CoreRT — это набор библиотек времени выполнения в сочетании с опережающим компилятором. Что делать, если мы заменим библиотеки времени выполнения с минимальным переопределением? Мы решили не использовать сборщик мусора, и это делает работу намного более выполнимой. Начнём с простого:
Мы просто переопределили Thread.Sleep и Environment.TickCount64 (для Windows), избегая всех зависимостей от существующей библиотеки времени выполнения. Делаем то же самое для подмножества System.Console , используемого игрой:
Пересоберём игру с заменой фреймворка:
Неудивительно, что это не слишком эффективно. Заменяемые API уже относительно легки, переписывание только добавляет пару килобайт, о которых не стоит упоминать. Но это важная ступенька к последнему шагу нашего путешествия.
Оставшиеся 1,2 МБ кода и данных в игре — это поддержка вещей, которые мы не видим, но они есть, они готовы, если нам понадобятся. Есть сборщик мусора, поддержка обработки исключений, код для форматирования и печати трассировок стека на консоль, когда происходит необработанное исключение, и многие другие вещи под капотом.
Компилятор может обнаружить, что ничего этого не требуется, и избежать их генерации, но то, что мы пытаемся сделать, настолько странно, что неплохо добавить функции компилятора для его поддержки. Способ избежать этого — просто предоставить альтернативную библиотеку времени выполнения. Начнем с переопределения минимальной версии базовых типов:
/noconfig , /nostdlib , и /runtimemetadataversion — волшебные параметры, необходимыми для компиляции чего-то, определяющего System.Object . Я выбрал расширение .ilexe , потому что .exe мы используем для готового продукта.
Перестроим IL с добавленным кодом и повторно запустим ILC.
Теперь у нас есть zerosnake.obj — стандартный объектный файл, который ничем не отличается от объектных файлов, создаваемых другими нативными компиляторами, такими как C или C++. Последний шаг — связать его. Воспользуемся link.exe , он должен быть в “x64 Native Tools Command Prompt”. Возможно, вам потребуется установить средства разработки C/C++ в Visual Studio.
Имя символа __managed__Main является контрактом с компилятором — это имя управляемой точки входа программы, созданной ILC. Но команда не работает:
Некоторые из этих символов кажутся знакомыми: компоновщик не знает, где искать вызываемые API Windows. Добавим библиотеки для них:
Выглядит лучше, всего 4 неразрешенных символа:
Остальные отсутствующие символы — это помощники, которые компилятор ожидает найти в библиотеке времени выполнения. Их отсутствие обнаруживается только во время связывания, потому что эти помощники обычно реализуются в сборке, и компилятор ссылается на них только по их символическому имени в отличие от других типов и методов, необходимых компилятору и предоставленных нами выше.
После перестроения исходного кода с этими изменениями и повторного запуска ILC, связывание, наконец, будет успешным. Мы сейчас на 27 килобайтах. Игра работает!
Оставшиеся килобайты можно получить с помощью трюков, которые используют разработчики нативного кода, чтобы уменьшить свои приложения. Мы собираемся:
- Отключить инкрементное связывание.
- Обрезать информацию о релокации.
- Объединить похожие разделы внутри исполняемого файла.
- Установить внутреннее выравнивание в небольшое значение
8176 байт! Игра все еще работает и, что интересно, она полностью отлаживаема. Вы можете отключить оптимизацию в ILC, чтобы сделать исполняемый файл еще более отладочным: просто удалите аргумент --Os .
Исполняемый файл ещё содержит несущественные данные — компилятор ILC просто не предоставляет параметры командной строки, отключающие их генерацию.
Одна из этих избыточных структур данных — информация GC для отдельных методов. В CoreRT есть точный сборщик мусора, требующий, чтобы каждый метод описывал, где находятся ссылки на кучу GC в каждой инструкции тела метода.
Поскольку у нас нет сборщика мусора, эти данные не нужны. Другие среды выполнения — например Mono — используют консервативный сборщик, не требующий этих данных. Он просто предполагает, что любая часть стека и регистров процессора может быть ссылкой GC. Консервативный сборщик торгует производительностью GC ради экономии размера. Точный сборщик CoreRT также может работать в консервативном режиме, но он еще не подключен. Это потенциальное будущее дополнение, которое мы могли бы использовать, чтобы сделать программу ещё меньше. Может, однажды мы сможем сделать упрощенную версию нашей игры в 512 байт загрузочного сектора. А до тех пор — счастливого кода!
Читайте также: