Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Николай Лихогруд
Руководитель группы разработки Яндекс.Карт для iOS
Оптимизация времени
запуска iOS-приложений
It's really clear that the
most precious resource
we all have is time
Steve Jobs
Зачем сокращать время запуска?
4
Важное конкурентное преимущество
Зачем сокращать время запуска?
4
Важное конкурентное преимущество
Влияет на retention, настроение пользователей, оценку в сторе
Зачем сокращать время запус...
Важное конкурентное преимущество
Влияет на retention, настроение пользователей, оценку в сторе
Максимальное время запуска ...
Важное конкурентное преимущество
Влияет на retention, настроение пользователей, оценку в сторе
Максимальное время запуска ...
Актуальность
5
Множество приложений и фреймворков переходят на swift
Актуальность
5
Множество приложений и фреймворков переходят на swift
Swift нельзя компилировать в статические библиотеки
Актуальность
5
Множество приложений и фреймворков переходят на swift
Swift нельзя компилировать в статические библиотеки
Динамические биб...
Множество приложений и фреймворков переходят на swift
Swift нельзя компилировать в статические библиотеки
Динамические биб...
План
6
1. Замеры времени запуска

План
6
1. Замеры времени запуска

2. Оптимизация pre-main

План
6
1. Замеры времени запуска

2. Оптимизация pre-main

3. Оптимизация after-main

План
6
1. Замеры времени запуска

2. Оптимизация pre-main

3. Оптимизация after-main

