На прошлой неделе вышел PHPStan 2.3 с публикацией в блоге под названием скачок производительности, и моя лента в соцсетях оживилась.
Сейфеддин Гмати, создатель Mago, в тот же день обновил бенчмарки цепочки инструментов PHP. Время работы PHPStan на WordPress сократилось с 55.8 до 19.0 секунд, и он написал Ондржею Миртесу, создателю PHPStan, дружеское «Потрясающая работа». Сам Mago, написанный на Rust, выполняет этот же проект на пике скорости за 1.5 секунды. Днем позже Томаш Вотруба, создатель Rector, опубликовал таблицу для Magento из того же бенчмарка (Mago — 2.9 секунды, PHPStan — 44.5) с подписью «Даже близко нет. Но продолжайте в том же духе». Затем вышли релизы Mago 1.53 и 1.54, и он стал работать еще быстрее.
Обе таблицы сравнивают производительность на «холодных» запусках, показывая большой разрыв между PHPStan и Mago. Однако в реальной разработке это далеко не самый частый сценарий. Когда вы в последний раз запускали PHPStan без кэша результатов?
Где-то между горячим и холодным
Вы изменяете несколько файлов и запускаете PHPStan. Либо вы отправляете изменения в репозиторий, и CI подхватывает кэш результатов от предыдущей сборки (если вы это настроили, подробнее об этом ниже). PHPStan заново анализирует измененные файлы и те файлы, которые от них зависят. Все остальное берется напрямую из кэша.
Поэтому я измерил практическую производительность, чтобы получить более реалистичную картину. Я провел бенчмарк wordpress-develop на 9-м уровне строгости и последовательно воспроизвел 60 реальных коммитов WordPress, причем каждый запуск начинался с кэша, оставленного предыдущим. Я проделал это для каждого релиза PHPStan от 2.1.49 до 2.3.1, запуская параллельно ту версию Mago, которая была актуальна в тот же день.
Интерактивный график доступен по адресу scode.nl/phpstan-231-benchmarks. Вот те строки, которые действительно имеют значение:
| PHPStan 2.1.49 | PHPStan 2.3.1 | Mago 1.54.0 | |
|---|---|---|---|
| Холодный, без Turbo | 48.6 с | 31.2 с | 1.1 с |
| Холодный, с Turbo | 13.3 с | ||
| Средний коммит | 6.0 с | 1.8 с | 1.1 с |
| Горячий, ничего не изменилось | 2.5 с | 1.7 с | 1.1 с |
Mago не использует кэш, поэтому каждый запуск Mago является полным.
О чем говорят цифры
На холодных запусках Mago побеждает с разгромным счетом. Он анализирует весь WordPress примерно за секунду, и я не собираюсь отрицать, что это впечатляет. (Забавная деталь: самый быстрый Mago в таблице Сейфеддина — это 1.20.1 из апреля, а версии 1.51.2 требуется вдвое больше времени. Мой график показывает такое же замедление, а версия 1.54 снова работает быстро).
Теперь посмотрите на средний коммит, зеленую линию. Это то, чего вы ждете на самом деле: PHPStan анализирует ваши изменения, а не весь проект. Это занимает 1.8 секунды против 1.1 у Mago. В предыдущем прогоне 300 коммитов WordPress медианный коммит, затрагивающий PHP-код, заново анализировал ровно два файла. В большинстве случаев PHPStan проверяет ваши два файла, а остальное отдает из дискового кэша.
Эта зеленая линия — моя любимая на графике. Она упала с 6.0 секунд в версии 2.1.49 до 1.8 в 2.3.1. Отчасти это связано с общим ускорением PHPStan, а отчасти — с тем, что кэш результатов стал реже сбрасываться.
Более умный кэш результатов
Последние несколько месяцев я работал над тем, чтобы PHPStan чаще переиспользовал свой кэш результатов. Вот самые заметные улучшения:
- #5933: обновление пакета Composer приводит к повторному анализу только тех файлов, которые используют этот пакет, а не всего проекта.
- #6694: один файл с ошибкой парсинга больше не мешает сохранению кэша. В сочетании с исправлением в BetterReflection для классов, наследующих самих себя, это позволило сократить «горячий» запуск Symfony с 42 секунд до менее чем 3.
-
#6699:
define()внутри тела функции больше не выглядит как символ, от которого зависят другие файлы. В WordPress два коммита, которые раньше анализировали 2291 и 2293 файла, теперь анализируют 2 и 3 файла. За вычетом всех 60 коммитов — это и есть вся разница между 2.3.0 и 2.3.1: 9696 файлов против 5117.
Более мелкие исправления сохраняют кэш при изменении composer.lock , которое не затрагивает версии (#6086), при перемещении проекта в другую директорию (#6190) и при включении или выключении Turbo (#6335). Исправление #5982 ускоряет сохранение и восстановление кэша.
Две вещи, которые стоит проверить сегодня
Устанавливайте PHPStan через Composer. Turbo, нативное расширение, обеспечивающее большую часть прироста производительности в версии 2.3, поставляется в пакете Composer рядом с phpstan.phar . Если ваш CI или ваши инструменты скачивают только phar-архив, вы запускаете проверку без Turbo и упускаете это улучшение производительности. На Symfony это дает 48.8 секунд на холодном запуске вместо 24.7. Поэтому устанавливайте PHPStan через Composer, чтобы получить ускорение от Turbo. Начиная со следующего релиза, PHPStan будет предупреждать об отсутствии Turbo (#6719).
Сохраняйте кэш результатов между запусками CI. Кешируйте tmpDir от PHPStan точно так же, как кешируете vendor/ . От этого зависят показатели среднего коммита и горячих запусков в таблице выше.
Так стоит ли переходить?
Я бы сказал, что не ради скорости. На обычном коммите разница составляет менее секунды. На чаше весов в пользу PHPStan лежит всё то, что он уже предоставляет: Larastan, расширения для Symfony и Doctrine, ваши собственные правила, бэзлайн (baseline) и годы настройки вывода типов под вашу кодовую базу. Это слишком многое придется перестраивать ради экономии 0.7 секунды на коммит, к тому же PHPStan продолжает ускоряться.
Mago — отличный проект, и мне нравится, что эти два инструмента подстегивают друг друга. Поэтому у меня есть одна просьба ко всем, кто публикует такие таблицы, включая Сейфеддина и Томаса: добавьте рядом с «холодной» строкой строку для запуска, который начинается с кэша предыдущего коммита. Холодный запуск — отличный стресс-тест, но он не отражает ваш вторник после обеда.
Большую часть этой работы я проделал в режиме парного программирования с Claude. Замеры проводились на Apple M4 Pro с 14 ядрами и PHP 8.5. PHPStan анализирует wordpress-develop src и tests на 9-м уровне строгости. Mago запускается с analyze и со строгими настройками. Mago и PHPStan проверяют разные вещи, поэтому строка Mago служит лишь ориентиром, а не прямым сравнением один к одному. Все запуски и методика описаны на странице бенчмарков.
Комментарии (0)
Пока нет комментариев — будьте первым.