Android Telegram S Optimizations
-
Upload
stepan-korshakov -
Category
Engineering
-
view
934 -
download
0
Transcript of Android Telegram S Optimizations
Оптимизация Telegram S for AndroidМетоды и принципы разработки высокопроизводительных приложений
Steve Korshakov CEO Actor [email protected]
История
V2
?
– Генри Форд
«Все можно сделать лучше, чем делалось до сих пор»
Хорошее приложение
Оптимизации
Сеть
БД
Интерфейс
Память
Оповещения
Мобильные сетиTCP-Подключения часто полуживые
Очень нестабильный Ping (от 100мс до 2с)
Иногда в подключение удается отправить лишь один пакет и не получить ответа
Необходима минимизация трафика, тк часто просто нет другой возможности работать
Часто пытаются слушать трафик так, что системы слежения сами падают и теряют пакеты
Высоконагруженные сервисыСервера часто умирают
Необходим эффективный протокол, который рассчитан на нагрузки
Быстрое восстановление работы при разных сбоях между клиентом и серверами
Производительность протокола напрямую связана с ценой обслуживания
Эффективный HTTPНа каждый запрос 5 попыток с таймаутом в 5 секунд
Не использовать отмену запросов (вешает Apache HTTP)
LongPoll для получения событий
В сумме выходит медленно, но, в целом, стабильно и комфортно.
Официальное приложение ВК продолжает НЕ делает так, потому у него часто все плохо.
MTProtoЛегкие сессии
Оптимальная криптография
Переотправка данных
Собственная бинарная сериализация
Определяет ТОЛЬКО шифрование транспорта.
MTProto v2 (by Actor)Стандартная TLS криптография
Редиректы
Более гибкая сериализация
На уровне транспорта реализованы инструменты проверки связи
MTProto v3?
Peer-To-Peer
Детектирование типа подключения и адаптация приложения под эти условия
Создание отдельного протокола для обмена файлами
Версия протокола для Long Fat Networks
Детали реализации
Мессенджер VK писался во времена Apache HTTP, он был стабильным, сейчас считается устаревшим.
Для Телеграмма использовались синхронные сокеты, а не NIO, тк NIO в разных версиях по разному глючит. Однако, официальный клиент всегда разрабатывал на асинхронных.
Работа с БДПоиск наиболее быстрой БД
История списковCursorAdapter
Cached CursorAdapter
MemoryAdapter + Background Cursor
MemoryAdapter + Background ORM
MemoryAdapter + Background Cursor + Custom serialization + Memory Cache
DisplayList + Actor ListEngine
5 msИли 1/3 кадра при 60 FPS
Время загрузки диалогов
CursorAdapter
- Чтение из курсора в UI-потоке
- Иногда и повторное чтение
+ Подгружается сразу весь список
+ Нет сложной логики по подгрузке
+ Более гибкое управление данными
Cached Cursor Adapter
Кеширование загруженных результатов
Немного исправляет производительность для тех, кто уже был отображен, но до первого обновления списка
Все равно читает с диска в UI-потоке
MemoryAdapter + Background Cursor
+ UI работает только с обычным ArrayList, который обновляется и синхронизируется в фоне с БД
+ Основано на тех же запросах, что и предыдущие варианты
- Нужно реализовывать сложную логику (не слишком, но в ней легко допустить баги) и более сложные состояния списков
- При обновлении списка надо все равно с нуля пере запрашивать Cursor
Оптимизация запросов SQLite
SQLite Cursors
ORMLite
GreenDao
Actor NoSQL Engines
Оптимизация запросовДенормализация: убрать все Join
Например, в списке диалогов хранятся имена и аватарки пользователей и групп и обновляются вручную
Минимизировать сами таблицы и набор колонок для списка, минимизировать любые большие колонки
В итоге запись немного медленнее, но чтение становится гораздо быстрее (увы, цифр не помню)
Можно упростить таблицу и оставить только ID и BLOB, почему-то это работает быстрее.
ORMБез ORM
+ Отсутствие overhead
- Более сложная работа с БД
ORMLite
+ Управление схемой из кода
- Reflection
GreenDAO
+ Малый overhead
- Необходимо писать в коде схему и генерировать необходимые классы
Actor List EngineНа базе SQLite, но адаптируемый под любой Storage
Запись - id, sortKey и BLOB
Id генерируется снаружи движка
Асинхронная запись и чтение в фоновом потоке
Кеширование записей
Лишь подмножество операций: addOrUpdate, delete, clear, getItem
Используется ProtoBuf-подобная сериализация
Actor Key-Value Engine
Аналогичен ListEngine
Запись - id + BLOB
Операции: addOrUpdate, remove, getItem
Display ListDisplay List - список элементов, который синхронизируется с ListView
Можно изменять список из любого потока
Внутри - два списка, которые попеременно переключаются
Много одновременных изменений списка группируется
BindedDisplayListBindedDisplayList - DisplayList, который связан с ListEngine
Автоматическая асинхронная подгрузка данных из БД
Возможность фильтрации списка (перезапросы в ListEngine)
Загрузка списка из центра коллекции
Результат
Максимальная возможная скорость загрузки списков из SQLite
Отзывчивый интерфейс
Удобная асинхронная работа с БД
Гибкие синхронизируемые списки, которые не надо программировать
Доработки?
Адаптация под RecyclerView
Документирование и упрощение использования движков
LMDB
Оптимизации интерфейсовСписки, изображения и layout
Пара фактов16 мс на все
Layout сложных объектов долгий. Иногда измерения проходят не один раз.
В ListView много View - все они делают layout
При обновлении списка - зачастую проходит повторный Layout (конкретно зависит от версии Android)
Лишние View - лишний рендеринг
Никакой магии
Уменьшение влияния GC
Облегчение Layout
Уменьшение влияния фоновых потоков
Список диалоговКаждый элемент - одно View
Проверяется повторный binding данных
Пересчитываются размеры только те, что изменились
Иногда для текста layout просчитывается в фоне
Это - единственный способ сделать быстро везде
Список сообщенийРасчет размеров текста при загрузке из БД (отключаемо)
Каждое сообщение - лишь одно View с ручной отрисовкой.
Нажатие на ссылки обрабатываются вручную
Изображения грузятся в фоне и масштабируются строго под размер сообщения
Blur превью через оптимизированный native-код
Работа с памятьюВыделение памяти и изображения
Работа с изображениямиВСЕГДА переиспользуются Bitmap, даже на 2.2.
Ручное декодирование libjpeg
Ручная отрисовка региона Bitmap
Итог - GC спит
Переиспользование памятиПри файловых операциях часто выделяются и высвобождаются небольшие массивы байт (8-16кб), что провоцировало GC
Для устранения проблемы был создан собственный аналог malloc, который кешировал выделенные массивы и переиспользовал их
В наиболее активных циклах переиспользуются объекты, а не выделяются новые (например Envelope в Actor System)
ОповещенияОптимизация оповещений Google Play
Google Cloud Messaging
Большие задержки (до 10 минут) в оповещениях
Иногда просто не работают
Есть устройства без GCM вообще
Итог - Написать свое.
Разработка собственных оповещенийДержать всегда активное подключение
Однако, нужно быть аккуратным что бы не тратить энергию
Запуск радио происходит за 5 секунд и работает на полную мощность примерно 10-20 секунд. Затем еще минуту радио работает в среднем режиме и только после этого возвращается в спящий режим.
WiFi при этом почти не потребляет энергии.
ЭнергоэффективностиВремя перехода от состояний зависят от типа сети
Потребление 3G может быть ниже чем у EDGE, тк можно за более короткое время скачать нужные данные
На WiFi можно всегда сколько угодно данных передавать и батарея не будет садиться вообще
Хорошая лекция от Google: http://www.youtube.com/watch?v=dASOm88Wh8g
Общая логика работы пушейУ Apple очень умные пуши. Анализируют порядка 23 параметров использования устройства и приложений. (последнее использование, движение устройства, последнее открытие приложения, активность его использования)
У гугла тупее.
Логика очень простая - поднимается коннект и раз в 10 минут на мобильных сетях отправляется пинг для проверки, на вайфае раз в 2-3 минуты (некоторые роутеры вешают коннект через 5 минут)