Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Синее смещение: оптимизация запуска на платформе iOS

CocoaHeads
February 13, 2018
89

Синее смещение: оптимизация запуска на платформе iOS

В докладе пойдет речь о практических подходах к оптимизации времени запуска приложения на примере Яндекс.Браузера для iOS. Рассматриваются такие вопросы, как выбор проектных и продуктовых метрик для проверки эффективности оптимизации, технические и архитектурные подходы к уменьшению времени старта, а также идеи, как можно заставить ваше приложение казаться быстрее, чем оно есть на самом деле.

CocoaHeads

February 13, 2018
Tweet

More Decks by CocoaHeads

Transcript

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

    время старта. Работа в условиях высокококонкурентного рынка. Необходимо уменьшать время старта приложения при Постановка проблемы 3
  2. 1. Разработчик вносит изменения; 2. снимается два видео старта —

    до и после изменений; 3. покадровое сравнение показывает наличие улучшений. Наивный подход с видео 6
  3. 1. оптимизации локальны и конфликтуют друг с другом, 2. деградируют

    со временем, 3. никто не гарантирует наличие эффекта для пользователя. Проблема наивного подхода: 7
  4. 1. Выбор метрик для оценки качества 2. Анализ чужого опыта

    3. Fake UI: интерфейс, доступный сразу же 4. Устранение «горячих точек» 5. Распрямление ленивых сервисов 6. Оценка полученных оптимизаций Содержание 8
  5. Критерии: 1. В мире пользователя 2. Количественные 3. Объективные Используются

    для оценки общего эффекта оптимизаций. Основные метрики 11
  6. Критерии: 1. В мире разработчика 2. Количественные 3. Объективные Используется

    для локализации проблем разработчиком. Вспомогательные метрики 12
  7. Main Эффект от добавления внешних библиотек — в мире разработчика

    ViewWillAppear Затраты на конфигурацию приложения — в мире разработчика ViewDidAppear Момент показа интерфейса — в мире пользователя UI Ready (ViewDidAppear + dispatch_async) Выбор метрик для Яндекс.Браузера 14
  8. Выделенная машина, которая по таймеру запускает самую свежую девелоперскую версию

    Яндекс.Браузера и замеряет время старта множество раз. Такой подход 1. исключает вероятностные факторы (погоду на Марсе), 2. контролирует деградацию выполненных опимизаций, 3. контролирует улучшение от вносимых правок. Сбор метрик у разработчика 16
  9. 1. Статическая линковка библиотек 2. Динамическое связывание и отложенная загрузка

    3. Использование xcassets 4. Отказ от Swift (более не актуален) Николай Лихогруд — Оптимизация времени запуска iOS-приложений https://www.youtube.com/watch?v=REaGfwq3Q5Y Чужой опыт 20
  10. Яндекс.Браузер основан на ядре Chromium. Старт ядра занимает продолжительное время.

    До старта ядра часть функциональности недоступна. Ядро 22
  11. Множество сервисов требуют для работы Ядра. Их запуск отложен до

    момента старта Ядра. Сразу после старта они запускаются одновременно. Конкурентность 30
  12. // File1.swift Chromium.performAfterStartup { /* Initiate first Chromium-depended heavy service

    */ } // File2.swift Chromium.performAfterStartup { /* Initiate another Chromium-depended heavy service */ } // And so one and so forth Конкурентность: история проблемы 31
  13. class Chromium { func performAfterStartup(_ block: @escaping () -> Void)

    { pendingBlocks.append(block) } private var pendingBlocks: [ () -> Void ] = [] private func startupFinished() { pendingBlocks.forEach { $0() } } } Конкурентность: история проблемы 32
  14. class Chromium { func performAfterStartup(_ block: @escaping () -> Void)

    { pendingBlocks.enqueue(block) } private var pendingBlocks = Queue<() -> Void>() private func startupFinished() { func runNextPendingBlock() { if let block = pendingBlocks.dequeue() { block() DispatchQueue.main.async(execute: runNextPendingBlock) } } runNextPendingBlock() } } Кооперация 34
  15. Стартуют по первому обращению, т.е. время старта может быть недетерминированным.

    Могут быть достаточно тяжелыми. class LazyService { static var instance = LazyService() init() { // I ABSOLUTELY HAVE TO READ THIS 60-MB ZIP PACKAGE AND // UNZIP IT ON MAIN QUEUE THATS WHY I'M LAZY } } Ленивые сервисы 36
  16. class LazyService { static var instance: LazyService! = nil static

    func prepare() { instance = LazyService() } init() { // I STILL ABSOLUTELY HAVE TO READ THIS 60-MB ZIP PACKAGE AND // UNZIP IT ON MAIN QUEUE but now I have a place to do it } } Отказ от ленивости 37
  17. class LazyService { static var instance: LazyService! = nil static

    func prepare() { unzip60mb { data in instance = LazyService(data: data) } } static func unzip60mb(then callback: (NSData) -> Void) { // Some background work with callback on main queue } init(data: NSData) { // Just retaining data, nothing more } } Создание графа объектов 38
  18. Если планируется внести изменения, которые потенциально могут повлиять на время

    старта, то: 1. разработчик вносит изменения, делает pull request, 2. с ветки делается сборка, проверяются метрики, 3. pull request вливается в мастер. Если разработчик случайно делает такие изменения — узнаем постфактум. Внесение изменений 43