android telegram s optimizations

Post on 18-Jul-2015

934 Views

Category:

Engineering

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Оптимизация Telegram S for AndroidМетоды и принципы разработки высокопроизводительных приложений

Steve Korshakov CEO Actor LLCsteve@actor.im

История

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 минут)

Вопросы?

Steve Korshakov CEO Actor LLCsteve@actor.im

top related