Мобильные процессоры и память все быстрее, а приложения загружаются все так же. В чем соль? Вопрос времени запуска iOS-приложений занимает ум не одного разработчика (особенно после перехода на Swift). Мы решили выяснить причины долгой загрузки и варианты решения этой проблемы у Николая Лихогруда, разработчика Мобильных Яндекс.Карт.
Под катом: типичные грабли, полезные инструменты и правильные подходы к решению проблем с производительностью.
— Расскажите, пожалуйста, в двух словах о себе и своей работе.
Николай Лихогруд: В 2012 году я окончил факультет Вычислительной Математики и Кибернетики МГУ и почти сразу начал работать в компании Any Void под руководством Юрия Подорожного. Какое-то время проработал там над различными проектами, набрался опыта, а после нашего перехода в «Яндекс» переключился на Мобильные Яндекс.Карты. В этом проекте Я уже почти 3 года, последний год работаю руководителем группы.
— Какую роль в вашей работе играют эксперименты с ускорением запуска iOS-приложений?
Николай Лихогруд: Время запуска — один из ключевых показателей мобильного приложения. Яндекс.Карты должны помогать пользователям быстро понять, где они находятся и куда нужно идти. Если приложение долго запускается, то оно будет раздражать людей, они будут уходить к конкурентам или писать гневные отзывы. Как руководитель разработки я напрямую отвечаю за этот ключевой показатель, и за последнее время мы проделали много работы в направлении его улучшения.
— На каком месте борьба за сокращение времени запуска приложения стоит в общей последовательности работ по улучшению производительности приложения?
Николай Лихогруд: Все зависит от того, на чем написано ваше приложение — на Objective-C или на Swift, и на какой парк устройств оно ориентировано. Приложения на Objective-C принципиально запускаются быстрее благодаря тому, что библиотеки Objective-C runtime встроены в систему, и код на Objective-C может быть скомпилирован в статические библиотеки. Это избавляет от ресурсоемких операций по загрузке динамических библиотек при запуске. Со Swift ситуация совсем другая — даже пустое приложение на iPhone 5 грузится 2 секунды. Поэтому для Swift проблема запуска выходит на первое место.
— Существуют ли какие-то типичные грабли (связанные со скоростью запуска приложений), на которые чаще всего «наступают» разработчики?
Николай Лихогруд: Одна из самых главных проблем — использование подов, написанных на Swift, т.к. в этом случае все таргеты CocoaPods собираются в отдельные динамические библиотеки. Даже если в проекте мало кода в основном таргете, поды на Swift могут увеличить время запуска в несколько раз
— Можно ли с ними как-то бороться (помимо очевидного решения — не использовать CocoaPods со Swift)?
Николай Лихогруд: Да. Есть способ, позволяющий, с одной стороны, подтягивать зависимости через CocoaPods, но с другой стороны не компилировать их в отдельные динамические библиотеки.
— Эти «грабли» актуальны для всех версий iOS (Swift)? Или у каждой версии есть свой «джентльменский набор» подводных камней?
Николай Лихогруд: На данный момент это верно для всех версий Swift и для всех iOS, как для старых, так и для новых. Загрузка пользовательских динамических библиотек выполняется долго, особенно на 32-битных устройствах. Разработчики Apple пытаются улучшить ситуацию. Например, в iOS 9.3 они уменьшили время проверки подписи динамических библиотек, но радикально ситуация не изменилась.
— С чего, на ваш взгляд, надо начинать работу в направлении оптимизации времени запуска приложения?
Николай Лихогруд: Первый шаг — включить переменную окружения DYLD_PRINT_STATISTICS и посмотреть, сколько времени уходит на pre-main и after-main — т.е. на части запуска, за которые ответственны система и непосредственно ваше приложение. Это позволит выявить, где именно проблемы — в вашем коде или в работе системного загрузчика, который собирает образ приложения в памяти. Дальнейшие действия зависят от результатов замеров. Есть способы уменьшить время системной части загрузки, а свой код оптимизируется как обычно — тут отлично помогает профилировщик.
— В рамках своего доклада на Mobius 2017 вы подробнее будете рассказывать именно про системную часть загрузки приложений на Swift?
Николай Лихогруд: Да. Поскольку у нас приложение на Swift, основная проблема была именно с системной частью. Я подробно расскажу, как нужно замерять pre-main и after-main, про холодный и теплый запуски, про способы оптимизации времени pre-main. Также немного затрону и оптимизацию собственного кода — в нашем проекте было несколько простых моментов, которые дали хороший прирост. Возможно, этот опыт кому-нибудь пригодится. Но в целом, оптимизация after-main относится к более общей задаче оптимизации iOS приложений, поэтому на ней я подробно останавливаться не буду, т.к. в каждом приложении свои проблемы — в отличие от системной части, оптимизация которой для всех приложений одинакова.
— Есть ли у вас какой-то предпочтительный инструментарий для работы над ускорением запуска приложения?
Николай Лихогруд: Основной инструмент — переменная DYLD_PRINT_STATISTICS, которая выводит статистику работы системного загрузчика. Также есть возможность выводить загружаемые динамические библиотеки через переменную DYLD_PRINT_LIBRARIES. Для более продвинутых пользователей существуют консольные утилиты, позволяющие смотреть зависимости отдельных библиотек, таблицы символов, сколько rebase и bind будет проведено при загрузке. Но на эти параметры сложно повлиять вручную, особенно для сторонних библиотек.
— Может ли обновление iOS само по себе решить все проблемы с временем запуска (без дополнительной оптимизации)?
Николай Лихогруд: Проблема в том, что Swift еще достаточно молод и продолжает активно развиваться, независимо от iOS. Поэтому в каждое приложение на Swift встраиваются используемые Swift Standard Libraries, и поэтому для Swift нет статической линковки. Но в какой-то момент Swift стабилизируется, станет частью операционной системы, для него сделают нормальную линковку. И тогда вся эта проблема с медленным запуском проектов на Swift полностью исчезнет, на первый план выйдет оптимизация after-main, как в Objective-C. Это дело не завтрашнего дня, но ближайшего будущего.
— Если проблема на самом деле в незрелости Swift, то так ли нужно использовать сейчас именно его? Нельзя ли продолжать писать на Objective C?
Николай Лихогруд: Swift продвигается самим Apple, и за ним будущее разработки под iOS. Уже сейчас существуют фреймворки, переставшие развивать свои имплементации на Objective-C. Так или иначе, Objective-C потихоньку становится некоей архаикой. На Swift писать действительно здорово — удобно и современно. Используя Objective-C, в какой-то момент вы придете к тому, что вам не хватит objective-c — swift interoperability для использования современных фреймворков или вы столкнетесь с проблемами найма новых разработчиков, ведь большинство новичков учат сейчас Swift. Конечно, в данный момент приходится сталкиваться с определенными проблемами качества компилятора, тормозами Xcode, временем запуска. Но они решатся, когда Swift завершит свое развитие.
— Предположим, мы решим проблему с временем запуска приложения для текущей версии iOS. Может ли с обновлением iOS достигнутый результат исчезнуть?
Николай Лихогруд: Есть вероятность изменений в iOS SDK, которые приведут к увеличению времени пользовательской части загрузки, но время pre-main обновление системы вряд ли ухудшит. Правда, с каждым разом новые iOS все хуже и хуже работают на старых устройствах — iPhone 5 работал на родной iOS 6 объективно лучше, чем с последней iOS 10. Но топовые устройства это коснется еще нескоро.
— Посоветуете ли вы какие-то источники информации по проблеме ускорения запуска приложений на Swift?
Николай Лихогруд: Обязательным к изучению является доклад WWDC 2016 «Optimizing App Startup Time» . До этого разработчики Apple о проблеме практически не упоминали, а на WWDC 2016 посвятили времени запуска отдельную сессию, раскрыли детали работы загрузчика и возможные источники оптимизаций. Еще есть хороший доклад Mail.Ru на Хабре про оптимизации времени запуска, но, правда, речь там идет о приложении на Objective C, поэтому проблему сокращения pre-main упомянута вскользь — не глубже, чем на WWDC. Зато много времени уделено работе с профилировщиком, оптимизации after-main и Continuous Integration.
Как было отмечено выше, более детальные рекомендации по оптимизации времени запуска iOS приложений на Swift специалист даст в рамках своего доклада на Mobius 2017.
Также на конференции вас ждут другие доклады про оптимизацию производительности под iOS:
— The Mysterious Swift Performance. Marcin Krzyżanowski, PSPDFKit GmbH;
— 60 fps UI на iOS. Сергей Пронин, App in the Air.
Автор: JUG.ru Group