Ответ 1
В Apple есть несколько замечательных документов в Руководстве по эффективности размера кода, почти все из которых относятся к этому вопросу в той или иной форме. Есть даже советы для педантичных подходов, таких как ручной порядок символов в двоичном формате, если это необходимо.: -)
Я полностью поклонник простого, тонкого кода и минимизации занимаемого диском/памятью. Преждевременная оптимизация - это всегда плохая идея, но последовательное ведение домашнего хозяйства может быть хорошим способом предотвратить накопление трещин. К сожалению, я не знаю автоматизированного способа профилирования размеров кода, но существует несколько инструментов, которые могут помочь обеспечить конкретную информацию.
Размер двоичного изображения
Файлы объектов не так страшны, как предполагалось. Одна из причин, по которой сумма меньше, чем части, состоит в том, что код все соединен вместе с одним заголовком. Хотя проценты не будут точными, самые большие объектные файлы являются самыми большими частями связанного двоичного файла.
Для понимания необработанной длины каждого конкретного метода в объектном файле вы можете использовать /usr/bin/otool
, чтобы распечатать код сборки, помеченный именами методов Objective-C:
$ otool -tV MyClass.o
Я ищу длинные отрезки сборки, которые соответствуют относительно коротким или простым методам и проверяют, можно ли вообще упростить или удалить код.
В дополнение к otool
я обнаружил, что /usr/bin/size
может быть весьма полезен, поскольку он разбивает сегменты и разделы иерархически и показывает размер каждого из них, как для объектных файлов и скомпилированные двоичные файлы. Например:
$ size -m -s __TEXT __text MyClass.o
$ size -m /Applications/iCal.app/Contents/MacOS/iCal
Это представление "большего размера", хотя обычно это подтверждает, что __TEXT __text
часто является одним из самых больших в файле и, следовательно, является хорошим местом для начала обрезки.
Идентификация мертвого кода
Никто не хочет, чтобы их двоичный код был усеян кодом, который никогда не используется. В динамическом и слабосвязаном языке, таком как Objective-C, может быть сложно или невозможно статически определить, используется ли конкретный код или нет. Даже если экземпляр класса создается или вызывается метод, пути отслеживания кода (как теоретические, так и фактические) могут быть головной болью. Я использую несколько трюков, чтобы помочь с этим.
- Для статического анализа я настоятельно рекомендую Clang Static Analyzer (который удачно встроен в Xcode 3.2 на Snow Leopard). Среди всех других достоинств этот инструмент может отслеживать пути кода, идентифицирующие фрагменты кода, которые невозможно выполнить, и должны быть удалены или окружающий код должен быть исправлен, чтобы он мог быть вызван.
- Для динамического анализа я использую
gcov
(с модульным тестированием), чтобы определить, какой код фактически выполнен. Отчеты о покрытии (чтение с помощью чего-то вроде CoverStory) показывают незавершенный код, который в сочетании с ручным экзаменом и тестированием может помочь определить код, который может быть мертв. Вам нужно настроить некоторые настройки и запустить gcov вручную в своих двоичных файлах. Я использовал этот пост в блоге, чтобы начать.
На практике необычным для мертвого кода является достаточно большая доля кода, чтобы существенно повлиять на размер двоичного файла или время загрузки, но мертвый код, безусловно, усложняет обслуживание, и лучше всего избавиться от него, если вы можете.
Видимость символов
Уменьшение видимости символов может показаться странной рекомендацией, но это облегчает задачу dyld
(компоновщик, который загружает программы во время выполнения ) и позволяет компилятору выполнять улучшенные оптимизации. Рассмотрите скрытие глобальных переменных (которые не объявлены как static
) и т.д., Префикс их с помощью "скрытого" атрибута или включение "Символы, скрытые по умолчанию" в Xcode и явно отображающие символы. Я использую следующие макросы:
#define HIDDEN __attribute__((visibility("hidden")))
#define VISIBLE __attribute__((visibility("default")))
Я нахожу /usr/bin/nm
неоценимым для идентификации ненужных видимых символов и для определения потенциальных внешних зависимостей, которые вы могли бы не знать или не рассматривали.
$ nm -m -s __TEXT __text MyClass.o # -s displays only a given section
$ nm -m -p MyClass.o # -p preserves symbol table ordering (no sort)
$ nm -m -u MyClass.o # -u displays only undefined symbols
Хотя уменьшение видимости символов вряд ли приведет к прямому уменьшению размера вашего бинарного файла, компилятор может сделать улучшения, которые он не мог бы сделать иначе. Кроме того, вы стоите, чтобы уменьшить случайные зависимости от символов, которые вы не собирались раскрывать.
Анализ зависимостей и загрузки библиотек
В дополнение к необработанному двоичному размеру часто бывает полезно проанализировать, с какими динамическими библиотеками вы ссылаетесь, и устранить те, которые могут быть ненужными, особенно менее часто используемые фреймворки, которые еще не загружены. (Вы также можете увидеть это также из Xcode, но со сложными проектами, иногда вещи проскальзывают, поэтому это также обеспечивает удобную проверку работоспособности после сборки.) Снова otool на помощь...
$ otool -L MyClass.o
Другой (чрезвычайно подробный) вариант состоит в том, чтобы иметь dyld
печатать загруженные библиотеки, например (из терминала):
$ export DYLD_PRINT_LIBRARIES=1
$ /Applications/iCal.app/Contents/MacOS/iCal
Это показывает, что именно загружается, включая зависимости библиотек от ваших кодовых ссылок.
Анализ производительности запуска
Обычно, что вам действительно интересно, зависит от того, влияют ли размер кода и зависимостей библиотеки на время запуска. Установка этой переменной среды приведет к тому, что dyld
сообщит статистику загрузки, что действительно поможет определить, сколько времени было потрачено на загрузку:
$ export DYLD_PRINT_STATISTICS=1
$ /Applications/iCal.app/Contents/MacOS/iCal
На Leopard и позже вы увидите записи о dyld общем кэше. В принципе, динамический компоновщик создает консолидированную "супербиблиотеку", состоящую из наиболее часто используемых динамических библиотек. Это упоминается в этой документации Apple, и поведение может быть изменено с помощью переменных среды DYLD_SHARED_REGION
и DYLD_NO_FIX_PREBINDING
, аналогичных приведенным выше. Подробнее см. man dyld
.