Laramago 0.1.7: как я ускорял анализ Laravel

Содержание
  1. С чего началась работа
  2. Что изменил
  3. Что получилось в итоговой серии
  4. На Windows картина другая
  5. Как проверял, что анализ сохранился
  6. Что не пригодилось и куда дальше

В первой записи про Laramago я показал довольно неприятный результат: анализ реального Laravel-приложения занимал несколько минут. Дальше занялся оптимизацией. В версии 0.1.7 накопились изменения, которые убирают повторный разбор кода и уменьшают объём хранимых данных. Расскажу, что получилось и как я это проверял.

Laramago 0.1.7: рабочее место разработчика, общий индекс исходников и условные графики нагрузки и памяти
Оптимизация анализа исходников. Сгенерированная иллюстрация; графики условные, это не скриншот замеров.

С чего началась работа

Laramago — мой пакет, который помогает статическому анализатору Mago понимать Laravel: модели Eloquent, коллекции, конфигурацию и другие конструкции фреймворка. О том, зачем я его делаю, уже писал в первой статье.

В опубликованных тогда замерах медиана полного анализа с Laramago 0.1.5 была 415,83 секунды. Быстрый анализатор сам по себе ещё не гарантирует, что расширение к нему тоже будет работать быстро. Каждая дополнительная проверка может снова читать исходники, разбирать их и искать сведения, которые другая проверка уже получила.

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

Что изменил

Отсекаю заведомо неподходящие случаи раньше. Прежде чем запускать дорогой разбор выражения, проверяю, может ли оно вообще подходить под нужное правило. Если по форме выражения ответ уже отрицательный, лишняя работа не нужна. Решение об удалении сообщения анализатора по-прежнему требует полной проверки. В одной из ранних сопоставимых серий этот шаг снизил медиану с 308,84 до 236,68 секунды, примерно на 23%. Это отдельный эксперимент 16 октября со своим снимком приложения; его протокол и ограничения сохранены.

Переиспользую результаты обхода исходников. Разным проверкам часто нужны разные сведения из одного файла. Теперь часть этой работы выполняется совместно, а проверки получают подготовленные данные для чтения. Так меньше повторных обходов и восстановления одних и тех же структур.

Храню данные компактнее. Для некоторых задач достаточно небольшого набора сведений о конструкциях кода и их расположении в файле. Полное дерево разбора для каждого последующего запроса избыточно. В оптимизированных участках сохраняются более компактные данные; служебная история хешей исходников тоже ограничена.

После вытеснения из кеша восстанавливаю только запрошенное. Раньше обращение за одним результатом могло повторно запускать соседние проверки. Теперь восстанавливается нужный результат. Для этого добавлен отдельный тест с настоящим вытеснением из кеша: важно проверить поведение именно в ситуации, которая возникает на большом приложении.

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

Что получилось в итоговой серии

В Linux-замерах 20 октября сравнил финальную комбинацию изменений с предыдущим уже оптимизированным вариантом. Сделал прогревы и восемь измеряемых запусков на конфигурацию, меняя порядок инструментов. Ниже медианы — середина серии, а не самый удачный запуск.

ВариантВремя полного анализаПиковая память
Laramago, предыдущий шаг оптимизации64,31 с1358,17 МиБ
Laramago, итоговый вариант в 0.1.761,48 с1298,22 МиБ
PHPStan + Larastan, уровень 582,46 с1177,90 МиБ
PHPStan + Larastan, уровень 1083,50 с1185,13 МиБ

Последний шаг дал ещё примерно 4,4% по времени и 4,4% по пиковому потреблению памяти относительно предыдущего варианта. Память здесь измеряется для всей группы процессов вместе с учитываемым системой файловым кешем. Это не размер PHP-кучи.

Условия серии: PHP 8.5.4, Mago 1.48.1, два CPU, общий лимит 6 ГиБ, PHP-куча 2 ГиБ, отключённые OPcache и JIT. PHPStan каждый раз получал пустой кеш результатов. Исходники и зависимости измеряемой копии контролировались; vendor использовался только для чтения. Полные результаты Linux-серии доступны в репозитории.

Начальные 415,83 секунды и итоговые 61,48 относятся к разным сериям: менялись снимок приложения, настройки и условия измерения. Они показывают историю работы, но точный коэффициент ускорения между ними я не считаю. Для оценки конкретной правки использую сравнение вариантов внутри одной серии.

На Windows картина другая

Проверил и нативный Windows, без Docker и WSL внутри измеряемого запуска. Здесь четыре измеряемых запуска на конфигурацию дали 143,23 секунды для Laramago, 186,04 для Larastan L5 и 121,71 для Larastan L10. Получилось примерно на 23% быстрее L5 и на 18% медленнее L10. Пиковая память Laramago тоже выше: около 1212 МиБ против 825–835 МиБ у этих конфигураций Larastan.

Рабочая машина оставалась общей: фоновая нагрузка, частота CPU и файловый кеш полностью не контролировались. В L5 сохранялись семь исключений конфигурации, а в L10 этот список был очищен; наборы правил инструментов различаются. Поэтому результаты описывают конкретное приложение и выбранные настройки. Windows и Linux также считают пиковую память по-разному — напрямую сопоставлять эти столбцы нельзя. Windows-отчёт с отдельными запусками и сохранёнными неудачными попытками открыт вместе с остальными материалами.

Как проверял, что анализ сохранился

При каждой оптимизации сравнивал полный результат анализа с контрольным вариантом. В итоговой Linux-серии сохранились все 13 100 диагностических записей, включая поля сообщений и повторения. В принятой Windows-серии результат каждого запуска также совпал с собственной контрольной диагностикой Windows. Одного одинакового количества сообщений для такой проверки недостаточно.

Отдельно повторил набор из 33 пар «ошибка и исправленный вариант» с 14 конфигурациями анализаторов. Это помогает увидеть, какие проверки сохранились и где есть пробелы. Например, выбранные ошибки с mixed обнаруживаются наравне с примерами старших уровней PHPStan, а часть проверок отсутствующих аннотаций остаётся пробелом. Одного общего «уровня PHPStan» у Laramago нет. Матрица проверок опубликована отдельно.

Точные исходники финальной оптимизации прошли полный локальный сценарий из 502 PHP-команд и строгую проверку Composer. Это локальная проверка; запуск hosted CI в этом отчёте не заявлен. Результаты проверки исходников тоже сохранил.

Что не пригодилось и куда дальше

Некоторые идеи пришлось откатить. Например, сжатие кеша выглядело способом уместить больше данных, но в полном прогоне добавило затрат. Улучшение отдельного счётчика ещё не означает ускорение всего анализа. Поэтому профилирование и обычные замеры времени проводились отдельно: сам сбор подробной статистики влияет на выполнение.

Результат этой работы вошёл в релиз Laramago 0.1.7. Пакет остаётся предрелизом. Дальше хочу проверять его на других реальных приложениях, продолжать уменьшать повторную работу и следить за памятью вместе со временем анализа. Для меня полезная оптимизация — та, после которой проверками удобнее пользоваться каждый день и они продолжают выдавать проверенный результат.

Описание проекта и новости собираю на странице Laramago и под меткой Laramago.

Есть что добавить?

Ваш email не публикуется. Обязательные поля отмечены *