4. Контроль результата
План
6
1. Замеры времени запуска
Что есть время запуска?
8
Что есть время запуска?
8
Что есть время запуска?
9
BestApp
Что есть время запуска?
10
Что есть время запуска?
11
Что есть время запуска?
12
BestApp
Полное время от нажатия на иконку до момента готовности к
использованию
Давно не запускалось или
не запускалось после
перезагрузки телефона
Холодный и теплый запуски
13
iPhone 5s
Теплый Холодный...
pre-main
14
1. Динамические библиотеки
pre-main
14
1. Динамические библиотеки
2. Исправление указателей, связывание
pre-main
14
1. Динамические библиотеки
2. Исправление указателей, связывание
3. Objective-C контекст
pre-main
14
1. Динамические библиотеки
2. Исправление указателей, связывание
3. Objective-C контекст
4. +load, c++ globals
pre-main
14
Холодный запуск и pre-main
15
Холодный
Теплый
0 200 400 600 800
dylib loading time rebase/binding time
ObjC setup time ini...
Полное время от нажатия на иконку до момента готовности к
использованию
Что замерять?
16
Полное время от нажатия на иконку до момента готовности к
использованию
› Замерять холодные и теплые запуски
Что замерять?...
Полное время от нажатия на иконку до момента готовности к
использованию
› Замерять холодные и теплые запуски
› Учитывать p...
Замер pre-main
17
DYLD_PRINT_STATISTICS
Total pre-main time: 677.82 milliseconds (100.0%)
dylib loading time: 520.33 milliseconds (76.7%)
rebase/binding time: 54....
Total pre-main time: 677.82 milliseconds (100.0%)
dylib loading time: 520.33 milliseconds (76.7%)
rebase/binding time: 54....
Total pre-main time: 677.82 milliseconds (100.0%)
dylib loading time: 520.33 milliseconds (76.7%)
rebase/binding time: 54....
Total pre-main time: 677.82 milliseconds (100.0%)
dylib loading time: 520.33 milliseconds (76.7%)
rebase/binding time: 54....
Total pre-main time: 677.82 milliseconds (100.0%)
dylib loading time: 520.33 milliseconds (76.7%)
rebase/binding time: 54....
Total pre-main time: 677.82 milliseconds (100.0%)
dylib loading time: 520.33 milliseconds (76.7%)
rebase/binding time: 54....
Замер after-main
19
с начала didFinishLaunching
Замер after-main
19
с начала didFinishLaunching
Замер after-main
19
› не учитывается время создания
UIApplication, UIApplicationDelegate
с начала didFinishLaunching
Замер after-main
19
с начала main
› не учитывается время создания
UIApplication, UIApplication...
Замер after-main
20
// main.swift
let start = Date().timeIntervalSince1970
let argc = Int(CommandLine.argc)
fileprivate le...
Замер after-main
20
// main.swift
let start = Date().timeIntervalSince1970
let argc = Int(CommandLine.argc)
fileprivate le...
Замер after-main
20
// main.swift
let start = Date().timeIntervalSince1970
let argc = Int(CommandLine.argc)
fileprivate le...
Важность множественных запусков
21
0
325
650
975
1300
1 2 3 4 6 7 8 9 10 11 12 13 15 16 17 18 19 20
21%
Автоматические запуски
22
libimobiledevice
› прямое взаимодействие с устройством
› не требует jailbreak
Автоматические запуски
22
libimobiledevice
23
$idevice_id -l
$ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app
$idevicede...
libimobiledevice
23
$idevice_id -l
$ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app
$idevicede...
libimobiledevice
23
$idevice_id -l
$ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app
$idevicede...
libimobiledevice
23
$idevice_id -l
$ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app
$idevicede...
libimobiledevice
23
$idevice_id -l
$ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app
$idevicede...
libimobiledevice + DYLD_PRINT_STATISTICS
24
$idevicedebug -e DYLD_PRINT_STATISTICS=1 
-u e1bbc7f0353933938929c3279fc6b7f51...
Релизная конфигурация с оптимизациями
› отключены ассерты и функциональность для дебага
exit(0) после завершения загрузки,...
На каждой итерации:
1. idevicediagnostics restart, дождаться загрузки
2. idevicedebug run - холодный запуск
3. idevicedebu...
pre-main: перезагрузка vs переустановка
27
Перезагрузка
Переустановка
Теплый
0 225 450 675 900
dyld rebase&bind objc-setup...
На каждой итерации:
1. ideviceinstaller -i
2. idevicedebug run - холодный запуск
3. idevicedebug run - теплый запуск
4. Об...
Запуски на разных устройствах
29
Времязапуска,мс
iPhone 5 iPhone 5s iPhone 6 iPhone 6s iPhone 7
8851 014
1 930
2 333
4 470...
Пустой проект на swift
Пустой проект: Objective-C, iPhone 5s
31
Total pre-main time: 19.40 milliseconds (100.0%)
dylib loading time: 0.72 millise...
Пустой проект: swift, iPhone 5s
Total pre-main time: 232.19 milliseconds (100.0%)
dylib loading time: 209.33 milliseconds ...
Пустой проект: swift, iPhone 5
33
Total pre-main time: 837.72 milliseconds (100.0%)
dylib loading time: 789.01 millisecond...
Список загружаемых библиотек
34
Бинарь приложения + 146 системных библиотек
Для проекта на swift дополнительно загружаются 9 библиотек из
бандла приложени...
Swift продолжает активно
развиваться, не обеспечивает
обратную совместимость и не
является частью iOS
в отличие от Objecti...
Выводы
37
Загрузка системных библиотек оптимизирована
Выводы
37
Загрузка системных библиотек оптимизирована
Библиотеки из бандла приложения грузятся долго
Выводы
37
Загрузка системных библиотек оптимизирована
Библиотеки из бандла приложения грузятся долго
Любое приложения на swift будет...
2. Оптимизация pre-main
Уменьшить число загружаемых динамических библиотек
Уменьшить использование Objective-C:
› Использовать swift
Перенести код...
Уменьшить число загружаемых динамических библиотек
Уменьшить размер бинарного файла
› вынести символы в динамические библи...
41
...
use_frameworks!
target :OriginalApp do
pod 'AlamofireObjectMapper'
pod 'AlamofireImage'
pod 'FacebookLogin'
pod 'Ob...
Тестовый: Функциональность
42
func handleDirectionsRequest(_ request: MKDirectionsRequest) {
//... использует MapKit из iO...
Тестовый: Время запуска
43
Total pre-main time: 741.74 milliseconds (100.0%)
dylib loading time: 627.80 milliseconds (84.6...
Добавились для каждого пода, собираемого из
исходных файлов
Добавились новые библиотеки swift standard
libraries
Динамичес...
$gem install cocoapods-amimono
Плагин для cocoa pods
Патчит скрипты и xcconfig-и cocoapods:
› Линкует объектные файлы, ост...
cocoapods-amimono
46
...
plugin 'cocoapods-amimono'
use_frameworks!
target :StaticPodsApp do
pod ‘AlamofireObjectMapper’
....
cocoapods-amimono
46
...
plugin 'cocoapods-amimono'
use_frameworks!
target :StaticPodsApp do
pod ‘AlamofireObjectMapper’
....
cocoapods-amimono
46
...
plugin 'cocoapods-amimono'
use_frameworks!
target :StaticPodsApp do
pod ‘AlamofireObjectMapper’
....
Тестовый: Время запуска
47
Total pre-main time: 741.74 milliseconds (100.0%)
dylib loading time: 627.80 milliseconds (84.6...
После cocoapods-amimono
48
Total pre-main time: 413.13 milliseconds (100.0%)
dylib loading time: 320.20 milliseconds (77.5...
swift standard libraries
49
В пустом проекте
В тестовом проекте
swift standard libraries
50
В пустом проекте
В тестовом проекте
import CoreLocation в *.swift
#import <CoreLocation/CoreLocation.h> в bridging header
Приводят к добавлению libswiftCoreLo...
Некоторые библиотеки
SDK стоит
использовать только в
Objective C
*Пока библиотеки swift не станут частью системы
Обернуть CoreLocation в Objective-C с таким же интерфейсом,
но другим префиксом
› CLWLocationManger, CLWLocation, CLWHeadi...
Обернуть CoreLocation в Objective-C с таким же интерфейсом,
но другим префиксом
› CLWLocationManger, CLWLocation, CLWHeadi...
swift standard libraries
54
import AVFoundation
libswiftAVFoundation.dylib
libswiftCoreMedia.dylib
libswiftCoreAudio.dylib...
После cocoapods-amimono
55
Total pre-main time: 413.13 milliseconds (100.0%)
dylib loading time: 320.20 milliseconds (77.5...
Objective C обертки
56
Total pre-main time: 294.30 milliseconds (100.0%)
dylib loading time: 210.64 milliseconds (71.5%)
r...
Objective C обертки
56
Total pre-main time: 294.30 milliseconds (100.0%)
dylib loading time: 210.64 milliseconds (71.5%)
r...
На старте должны
загружаться только
необходимые символы
Все остальное можно загружать лениво
В рассматриваемом примере карта и распознавание звука не
включаются на старте
› Можно конвертировать YandexSpeechKit и Yan...
Создать отдельный таргет «Cocoa Touch Framework»
В созданный таргет
› Добавить pod со статической библиотекой в Podfile
› ...
Для основного таргета
› Убрать pod со статической библиотекой в Podfile
› Добавить новый framework в «embedded binaries»
›...
dlopen: загрузка библиотеки
61
#import <dlfcn.h>
NSString *frameworksPath = [[NSBundle mainBundle] privateFrameworksPath];...
dlopen: загрузка библиотеки
61
#import <dlfcn.h>
NSString *frameworksPath = [[NSBundle mainBundle] privateFrameworksPath];...
dlopen: загрузка библиотеки
61
#import <dlfcn.h>
NSString *frameworksPath = [[NSBundle mainBundle] privateFrameworksPath];...
dlsym: функции и глобальные переменные
62
void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc");
someFuncPtr(5);
NSString *...
dlsym: функции и глобальные переменные
62
void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc");
someFuncPtr(5);
NSString *...
dlsym: функции и глобальные переменные
62
void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc");
someFuncPtr(5);
NSString *...
dlsym: функции и глобальные переменные
62
void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc");
someFuncPtr(5);
NSString *...
dlsym: функции и глобальные переменные
62
void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc");
someFuncPtr(5);
NSString *...
dlsym: классы
63
#import <SomeFramework/SomeFramework.h>
Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_Som...
dlsym: классы
63
#import <SomeFramework/SomeFramework.h>
Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_Som...
dlsym: классы
63
#import <SomeFramework/SomeFramework.h>
Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_Som...
dlsym: классы
63
#import <SomeFramework/SomeFramework.h>
Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_Som...
dlsym: классы
63
#import <SomeFramework/SomeFramework.h>
Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_Som...
dlsym: классы
63
#import <SomeFramework/SomeFramework.h>
Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_Som...
Objective C обертки
64
Total pre-main time: 294.30 milliseconds (100.0%)
dylib loading time: 210.64 milliseconds (71.5%)
r...
65
Total pre-main time: 263.41 milliseconds (100.0%)
dylib loading time: 210.72 milliseconds (79.9%)
rebase/binding time: ...
Таким же образом можно загружать динамические
библиотеки из подов и собственные модули
приложения
dlopen
66
Итог
67
Оригинальный
amimono
objc обертки
dlopen
пустой
0 200 400 600 800
dylib rebase&bind objc-setup initialization
3. Оптимизация after-main
Нужно на старте делать
как можно меньше
работы
Избыточное создание сущностей на старте
Избыточный UI
Инициализация, необязательная для показа стартового UI
На что обрати...
Инъекция зависимостей
71
class RootViewController {
init(searchFacade: SearchFacade, routingFacade: RoutingFacade) {
...
}...
Инъекция зависимостей
72
RootViewController
Инъекция зависимостей
72
RootViewController
SearchFacade RoutingFacade
Инъекция зависимостей
72
RootViewController
SearchManager RoutingPresenter... ...
SearchFacade RoutingFacade
Инъекция зависимостей
72
RootViewController
SearchManager RoutingPresenter... ...
SearchFacade RoutingFacade
...... ... ...
Оформить зависимости сущностей в протоколы
В конструкторе принимать реализацию протокола
В реализации использовать lazy va...
Инъекция ленивых зависимостей
74
protocol RootViewControllerDeps {
var searchFacade: SearchFacade { get }
var routingFacad...
Инъекция ленивых зависимостей
75
protocol RootViewControllerDeps {
var searchFacade: SearchFacade { get }
var routingFacad...
Composition Root
76
class ApplicationDeps: SearchFacadeImplDeps, RoutingFacadeImplDeps,
RootViewControllerDeps {
lazy var ...
На старте создаются только нужные сущности
Не используется рефлексия
resolve проверяется компилятором
Инъекция ленивых зав...
Сократить view-tree, создаваемое на старте
› ленивое создание контейнерных view и view-контроллеров
› ленивое создание vie...
Оптимизация UI в Яндекс.Картах
79
Оптимизация UI в Яндекс.Картах
79
Оптимизация UI в Яндекс.Картах
79
› Ленивая загрузка шрифтов
› Графику, генерируемую программно, отрисовать в ассеты
› Текст также отрисовать в ассеты
› Сло...
Цель - как можно быстрее разблокировать UI для пользователя
Пока UI не загружен - выполнять только необходимые
действия, п...
Отложили на 0.3с
› Синхронизацию закладок
› Отображение закладок на карте
› Загрузку конфигурации приложения
› Настройку а...
4. Сохранение результата
Continuous integration
84
Встроить вызов скрипта c автозапусками в CI
Continuous integration
84
Встроить вызов скрипта c автозапусками в CI
Собирать статистику запусков
Continuous integration
84
Встроить вызов скрипта c автозапусками в CI
Собирать статистику запусков
Обеспечить доступ к статистике
Continuous integra...
Логировать создание зависимостей в composition root
Логировать список загружаемых динамических бибиотек через
objc_copyIma...
Информация о процессе через sysctl
86
#import <sys/sysctl.h>
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };...
Время старта через sysctl
87
#import <sys/sysctl.h>
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
struct k...
Замерять полное время запуска от нажатия на иконку
приложения через sysctl
Отправлять в системы аналитики
Проверять график...
Заключение
Яндекс.Карты - холодный, 5s
90
До оптимизаций
После оптимизаций
0 625 1250 1875 2500
dylib loading time rebase/binding tim...
Яндекс.Карты - холодный запуск
91
Времязапуска,мс
iPhone 5 iPhone 5s iPhone 6 iPhone 6s iPhone 7
607695
1 342
1 620
3 148
...
Яндекс.Карты - теплый, 5s
92
До оптимизаций
После оптимизаций
0 300 600 900 1200
dylib loading time rebase/binding time
Ob...
Яндекс.Карты - теплый запуск
93
Времязапуска,мс
iPhone 5 iPhone 5s iPhone 6 iPhone 6s iPhone 7
283354
676752
1 347
533
721...
Примеры из доклада
WWDC 2016 "Optimizing App Startup Time"
libimobiledevice
Доклад Почты Mail.Ru
cocoapods-amimono
sysctl
...
Николай Лихогруд
Руководитель группы разработки Яндекс.Карт для iOS
likhogrud@yandex-team.ru
Спасибо за внимание
Оптимизация времени запуска iOS-приложений / Николай Лихогруд (Яндекс)
Upcoming SlideShare
Loading in …5
×

Оптимизация времени запуска iOS-приложений / Николай Лихогруд (Яндекс)

233 views

Published on

РИТ++ 2017, App's Conf
Зал Найроби, 6 июня, 15:00

Тезисы:
http://appsconf.ru/2017/abstracts/2605.html

Доклад посвящен проблеме ускорения запуска приложений на мобильных устройствах под управлением iOS — как правильно замерить время запуска, оптимизировать системную и пользовательскую части, гарантировать сохранение результата в дальнейшем.

Рассказ основан на личном опыте оптимизации запуска Яндекс.Карт, описывает весь процесс от осознания проблемы до получения результата, подкреплен множеством технических подробностей и реальных примеров. Доклад является концептуальным, содержит конкретные предложения по разработке мобильных приложений с быстрым запуском и будет полезен iOS-разработчикам любого уровня.

Published in: Engineering
  • Be the first to comment

Оптимизация времени запуска iOS-приложений / Николай Лихогруд (Яндекс)

  1. 1. Николай Лихогруд Руководитель группы разработки Яндекс.Карт для iOS Оптимизация времени запуска iOS-приложений
  2. 2. It's really clear that the most precious resource we all have is time Steve Jobs
  3. 3. Зачем сокращать время запуска? 4
  4. 4. Важное конкурентное преимущество Зачем сокращать время запуска? 4
  5. 5. Важное конкурентное преимущество Влияет на retention, настроение пользователей, оценку в сторе Зачем сокращать время запуска? 4
  6. 6. Важное конкурентное преимущество Влияет на retention, настроение пользователей, оценку в сторе Максимальное время запуска - 20s, при превышении система останавливает загрузку Зачем сокращать время запуска? 4
  7. 7. Важное конкурентное преимущество Влияет на retention, настроение пользователей, оценку в сторе Максимальное время запуска - 20s, при превышении система останавливает загрузку › Актуально для слабых устройств Зачем сокращать время запуска? 4
  8. 8. Актуальность 5
  9. 9. Множество приложений и фреймворков переходят на swift Актуальность 5
  10. 10. Множество приложений и фреймворков переходят на swift Swift нельзя компилировать в статические библиотеки Актуальность 5
  11. 11. Множество приложений и фреймворков переходят на swift Swift нельзя компилировать в статические библиотеки Динамические библиотеки грузятся долго Актуальность 5
  12. 12. Множество приложений и фреймворков переходят на swift Swift нельзя компилировать в статические библиотеки Динамические библиотеки грузятся долго WWDC 2016: Optimizing App Startup Time Актуальность 5
  13. 13. План 6
  14. 14. 1. Замеры времени запуска
 План 6
  15. 15. 1. Замеры времени запуска
 2. Оптимизация pre-main
 План 6
  16. 16. 1. Замеры времени запуска
 2. Оптимизация pre-main
 3. Оптимизация after-main
 План 6
  17. 17. 1. Замеры времени запуска
 2. Оптимизация pre-main
 3. Оптимизация after-main
 4. Контроль результата План 6
  18. 18. 1. Замеры времени запуска
  19. 19. Что есть время запуска? 8
  20. 20. Что есть время запуска? 8
  21. 21. Что есть время запуска? 9 BestApp
  22. 22. Что есть время запуска? 10
  23. 23. Что есть время запуска? 11
  24. 24. Что есть время запуска? 12 BestApp Полное время от нажатия на иконку до момента готовности к использованию
  25. 25. Давно не запускалось или не запускалось после перезагрузки телефона Холодный и теплый запуски 13 iPhone 5s Теплый Холодный 2 333мс 1 203мс
  26. 26. pre-main 14
  27. 27. 1. Динамические библиотеки pre-main 14
  28. 28. 1. Динамические библиотеки 2. Исправление указателей, связывание pre-main 14
  29. 29. 1. Динамические библиотеки 2. Исправление указателей, связывание 3. Objective-C контекст pre-main 14
  30. 30. 1. Динамические библиотеки 2. Исправление указателей, связывание 3. Objective-C контекст 4. +load, c++ globals pre-main 14
  31. 31. Холодный запуск и pre-main 15 Холодный Теплый 0 200 400 600 800 dylib loading time rebase/binding time ObjC setup time initializer time 0.11s 0.73s
  32. 32. Полное время от нажатия на иконку до момента готовности к использованию Что замерять? 16
  33. 33. Полное время от нажатия на иконку до момента готовности к использованию › Замерять холодные и теплые запуски Что замерять? 16
  34. 34. Полное время от нажатия на иконку до момента готовности к использованию › Замерять холодные и теплые запуски › Учитывать pre-main Что замерять? 16
  35. 35. Замер pre-main 17 DYLD_PRINT_STATISTICS
  36. 36. Total pre-main time: 677.82 milliseconds (100.0%) dylib loading time: 520.33 milliseconds (76.7%) rebase/binding time: 54.31 milliseconds (8.0%) ObjC setup time: 43.16 milliseconds (6.3%) initializer time: 59.99 milliseconds (8.8%) slowest intializers : libSystem.B.dylib : 4.35 milliseconds (0.6%) TestApp : 74.83 milliseconds (11.0%) DYLD_PRINT_STATISTICS 18
  37. 37. Total pre-main time: 677.82 milliseconds (100.0%) dylib loading time: 520.33 milliseconds (76.7%) rebase/binding time: 54.31 milliseconds (8.0%) ObjC setup time: 43.16 milliseconds (6.3%) initializer time: 59.99 milliseconds (8.8%) slowest intializers : libSystem.B.dylib : 4.35 milliseconds (0.6%) TestApp : 74.83 milliseconds (11.0%) DYLD_PRINT_STATISTICS 18
  38. 38. Total pre-main time: 677.82 milliseconds (100.0%) dylib loading time: 520.33 milliseconds (76.7%) rebase/binding time: 54.31 milliseconds (8.0%) ObjC setup time: 43.16 milliseconds (6.3%) initializer time: 59.99 milliseconds (8.8%) slowest intializers : libSystem.B.dylib : 4.35 milliseconds (0.6%) TestApp : 74.83 milliseconds (11.0%) DYLD_PRINT_STATISTICS 18
  39. 39. Total pre-main time: 677.82 milliseconds (100.0%) dylib loading time: 520.33 milliseconds (76.7%) rebase/binding time: 54.31 milliseconds (8.0%) ObjC setup time: 43.16 milliseconds (6.3%) initializer time: 59.99 milliseconds (8.8%) slowest intializers : libSystem.B.dylib : 4.35 milliseconds (0.6%) TestApp : 74.83 milliseconds (11.0%) DYLD_PRINT_STATISTICS 18
  40. 40. Total pre-main time: 677.82 milliseconds (100.0%) dylib loading time: 520.33 milliseconds (76.7%) rebase/binding time: 54.31 milliseconds (8.0%) ObjC setup time: 43.16 milliseconds (6.3%) initializer time: 59.99 milliseconds (8.8%) slowest intializers : libSystem.B.dylib : 4.35 milliseconds (0.6%) TestApp : 74.83 milliseconds (11.0%) DYLD_PRINT_STATISTICS 18
  41. 41. Total pre-main time: 677.82 milliseconds (100.0%) dylib loading time: 520.33 milliseconds (76.7%) rebase/binding time: 54.31 milliseconds (8.0%) ObjC setup time: 43.16 milliseconds (6.3%) initializer time: 59.99 milliseconds (8.8%) slowest intializers : libSystem.B.dylib : 4.35 milliseconds (0.6%) TestApp : 74.83 milliseconds (11.0%) DYLD_PRINT_STATISTICS 18
  42. 42. Замер after-main 19
  43. 43. с начала didFinishLaunching Замер after-main 19
  44. 44. с начала didFinishLaunching Замер after-main 19 › не учитывается время создания UIApplication, UIApplicationDelegate
  45. 45. с начала didFinishLaunching Замер after-main 19 с начала main › не учитывается время создания UIApplication, UIApplicationDelegate
  46. 46. Замер after-main 20 // main.swift let start = Date().timeIntervalSince1970 let argc = Int(CommandLine.argc) fileprivate let argv = UnsafeMutableRawPointer(CommandLine.unsafeArgv) .bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: argc) UIApplicationMain(CommandLine.argc, argv, nil, NSStringFromClass(AppDelegate.self))
  47. 47. Замер after-main 20 // main.swift let start = Date().timeIntervalSince1970 let argc = Int(CommandLine.argc) fileprivate let argv = UnsafeMutableRawPointer(CommandLine.unsafeArgv) .bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: argc) UIApplicationMain(CommandLine.argc, argv, nil, NSStringFromClass(AppDelegate.self))
  48. 48. Замер after-main 20 // main.swift let start = Date().timeIntervalSince1970 let argc = Int(CommandLine.argc) fileprivate let argv = UnsafeMutableRawPointer(CommandLine.unsafeArgv) .bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: argc) UIApplicationMain(CommandLine.argc, argv, nil, NSStringFromClass(AppDelegate.self))
  49. 49. Важность множественных запусков 21 0 325 650 975 1300 1 2 3 4 6 7 8 9 10 11 12 13 15 16 17 18 19 20 21%
  50. 50. Автоматические запуски 22
  51. 51. libimobiledevice › прямое взаимодействие с устройством › не требует jailbreak Автоматические запуски 22
  52. 52. libimobiledevice 23 $idevice_id -l $ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app $idevicedebug -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 run com.some.app $idevicediagnostics restart
  53. 53. libimobiledevice 23 $idevice_id -l $ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app $idevicedebug -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 run com.some.app $idevicediagnostics restart
  54. 54. libimobiledevice 23 $idevice_id -l $ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app $idevicedebug -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 run com.some.app $idevicediagnostics restart
  55. 55. libimobiledevice 23 $idevice_id -l $ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app $idevicedebug -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 run com.some.app $idevicediagnostics restart
  56. 56. libimobiledevice 23 $idevice_id -l $ideviceinstaller -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 -i SomeApp.app $idevicedebug -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 run com.some.app $idevicediagnostics restart
  57. 57. libimobiledevice + DYLD_PRINT_STATISTICS 24 $idevicedebug -e DYLD_PRINT_STATISTICS=1 -u e1bbc7f0353933938929c3279fc6b7f51fac1c02 run test.app Total pre-main time: 52.85 milliseconds (100.0%) dylib loading time: 7.21 milliseconds (13.6%) rebase/binding time: 3.22 milliseconds (6.1%) ...
  58. 58. Релизная конфигурация с оптимизациями › отключены ассерты и функциональность для дебага exit(0) после завершения загрузки, если
 processInfo.environment["DYLD_PRINT_STATISTICS"] != nil Сборка для тестов 25
  59. 59. На каждой итерации: 1. idevicediagnostics restart, дождаться загрузки 2. idevicedebug run - холодный запуск 3. idevicedebug run - теплый запуск 4. Обработать вывод, сохранить лог Организация скрипта 26
  60. 60. pre-main: перезагрузка vs переустановка 27 Перезагрузка Переустановка Теплый 0 225 450 675 900 dyld rebase&bind objc-setup initialization
  61. 61. На каждой итерации: 1. ideviceinstaller -i 2. idevicedebug run - холодный запуск 3. idevicedebug run - теплый запуск 4. Обработать вывод, сохранить лог Организация скрипта 28
  62. 62. Запуски на разных устройствах 29 Времязапуска,мс iPhone 5 iPhone 5s iPhone 6 iPhone 6s iPhone 7 8851 014 1 930 2 333 4 470 533721 1 0881 203 2 195 Теплый Холодный
  63. 63. Пустой проект на swift
  64. 64. Пустой проект: Objective-C, iPhone 5s 31 Total pre-main time: 19.40 milliseconds (100.0%) dylib loading time: 0.72 milliseconds (3.7%) rebase/binding time: 1.14 milliseconds (5.8%) ObjC setup time: 5.02 milliseconds (25.9%) initializer time: 12.51 milliseconds (64.4%) slowest intializers : libSystem.B.dylib : 10.27 milliseconds (52.9%) CoreFoundation : 0.66 milliseconds (3.4%)
  65. 65. Пустой проект: swift, iPhone 5s Total pre-main time: 232.19 milliseconds (100.0%) dylib loading time: 209.33 milliseconds (90.1%) rebase/binding time: 7.98 milliseconds (3.4%) ObjC setup time: 8.49 milliseconds (3.6%) initializer time: 6.37 milliseconds (2.7%) slowest intializers : libSystem.B.dylib : 3.14 milliseconds (1.3%) 32
  66. 66. Пустой проект: swift, iPhone 5 33 Total pre-main time: 837.72 milliseconds (100.0%) dylib loading time: 789.01 milliseconds (94.1%) rebase/binding time: 15.41 milliseconds (1.8%) ObjC setup time: 13.52 milliseconds (1.6%) initializer time: 19.57 milliseconds (2.3%) slowest intializers : libSystem.B.dylib : 5.56 milliseconds (0.6%) 3.5x!
  67. 67. Список загружаемых библиотек 34
  68. 68. Бинарь приложения + 146 системных библиотек Для проекта на swift дополнительно загружаются 9 библиотек из бандла приложения, т.н. swift standard libraries Список загружаемых библиотек 35 ... dyld: loaded: /private/var/containers/Bundle/Application/ AE358ED8-8FE3-4E90-88C8-FC602FDEA528/Empty.app/Frameworks/libswiftCore.dylib dyld: loaded: /private/var/containers/Bundle/Application/ AE358ED8-8FE3-4E90-88C8-FC602FDEA528/Empty.app/Frameworks/libswiftUIKit.dylib ...
  69. 69. Swift продолжает активно развиваться, не обеспечивает обратную совместимость и не является частью iOS в отличие от ObjectiveC
  70. 70. Выводы 37
  71. 71. Загрузка системных библиотек оптимизирована Выводы 37
  72. 72. Загрузка системных библиотек оптимизирована Библиотеки из бандла приложения грузятся долго Выводы 37
  73. 73. Загрузка системных библиотек оптимизирована Библиотеки из бандла приложения грузятся долго Любое приложения на swift будет грузиться на iPhone 5 минимум секунду при холодном запуске Выводы 37
  74. 74. 2. Оптимизация pre-main
  75. 75. Уменьшить число загружаемых динамических библиотек Уменьшить использование Objective-C: › Использовать swift Перенести код +load в +initialize Избавиться от статических с++ переменных со сложными конструкторами На что теоретически можно повлиять? 39
  76. 76. Уменьшить число загружаемых динамических библиотек Уменьшить размер бинарного файла › вынести символы в динамические библиотеки › грузить лениво через dlopen На что практически можно повлиять?* 40 *в готовом проекте на swift
  77. 77. 41 ... use_frameworks! target :OriginalApp do pod 'AlamofireObjectMapper' pod 'AlamofireImage' pod 'FacebookLogin' pod 'ObjectMapper' pod 'PhoneNumberKit' pod 'UIImageEffects' pod 'pop' pod 'KissXML' pod 'MTDates' pod 'Punycode-Cocoa' pod 'YandexSpeechKit' pod 'YandexMapKit' end Тестовый: Podfile use_frameworks! из-за подов на swift много подов, собираемых из исходных файлов
  78. 78. Тестовый: Функциональность 42 func handleDirectionsRequest(_ request: MKDirectionsRequest) { //... использует MapKit из iOS SDK } func presentMap() { //... использует YandexMapKit } func startSpeechRecognition() { //... использует YandexSpeechKit и AVFoundation }
  79. 79. Тестовый: Время запуска 43 Total pre-main time: 741.74 milliseconds (100.0%) dylib loading time: 627.80 milliseconds (84.6%) rebase/binding time: 28.87 milliseconds (3.8%) ObjC setup time: 36.00 milliseconds (4.8%) initializer time: 49.04 milliseconds (6.6%) slowest intializers : libSystem.B.dylib : 4.57 milliseconds (0.6%) OriginalApp : 28.50 milliseconds (3.8%) 3x!! Тестовый Пустой
  80. 80. Добавились для каждого пода, собираемого из исходных файлов Добавились новые библиотеки swift standard libraries Динамические библиотеки 44
  81. 81. $gem install cocoapods-amimono Плагин для cocoa pods Патчит скрипты и xcconfig-и cocoapods: › Линкует объектные файлы, оставшиеся после сборки подов, непосредственно в бинарный файл приложения cocoapods-amimono 45
  82. 82. cocoapods-amimono 46 ... plugin 'cocoapods-amimono' use_frameworks! target :StaticPodsApp do pod ‘AlamofireObjectMapper’ ... pod 'YandexSpeechKit' pod 'YandexMapKit' end post_install do |installer| require 'cocoapods-amimono/patcher' Amimono::Patcher.patch!(installer) end
  83. 83. cocoapods-amimono 46 ... plugin 'cocoapods-amimono' use_frameworks! target :StaticPodsApp do pod ‘AlamofireObjectMapper’ ... pod 'YandexSpeechKit' pod 'YandexMapKit' end post_install do |installer| require 'cocoapods-amimono/patcher' Amimono::Patcher.patch!(installer) end Добавить plugin в Podfile
  84. 84. cocoapods-amimono 46 ... plugin 'cocoapods-amimono' use_frameworks! target :StaticPodsApp do pod ‘AlamofireObjectMapper’ ... pod 'YandexSpeechKit' pod 'YandexMapKit' end post_install do |installer| require 'cocoapods-amimono/patcher' Amimono::Patcher.patch!(installer) end Добавить plugin в Podfile Добавить post_install в Podfile
  85. 85. Тестовый: Время запуска 47 Total pre-main time: 741.74 milliseconds (100.0%) dylib loading time: 627.80 milliseconds (84.6%) rebase/binding time: 28.87 milliseconds (3.8%) ObjC setup time: 36.00 milliseconds (4.8%) initializer time: 49.04 milliseconds (6.6%) 3x!! оригинал пустой 0 200 400 600 800
  86. 86. После cocoapods-amimono 48 Total pre-main time: 413.13 milliseconds (100.0%) dylib loading time: 320.20 milliseconds (77.5%) rebase/binding time: 28.71 milliseconds (6.9%) ObjC setup time: 21.40 milliseconds (5.1%) initializer time: 42.80 milliseconds (10.3%) оригинал aminomo пустой
  87. 87. swift standard libraries 49 В пустом проекте В тестовом проекте
  88. 88. swift standard libraries 50 В пустом проекте В тестовом проекте
  89. 89. import CoreLocation в *.swift #import <CoreLocation/CoreLocation.h> в bridging header Приводят к добавлению libswiftCoreLocation.dylib в бандл приложения swift standard libraries 51
  90. 90. Некоторые библиотеки SDK стоит использовать только в Objective C *Пока библиотеки swift не станут частью системы
  91. 91. Обернуть CoreLocation в Objective-C с таким же интерфейсом, но другим префиксом › CLWLocationManger, CLWLocation, CLWHeading и т.д. 53 Objective C обертки
  92. 92. Обернуть CoreLocation в Objective-C с таким же интерфейсом, но другим префиксом › CLWLocationManger, CLWLocation, CLWHeading и т.д. 53 Objective C обертки Использовать в swift только эти обертки › Тогда libswiftCoreLocation.dylib не добавляется
  93. 93. swift standard libraries 54 import AVFoundation libswiftAVFoundation.dylib libswiftCoreMedia.dylib libswiftCoreAudio.dylib import MapKit libswiftMapKit.dylib libswiftCoreLocation.dylib import CoreLocation
  94. 94. После cocoapods-amimono 55 Total pre-main time: 413.13 milliseconds (100.0%) dylib loading time: 320.20 milliseconds (77.5%) rebase/binding time: 28.71 milliseconds (6.9%) ObjC setup time: 21.40 milliseconds (5.1%) initializer time: 42.80 milliseconds (10.3%) оригинал aminomo пустой
  95. 95. Objective C обертки 56 Total pre-main time: 294.30 milliseconds (100.0%) dylib loading time: 210.64 milliseconds (71.5%) rebase/binding time: 22.18 milliseconds (7.5%) ObjC setup time: 19.08 milliseconds (6.4%) initializer time: 42.38 milliseconds (14.4%) оригинал обертки пустой
  96. 96. Objective C обертки 56 Total pre-main time: 294.30 milliseconds (100.0%) dylib loading time: 210.64 milliseconds (71.5%) rebase/binding time: 22.18 milliseconds (7.5%) ObjC setup time: 19.08 milliseconds (6.4%) initializer time: 42.38 milliseconds (14.4%) оригинал обертки пустой
  97. 97. На старте должны загружаться только необходимые символы Все остальное можно загружать лениво
  98. 98. В рассматриваемом примере карта и распознавание звука не включаются на старте › Можно конвертировать YandexSpeechKit и YandexMapKit в динамические библиотеки и лениво загружать через dlopen Неизбежно уменьшит rebase&bind, obj startup, initialization Уменьшение размера бинарного файла 58
  99. 99. Создать отдельный таргет «Cocoa Touch Framework» В созданный таргет › Добавить pod со статической библиотекой в Podfile › Прилинковать недостающие зависимости static lib -> dynamic lib 59
  100. 100. Для основного таргета › Убрать pod со статической библиотекой в Podfile › Добавить новый framework в «embedded binaries» › Перенести ресурсы › Сделать Objective-C обертки для ленивой загрузки символов библиотеки static lib -> dynamic lib 60
  101. 101. dlopen: загрузка библиотеки 61 #import <dlfcn.h> NSString *frameworksPath = [[NSBundle mainBundle] privateFrameworksPath]; NSString *dyLib = @"SomeFramework.framework/SomeFramework"; NSString *path = [NSString stringWithFormat:@"%@/%@", frameworksPath, dyLib]; const char *pathStr = [path cStringUsingEncoding:NSASCIIStringEncoding]; void *handle = dlopen(pathStr, RTLD_LAZY);
  102. 102. dlopen: загрузка библиотеки 61 #import <dlfcn.h> NSString *frameworksPath = [[NSBundle mainBundle] privateFrameworksPath]; NSString *dyLib = @"SomeFramework.framework/SomeFramework"; NSString *path = [NSString stringWithFormat:@"%@/%@", frameworksPath, dyLib]; const char *pathStr = [path cStringUsingEncoding:NSASCIIStringEncoding]; void *handle = dlopen(pathStr, RTLD_LAZY);
  103. 103. dlopen: загрузка библиотеки 61 #import <dlfcn.h> NSString *frameworksPath = [[NSBundle mainBundle] privateFrameworksPath]; NSString *dyLib = @"SomeFramework.framework/SomeFramework"; NSString *path = [NSString stringWithFormat:@"%@/%@", frameworksPath, dyLib]; const char *pathStr = [path cStringUsingEncoding:NSASCIIStringEncoding]; void *handle = dlopen(pathStr, RTLD_LAZY);
  104. 104. dlsym: функции и глобальные переменные 62 void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc"); someFuncPtr(5); NSString *__autoreleasing *someGlobalVarPtr = (NSString *__autoreleasing *)dlsym(handle, "SomeGlobalVar"); NSLog(@"%@", *someGlobalVarPtr);
  105. 105. dlsym: функции и глобальные переменные 62 void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc"); someFuncPtr(5); NSString *__autoreleasing *someGlobalVarPtr = (NSString *__autoreleasing *)dlsym(handle, "SomeGlobalVar"); NSLog(@"%@", *someGlobalVarPtr);
  106. 106. dlsym: функции и глобальные переменные 62 void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc"); someFuncPtr(5); NSString *__autoreleasing *someGlobalVarPtr = (NSString *__autoreleasing *)dlsym(handle, "SomeGlobalVar"); NSLog(@"%@", *someGlobalVarPtr);
  107. 107. dlsym: функции и глобальные переменные 62 void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc"); someFuncPtr(5); NSString *__autoreleasing *someGlobalVarPtr = (NSString *__autoreleasing *)dlsym(handle, "SomeGlobalVar"); NSLog(@"%@", *someGlobalVarPtr);
  108. 108. dlsym: функции и глобальные переменные 62 void (*someFuncPtr)(int) = dlsym(handle, "SomeFunc"); someFuncPtr(5); NSString *__autoreleasing *someGlobalVarPtr = (NSString *__autoreleasing *)dlsym(handle, "SomeGlobalVar"); NSLog(@"%@", *someGlobalVarPtr);
  109. 109. dlsym: классы 63 #import <SomeFramework/SomeFramework.h> Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_SomeClass"); SomeClass *someObj = [[someClass alloc] initWithParameter: param]; [someObj someMethod];
  110. 110. dlsym: классы 63 #import <SomeFramework/SomeFramework.h> Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_SomeClass"); SomeClass *someObj = [[someClass alloc] initWithParameter: param]; [someObj someMethod];
  111. 111. dlsym: классы 63 #import <SomeFramework/SomeFramework.h> Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_SomeClass"); SomeClass *someObj = [[someClass alloc] initWithParameter: param]; [someObj someMethod];
  112. 112. dlsym: классы 63 #import <SomeFramework/SomeFramework.h> Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_SomeClass"); SomeClass *someObj = [[someClass alloc] initWithParameter: param]; [someObj someMethod];
  113. 113. dlsym: классы 63 #import <SomeFramework/SomeFramework.h> Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_SomeClass"); SomeClass *someObj = [[someClass alloc] initWithParameter: param]; [someObj someMethod];
  114. 114. dlsym: классы 63 #import <SomeFramework/SomeFramework.h> Class someClass = (__bridge Class)dlsym(handle, "OBJC_CLASS_$_SomeClass"); SomeClass *someObj = [[someClass alloc] initWithParameter: param]; [someObj someMethod];
  115. 115. Objective C обертки 64 Total pre-main time: 294.30 milliseconds (100.0%) dylib loading time: 210.64 milliseconds (71.5%) rebase/binding time: 22.18 milliseconds (7.5%) ObjC setup time: 19.08 milliseconds (6.4%) initializer time: 42.38 milliseconds (14.4%) оригинал обертки пустой
  116. 116. 65 Total pre-main time: 263.41 milliseconds (100.0%) dylib loading time: 210.72 milliseconds (79.9%) rebase/binding time: 16.89 milliseconds (6.4%) ObjC setup time: 17.47 milliseconds (6.6%) initializer time: 18.32 milliseconds (6.9%) Уменьшение размера бинарного файла оригинал dlopen пустой
  117. 117. Таким же образом можно загружать динамические библиотеки из подов и собственные модули приложения dlopen 66
  118. 118. Итог 67 Оригинальный amimono objc обертки dlopen пустой 0 200 400 600 800 dylib rebase&bind objc-setup initialization
  119. 119. 3. Оптимизация after-main
  120. 120. Нужно на старте делать как можно меньше работы
  121. 121. Избыточное создание сущностей на старте Избыточный UI Инициализация, необязательная для показа стартового UI На что обратить внимание в первую очередь 70
  122. 122. Инъекция зависимостей 71 class RootViewController { init(searchFacade: SearchFacade, routingFacade: RoutingFacade) { ... } } let searchFacade: SearchFacade = SearchFacadeImpl(...) let routingFacade: RoutingFacade = RoutingFacadeImlp(...) let rootVC = RootViewController(searchFacade: searchFacade, routingFacade: routingFacade)
  123. 123. Инъекция зависимостей 72 RootViewController
  124. 124. Инъекция зависимостей 72 RootViewController SearchFacade RoutingFacade
  125. 125. Инъекция зависимостей 72 RootViewController SearchManager RoutingPresenter... ... SearchFacade RoutingFacade
  126. 126. Инъекция зависимостей 72 RootViewController SearchManager RoutingPresenter... ... SearchFacade RoutingFacade ...... ... ...
  127. 127. Оформить зависимости сущностей в протоколы В конструкторе принимать реализацию протокола В реализации использовать lazy var Инъекция ленивых зависимостей на Swift 73
  128. 128. Инъекция ленивых зависимостей 74 protocol RootViewControllerDeps { var searchFacade: SearchFacade { get } var routingFacade: RoutingFacade { get } } class RootViewController { init(deps: RootViewControllerDeps) { ... } }
  129. 129. Инъекция ленивых зависимостей 75 protocol RootViewControllerDeps { var searchFacade: SearchFacade { get } var routingFacade: RoutingFacade { get } } class RootViewControllerDepsImpl: RootViewControllerDeps { lazy var searchFacade: SearchFacade = { return SearchFacadeImpl(...) }() lazy var routingFacade: RoutingFacade = { return RoutingFacadeImpl(...) }() }
  130. 130. Composition Root 76 class ApplicationDeps: SearchFacadeImplDeps, RoutingFacadeImplDeps, RootViewControllerDeps { lazy var searchFacade: SearchFacade = { return SearchFacadeImpl(deps: self) }() lazy var routingFacade: RoutingFacade = { return RoutingFacadeImpl(deps: self) }() lazy var rootViewController: RootViewController = { return RootViewController(deps: self) }() }
  131. 131. На старте создаются только нужные сущности Не используется рефлексия resolve проверяется компилятором Инъекция ленивых зависимостей 77
  132. 132. Сократить view-tree, создаваемое на старте › ленивое создание контейнерных view и view-контроллеров › ленивое создание view с опциональным контентом Оптимизация UI 78
  133. 133. Оптимизация UI в Яндекс.Картах 79
  134. 134. Оптимизация UI в Яндекс.Картах 79
  135. 135. Оптимизация UI в Яндекс.Картах 79
  136. 136. › Ленивая загрузка шрифтов › Графику, генерируемую программно, отрисовать в ассеты › Текст также отрисовать в ассеты › Сложный autolayout перевести на фреймы › … Прочие оптимизации UI 80
  137. 137. Цель - как можно быстрее разблокировать UI для пользователя Пока UI не загружен - выполнять только необходимые действия, прочие отложить на 0.1-0.3с Необязательная на старте работа 81
  138. 138. Отложили на 0.3с › Синхронизацию закладок › Отображение закладок на карте › Загрузку конфигурации приложения › Настройку аудиосессии › ... В Яндекс.Картах 82
  139. 139. 4. Сохранение результата
  140. 140. Continuous integration 84
  141. 141. Встроить вызов скрипта c автозапусками в CI Continuous integration 84
  142. 142. Встроить вызов скрипта c автозапусками в CI Собирать статистику запусков Continuous integration 84
  143. 143. Встроить вызов скрипта c автозапусками в CI Собирать статистику запусков Обеспечить доступ к статистике Continuous integration 84
  144. 144. Логировать создание зависимостей в composition root Логировать список загружаемых динамических бибиотек через objc_copyImageNames() Сверять с эталонными логами Идеи для CI 85
  145. 145. Информация о процессе через sysctl 86 #import <sys/sysctl.h> int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; struct kinfo_proc kp; size_t len = sizeof(kp); sysctl(mib, 4, &kp, &len, NULL, 0)
  146. 146. Время старта через sysctl 87 #import <sys/sysctl.h> int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; struct kinfo_proc kp; size_t len = sizeof(kp); sysctl(mib, 4, &kp, &len, NULL, 0) struct timeval start_time = kp.kp_proc.p_starttime;
  147. 147. Замерять полное время запуска от нажатия на иконку приложения через sysctl Отправлять в системы аналитики Проверять графики 88 Пользовательские запуски
  148. 148. Заключение
  149. 149. Яндекс.Карты - холодный, 5s 90 До оптимизаций После оптимизаций 0 625 1250 1875 2500 dylib loading time rebase/binding time ObjC setup time initializer time before didFinishLaunching time didFinishLaunching time 1.6s, -30% 2.3s
  150. 150. Яндекс.Карты - холодный запуск 91 Времязапуска,мс iPhone 5 iPhone 5s iPhone 6 iPhone 6s iPhone 7 607695 1 342 1 620 3 148 8851 014 1 930 2 333 4 470 До оптимизаций После оптимизаций -30% -31% -31%-29% -31%
  151. 151. Яндекс.Карты - теплый, 5s 92 До оптимизаций После оптимизаций 0 300 600 900 1200 dylib loading time rebase/binding time ObjC setup time initializer time before didFinishLaunching time didFinishLaunching time 0.75s, -37.5% 1.2s
  152. 152. Яндекс.Карты - теплый запуск 93 Времязапуска,мс iPhone 5 iPhone 5s iPhone 6 iPhone 6s iPhone 7 283354 676752 1 347 533 721 1 088 1 203 2 195 До оптимизаций После оптимизаций -37% -37% -51%-39% -47%
  153. 153. Примеры из доклада WWDC 2016 "Optimizing App Startup Time" libimobiledevice Доклад Почты Mail.Ru cocoapods-amimono sysctl Ссылки 94
  154. 154. Николай Лихогруд Руководитель группы разработки Яндекс.Карт для iOS likhogrud@yandex-team.ru Спасибо за внимание

×