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 shared cache ". По сути, динамический компоновщик создает консолидированную «супер библиотеку», состоящую из наиболее часто используемых динамических библиотек. Это упомянуто в этой документации Apple , и поведение может быть изменено с помощью переменных окружения DYLD_SHARED_REGION
и DYLD_NO_FIX_PREBINDING
, аналогично приведенному выше. Подробнее см. man dyld
.