Перейти к основному контенту
2026-09-23PurePlayerЗамеры производительностиБольшие библиотеки

Замеры зеркала чтения PurePlayer: глубокий пейджинг быстрее секунды на 500 000 треков

В PurePlayer добавлен слой изолированного зеркала чтения на SQLite для масштабных медиатек. На тестовой коллекции в 500 000 треков время глубокого постраничного перехода сократилось до менее чем одной секунды, а переходы по алфавитному индексу A-Z выполняются за единицы миллисекунд. Начальное построение индекса занимает около 30 минут и требует около 2 ГБ дискового пространства, управляясь из настроек.

Замеры зеркала чтения PurePlayer: глубокий пейджинг быстрее секунды на 500 000 треков

Когда объем локальной медиатеки достигает сотен тысяч треков, прокрутка начальных экранов обычно сохраняет плавность, тогда как глубокая пагинация, навигация по буквам и глобальный поиск сталкиваются с ощутимыми задержками. На эталонном наборе из 500 500 треков мы замерили длительность выполнения основных сценариев чтения и реализовали механизм локального зеркала чтения на SQLite, запланированный для версии 7.0.

Примечание к версии: в версии 6.0 выполнен комплекс базовых оптимизаций. Описанное здесь высокопроизводительное зеркало чтения разрабатывается для версии 7.0 и проходит внутреннее тестирование. Все показатели получены в средах внутренней сборки.

Архитектура формирует на устройстве производную копию медиатеки на базе SQLite в режиме только для чтения. При индексации ключи сортировки, параметры фильтрации и буквенные якоря A-Z вычисляются заранее и сохраняются на диск. Основное хранилище системы сохраняет статус первоисточника, а зеркало чтения берет на себя поисковые запросы и поддерживает независимую перестройку или очистку.

Результаты измерений производительности

Замеры проводились на Mac mini M4 с тестовым набором на 500 500 треков. Все показатели подтверждены попольным контролем целостности:

Сценарий чтения Масштаб Базовое хранилище Зеркало Ускорение
Построение снимка списка песен 10k 3030 мс 21 мс ~143×
Постраничная навигация (с фильтром) 10k 1984 мс 8,2 мс ~242×
Переход по буквенному якорю A-Z 10k ~2050 мс 0,4–7,6 мс 274–5100×
Глубокая страница (смещение 450k) 500k ~28 мин/стр ~0,9 с ~1800×
Проверка наличия строк без совпадений 100k 98,1 с 46 мс ~2100×
Многокритериальные выборки смарт-плейлистов 100k 6,9 с 1,27 с ~5,4×
Страница плейлиста / детализация артиста / первый рендер 50k – – 4,5–7,3×

При объеме в 500 000 треков стандартный путь чтения использует последовательное сканирование всей таблицы, из-за чего задержка растет пропорционально глубине смещения: загрузка одной страницы в районе 400 000-го трека занимала в среднем 1668 секунд. Зеркало чтения снижает время выборки на этой глубине до 0,8–1,4 секунды, обеспечивая ровный отклик при любых смещениях.

Теги сортировки и совместимость представлений

В классической музыке и джазе слушатели часто задействуют теги сортировки (Sort Tags), чтобы группировать исполнителей вроде «The Beatles» на букву B («Beatles, The»). Зеркало сохраняет два независимых набора ключей сортировки для пяти поддерживаемых полей. Переключение тегов сортировки меняет запрашиваемый столбец в SQL-запросе без перестройки индекса, а алфавитная панель A-Z обновляется синхронно.

Сводные экраны альбомов, исполнителей и композиторов также обращаются к запросам через зеркало. Если на этих экранах активны теги сортировки, запросы перенаправляются к основному системному хранилищу для сохранения корректности тегов уровня сущностей. Создание материализованных сводных таблиц на стороне записи внесено в план разработки.

Потребление ресурсов и баланс параметров

Поддержание локального зеркала чтения требует времени на построение индекса и локального дискового пространства:

  • Время построения: индексация выполняется в фоновом режиме. Первичное создание индекса для 500 000 треков занимает около 32 минут, при этом навигация по библиотеке в стандартном режиме остается доступной; для 50 000 треков построение завершается примерно за 35 секунд.
  • Объем хранилища: файл зеркала для 500 000 треков занимает около 2,1 ГБ. Раздел настроек отражает фактический размер; при выключении режима данные сохраняются для быстрого повторного включения, а также доступно полное удаление.
  • Накладные расходы записи: сохранение метаданных синхронно обновляет зеркало, добавляя в наших замерах около 20 мс на групповую фиксацию изменений.

Ввиду указанных требований к ресурсам функция по умолчанию выключена и включается пользователем в настройках при наличии крупной медиатеки.

Ключевые механизмы ускорения

  • Проекция легковесных структур: стандартный путь чтения создает управляемые объекты со всеми зависимостями жизненного цикла; зеркало извлекает строки SQLite и проецирует их в легковесные структуры, снижая нагрузку на память и процессор.
  • Спуск предикатов в БД: фильтры, сортировка и дедупликация выполняются ядром базы данных до загрузки записей в оперативную память приложения.
  • Предварительный расчет ключей и секций: нормализованные ключи сортировки и символы индекса A-Z рассчитываются при сохранении. Выборка сочетает постраничную навигацию по ключам (Keyset Pagination) и сканирование индексов, сохраняя стабильную скорость на глубоких страницах.

Целостность данных и детали реализации

Для подтверждения идентичности результатов между зеркалом и основным хранилищем тестовый стенд проверил комбинации 19 колонок сортировки, прямых и обратных направлений, состояний тегов и составных фильтров. В ходе интеграции было учтено несколько граничных условий:

  • Обработка пустых значений и пустых строк: логика тегов сортировки использует тег при наличии и переходит к отображаемому имени при его отсутствии. Поскольку операторы Swift и строковые функции SQL по-разному работают с пустыми строками, зеркало разносит ключи по отдельным столбцам и явно указывает нужный столбец при составлении запроса.
  • Совместимость сортировки длительности: длительность треков исторически сравнивалась посимвольно как строка (например, «120.0» перед «90.0»). Для сохранения привычного порядка строковые поля получили специализированные текстовые ключи, а числовые индексы закреплены за строгими числовыми типами.
  • Синхронизация наборов пробельных символов: состав пробельных символов в Swift (включая неразрывные пробелы, символы табуляции и перевода строки) отличается от стандартных функций обрезки в SQL. На стороне записи добавлены статусные флаги, гарантирующие единую проверку пустоты данных.
  • Пакетная обработка при масштабной сборке: на 500 000 записей триггеры полнотекстового поиска выполняют запись в несколько индексов, доводя суммарную продолжительность до 32 минут. Процесс разбит на фрагменты и поддерживает возобновление после прерываний.
  • Координация параллельных записей: если во время перестроения индекса редактируются метаданные, каждой операции присваивается монотонно возрастающий номер последовательности, что позволяет правилам разрешения конфликтов сохранять актуальное состояние.

Все замеры получены на отладочных сборках; журналы тестов и проверочные сценарии сохранены в репозитории.