Измерение производительности iOS-приложений как правило производится путем профилирования приложения для расчета затрачиваемого времени в каждой функции. Обычно это делается с помощью Time Profiler в Xcode Instruments, но он известен, как медленный и ненадежный.

Emerge предлагает инструмент профилирования как часть Тестирования Производительности в CI. Данное профилирование представлено как flame-граф, и доказало, что может быть простым способом получить понимание о критических элементах, влияющих на производительность приложения, и находить решения. Сегодня мы представляем новый способ использовать те же самые великолепные визуализации профилирования, полностью локальные и с открытым исходным кодом.

ETTrace - это фреймворк с открытым исходным кодом, написанный на Objective-C, и CLI (интерфейс командной строки) на Swift, который профилирует и визуализирует данные полностью локально. Он создан чтобы быть простым и быстрым: просто подключите фреймворк к вашему приложению, запустите ettrace для начала профилирования и остановите, чтобы мгновенно увидеть flame-граф. Перезапускать приложение не надо, как и блуждать по долгим меню, чтобы увидеть результат.

Почему нам нужен новый профайлер

Performance Analysis от Emerge предназначен для предупреждения регрессий от объединения с вашей кодовой базой. Он дает вам последовательные результаты при определенных сценариях, настроенных в CI. Однако при отладке отправка в CI не всегда удобна. Вам нужен что-то быстрое и локальное.

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

Возможно, вы задаетесь вопросом, почему бы просто не использовать для этого Instruments? Хотя Time Profiler является лучшим инструментом для профилирования iOS, но если вы не являетесь экспертом по производительности (и даже для них) - он может быть сложен в использовании. Это частично связано с неинтуитивной визуализацией, а также с массивной составляющей инструмента.

На Emerge я общался с многими инженерами, работающими над большими приложениями, и обратная связь была одинаковой: Time Profiler может быть “хрупким” и медленным. Даже для получения скриншотов для этой статьи я столкнулся с несколькими зависаниями и пришлось принудительно выходить. Частые проблемы с символами, при генерации “следов”* и показывающими только адреса, но не названия функций.

*trace - свойства события, которое указывает на попытку одним процессом присоединиться к другому, собирает данные производительности в процессе выполнения приложения на IOS-устройстве.

Instruments против ETTrace

Instruments против ETTrace

ETTrace поддерживает “символизацию” двумя способами. Во-первых, если у вас есть dSYMs (debug symbols), вы можете напрямую предоставить их нашему инструменту через аргумент --dsyms. Во-вторых, для сборок в симуляторе, ETTrace автоматически использует таблицу символов в бинарном файле приложения для символизации. T.к. наш инструмент с открытым исходным кодом, то если у вас возникнут проблемы с символизацией, их легко отладить - в отличие от Instruments.

Как работает выборка (выбор дискретных данных)

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

sStackRecordingThread = [[NSThread alloc] initWithBlock:^{
    NSThread *thread = [NSThread currentThread];
    while (!thread.cancelled) {
        [self recordStack];
        usleep(4500);
    }
}];

Каждая запись включает список адресов, обнаруженных в стеке, и текущую отметку времени. Адреса “символизируются” после завершения записи “следа”, и версии с символами суммируются. Максимальное время между любыми двумя стеками должно быть 5 мс (учитывая, что режим ожидания может занять до 0,5 мс больше, чем указанное нами время). Чтобы гарантировать точность “следов”, любое дополнительное время, превышающее это, накапливается и передается как <unattributed>.

Технически визуализация является flame-графом, что означает, что узлы упорядочены по времени на оси X. Она придерживается этой структуры данных:

struct FlameChartNode {
  let name: String
  let duration: Double
  let children: [FlameChartNode]
}

Визуализация, создаваемая при тестировании производительности с помощью Emerge, представляет собой flame-граф, где стеки собираются по имени на каждом уровне. Каждый узел не имеет конкретного времени начала/конца, так как они не упорядочены; только длительность на оси x. Структура данных выглядит следующим образом:

struct FlameGraphNode {
  let duration: Double
  // Children is keyed by node name
  let children: [String: FlameGraphNode]
}

Поскольку ETTrace визуализирует только один “след” приложения (а не среднее значение многих), данные представлены в виде flame-графа, который легче дебажить. У ETTrace также есть функция сравнения, которая позволяет загрузить два “следа" и сравнить их, чтобы увидеть, как улучшилась или ухудшилась функция. При использовании этого параметра визуализация будет представлена в виде flame-графа.

Понимание соответствия протоколам

В качестве примера использования ETTrace для анализа влияния соответствия протоколам на запуск приложения, с использованием приложения Mastodon с открытым исходным кодом, но модифицированного для включения большего количества соответствий протоколам. Обычно ETTrace используется после запуска приложения, но для профилирования напрямую с момента запуска мы добавляем ключ ETTraceRunAtStartup, установленный на YES, в Info.plist.

Теперь мы можем запустить приложение с подключенным ETTrace.framework и начать профилирование! Убедитесь, что приложение удалено с вашего телефона перед установкой из Xcode. Затем установите приложение, но не запускайте его. Наконец, из командной строки запустите ettrace и следуйте инструкциям, включая запуск приложения вручную с начального экрана. Полученный flame-граф показывает большое количество времени, затраченное на соответствие протоколам: более 60 мс!

Медленные соответствия протоколам в ETTrace.

Медленные соответствия протоколам в ETTrace.

Далее попробуйте запустить то же самое приложение во второй раз и запустите ettrace, чтобы получить “след”. В этот раз проверки соответствия настолько быстры, что ETTrace даже не отслеживает их! Выбрав оба “следа”, вы можете использовать дифференциальный flame-граф, чтобы подтвердить причину замедления.

Это демонстрирует, что проверка соответствия протоколу все еще использует медленный путь при первом запуске приложения в iOS 16 (включая после установки обновления) и очень быстрая для последующих запусков. Однако другие виды проверок соответствия протоколу, например, когда результат операции as? является nil, могут по-прежнему быть очень медленными. Запуск вашего приложения локально с помощью ETTrace может помочь выявить такие медленные участки в вашем приложении.

Автоматизация в CI

Отладка локальной производительности с помощью ETTrace - это только часть процесса оптимизации производительности. Вы хотите быстрый цикл итераций, который позволяет оценивать новые идеи, но непрерывное тестирование и оповещения обеспечивают дополнительную защиту от возникновения проблем в проде и подтверждают измерения локальной производительности. Emerge предлагает функцию тестирования производительности, которая делает именно это. Отладка локальной производительности с помощью ETTrace вместе с Анализом Производительности Emerge обеспечивают единый рабочий процесс по оптимизации производительности для разработчиков, что приводит к непрерывному улучшению производительности приложения. Если вы хотите узнать больше об этих инструментах, не стесняйтесь и пишите, и если у вас есть какие-либо отзывы по ETTrace, пожалуйста, откройте Github issue!

Благодарим Itay Brenner за его работу над этим проектом, а также Filip Busic, Miguel Jimenez и Keith Smiley за их обратную связь при тестирование инструмента ранее!

Комментарии (0)