Дмитрий Стогов из Zend by Perforce уже много лет занимается самым сердцем PHP и знает про него много полезного. В том числе о вопросах, связанных с производительностью.
В своем докладе на конференции PHP Russia 2021 Дмитрий расскажет, как продвигается работа над JIT. И какие другие идеи, направленные на повышение производительности, были реализованы в PHP 8.0 и готовятся в PHP 8.1. А уже сегодня можно почитать интервью, в котором я задал эксперту PHP вопросы обо всем понемногу: от его истории до обсуждения современных реалий.
— Что было первым, что ты запрограммировал?
— Еще в 80-е годы прошлого столетия папа познакомил меня с программируемым калькулятором «Электроника Б3-34». У него была память всего на 98 команд и 14 регистров. Но главное, на нем можно было программировать игры, чем я и увлекся. Я очень плотно занимался «Посадкой на Луну», даже стал брать книги по космической динамике. Но когда понял, что дифуры третьего порядка калькулятор не потянет, перешел на Электронику Д3-28.
Тут я загорелся написать Pac-Man, но у Бейсика не было ни возможностей, ни производительности. Я начал изучать ассемблер, и хотя Pac-Man так и не написал, смог дизассемблировать монитор и разобраться в его внутренностях.
— Когда увлечение стало приносить какие-то деньги? И что ты программировал в процессе?
— Первые деньги получил за работу на языке Modula-2 на PDP-11 (СМ4), хотя как сейчас понимаю, за ту работу и обучение я должен был еще сам доплачивать. В начале 90-ых, учась в Питерском Политехе, я работал с командой программистов собранной одним из преподавателей. Нам заказали децентрализованную систему по продаже квартир с серверами на VAX и удаленными терминалами на PC, связывающимися по модему на скорости 2400 бод. Интернета тогда еще в России не было (было FIDO), а мы уже использовали SQL базы данных и многоуровневую архитектуру.
Позже, работая системным администратором в банке, и имея свободное время, я развивал компилятор Modula-2 под Linux/DOS/Windows/OS2.
Потом был системным аналитиком в компании, занимающейся автоматизацией складской логистики. Мы создавали складскую систему, которая управляла рабочими на складе. У каждого рабочего был мобильный терминал со сканером штрихкодов, а на каждом стеллаже и паллете были приклеены метки. Система говорила сотрудникам, куда пойти, что взять, куда отвезти. Сейчас этим никого не удивишь, а в конце 90-х годов система с «искусственным интеллектом» выглядела чем-то нереальным. Но она успешно распределяла паллеты по складу, знала где что лежит. И она же определяла, какую паллету раздраконить для мелкого опта. Это была эвристическая система принятия решений, написанная на SWI Prolog. После знакомства с внутренним устройством Prolog (Warren Abstract Machine), я попытался его улучшить и писал свой интерпретатор.
— А как от всего этого пришел к PHP?
— В начале 2000-х я работал в немецкой конторе Turck Software, где мы занимались достаточно странными программами, которые в немецких банках рассчитывали процент по ипотечным кредитам. А после создали простенький на тот момент сервис для встраивания Guestbook-ов в статические сайты. Если не ошибаюсь, то мы набрали всего 2-3 тысячи клиентов, когда стали упираться в специфичные для PHP тех времен тормоза, и мне пришлось залезть в код PHP.
— Так родился eAccelerator?
— Да, так появился Turck MMCache, который тогда конкурировал с Zend Accelerator и APC. Волей неволей, работая над MMCache, я стал одним из экспертов по внутренностям PHP a так же познакомился с ребятами из Zend. Так что когда немецкая контора закрылась я попросился в Zend, где и был с радостью встречен Zeev-ом и Andi. После нескольких тестовых заданий (расширения ext/soap и pecl/perl), меня допустили к баг-фиксам в ядре PHP, а чуть позже почти полностью отдали работу над улучшением ядра мне. Так с 5.1 оно у меня и улучшается. Этой осенью выходит уже 8.1.
— Какая специфика есть в работе над PHP?
— Нужно быть программистом определенного склада — компиляторщиком. Поэтому у меня очень хорошо складываются отношения с ребятами, которые работают над другими языками, например Java. Мы понимаем друг друга с полуслова, хотя говорим об абсолютно разных виртуальных машинах. Только что я работал с инженерами из ARM, которые начали писать PHP JIT под свои процессоры. Все великолепно получилось именно потому что там тоже компиляторщики, которых не пугает ассемблер, и говорим мы на одном языке (китайско-русско-английский). Нам потребовалось всего пару месяцев чтобы переписать под ARM то, что под x86 писалось пару лет.
— Это проблема, когда человек в PHP не сильно разбирается в том, что там внутри?
— Если человек пытается добавить в PHP что-то новое, но не разбирается во внутренностях, то вряд ли у него получится что-то хорошее. Если идея хороша, то с реализацией можем помочь я или Никита Попов. Иногда мне приходится работать над реализацией принятых RFC, которые мне совсем не нравятся, чтобы уменьшить их негативное влияние на производительность. Люди делают новые фичи, не всегда задумываясь, насколько они действительно нужны. Например, как часто ты будешь использовать пересечения типов? Скорее всего, никогда, а тормозить они будут всегда (даже когда не используются).
— А получается при внедрении фич с производительностью какие-то трюки сделать?
— Никита Попов этим занимается. Он часто пытается довести фичи, так чтобы они особенно не влияли на производительность как минимум того, что пишется в старом стиле. Но получается, что использование этих фич сразу приводит к замедлению. Разумеется, Никита и ядро знает очень хорошо, а некоторые новые места даже лучше меня.
— Есть ли по ядру какие-то глобальные планы?
— У меня есть планы на JIT и на связь JIT с FFI. В идеале, код, написанный на PHP с использованием FFI типов, должен компилироваться так же эффективно, как и код на С. Эта идея, как и многие другие, подсмотрена в реализации LuaJIT.
— А не было идеи как-то стандартизировать опкоды, чтобы была обратная совместимость, как в том же JVM?
— Нет, не было. Здесь большой вопрос. PHP не стоит на месте и постоянно изменяется. Не любые новые фичи возможны без расширения системы команд или изменения в уже существующих.
— Получается, у тебя в докладе речь пойдет не только про JIT, но и про те трюки, которые по производительности дают чуть ли не больше, чем JIT?
— Да. В начале этого года я почти месяц потратил на то, что профилировал приложения, основанные на Symfony, и смотрел, где они тормозят. В результате поправил алгоритмы в нескольких местах. Где-то это были локальные нюансы реализации, где-то просто жуткие ляпы. Получились реализовать новые и достаточно интересные алгоритмы. Например, кэшировать зависимостей между классами, когда они объявлены в разных файлах. На Symfony это сразу же дало порядка 10% ускорения. И я думаю, что подобные улучшения будут и на других фреймворках. Хотя WordPress практически не ускорился — в нем классов немного.
— Что-нибудь еще может дать очень много прироста?
— Есть идеи, но они спорные. Самая простая — это то, что сейчас массивы, даже если они упакованы, хранятся как хеш-таблицы. В принципе, мы могли бы хранить там только сами значения — zval. Это бы потребовало в два раза меньше памяти, в два раза больше кэша, соответственно, по массивам мы бы бегали быстрее. А можно было бы пойти еще дальше и специализировать массивы по типам хранимых данных. Например массив целых чисел. Подобные трюки делаются в v8.
Но если вы хотите оптимизировать именно ваше приложение, необязательно рыться в ядре. Поверьте, все основные тормоза вводятся уже на самом PHP, особенно во фреймворках, при реализации каких-то очень хитрых паттернов. Я, например, смотрю на autoloading в Symfony и с трудом понимаю, как это вообще работает.
— Для чего еще можно использовать PHP?
— Помимо вэба, я использую PHP для прототипирования, чтобы быстро набросать какой-то алгоритм, и, если надо, перевести на C. Например, всегда хотел сделать простой генератор парсеров для LL грамматик с автоматическим разрешением конфликтов. Конечно есть ANTLR, но он тянет за собой очень много мусора. Теперь такая вещь есть. Написана она именно как прототип на PHP, но уже используется для генерации С парсера в PHP FFI.
Конечно, на С мы могли бы использовать более оптимальные структуры данных, но в принципе, классы и массивы PHP вполне достаточны для описания сложных алгоритмов на графах и тп. Да, работать он будет медленнее, зато вы выиграете по времени написания и отладки.
— А не было идеи притащить такие типы прямо в ядро?
— Они уже есть в FFI. Любой С-ишный тип можно подключить и работать с ним из PHP. Но сейчас FFI предполагает свои накладные расходы. Я уже говорил об идее скрестить FFI c JIT для избавления от них.
— Я помню, была идея написать extension для PHP на PHP.
— Это реально уже сейчас. Опять-таки на FFI и preloading это делать можно. Вопрос насколько это эффективно? Не-многие extension-ы имеют хоть какое-то отношение к ядру PHP и oп-кодам: OPcache, XDebug, какие-то профайлеры. Все остальные userland extension-ы — базы данных, JSON, XML — никак не связаны с оп-кодами. По сути, в большинстве случаев это обертки над какими-то библиотеками, и тут FFI может быть вполне достаточно. А некоторые расширения наоборот написаны на С, но работают с PHP-ыми структурами данных, так что большого выигрыша от использования С нет. Например, тот же ext/soap.
— Я слышал, вы обсуждали с Александром Лисаченко какие-то очень интересные вопросы?
— На самом деле мы давно не общались. То, что он реализовывал на PHP и FFI, конечно, очень интересно. Прямо из PHP внедриться в ядро, перехватить какой-то С-ый callback и обработать в PHP приложении. Но реального применения для этой технологии я не вижу. Получается, что из PHP можно сделать в ядре все, что угодно. А там и так любой чих не в ту сторону, и у тебя все развалится. Там же все основано на рефкаунтинге. Если ты в одном месте забыл поставить ++ или - -, то рано или поздно придет краш.
— А в ядре segfaults по тем же причинам возникают?
— У каждого segfault своя причина. Но, действительно, очень часто первопричина того, что происходит в ядре, в каком-нибудь extension. Например, там забыли увеличить refcount при присваивании, потом при выходе из какой-то процедуры появился дабл free по одному принтеру.
— А не было идеи сделать разработку extension чуть более user-friendly, чтобы не крашило ядро, и чтобы оно ругалось на эти ситуации? Или это все существенно замедлит?
— Придется полностью отказаться от существующего API. В принципе, возможно что-то сделать на smart pointers C++, auto-increment auto-decrement. Но ядро на C, а не на C++. Это добавит свои проблемы. Ну, и, разумеется, с производительностью будет деградация. Необязательно серьезная. Пока не попробуешь, не узнаешь.
А из моего опыта, далеко не все, что хочется попробовать, стоит доделывать до конца. Работаешь месяц, делаешь фичу proof of concept, доделываешь ее, понимаешь, что в производительности ты выиграл, но не настолько много, насколько усложнил систему. Поэтому половина работы уходит в треш.
— Что самое большое ушло в треш?
— Была идея уменьшить размер каждого значения zval до 8 байт — сейчас 16 байт. Использовать так называемый nan-taging, когда все хранится как будто бы вещественное число, и для представления типов, отличных от вещественных, используется not a number (NaN). Упрощенно, если в экспоненте все единицы, то это NaN. А в мантисе при этом можно записать все что угодно. То есть закодировать тип, целые числа и указатели.
Это дало бы существенный прирост производительность на работе с массивами — в два раза меньше памяти, все компактнее ложится...
— И почему в итоге этого не прошло?
— Усложнение системы. И отсутствие поддержка 64-битных целых чисел. К сожалению, данная технология не позволяет кодировать 64-битные целые числа и усложняет доступ по указателям.
— Чего ты ждешь от конференции?
— Для меня конференция — повод пообщаться. Может быть, получить какие-то новые идеи для реализации.
— Какой совет ты можешь дать начинающему программисту PHP, опытному и эксперту?
— Для начинающего мой совет: учиться, учиться и учиться. И делать это все-таки не на конференциях и курсах, а по книгам, интернету, мануалам. Все это дает намного больше, чем занятия с учителем, который впихивает в тебя знания, а ты смотришь в окно и думаешь о чем-то своем.
Опытному нужно отдыхать. Отдыхом могут быть и конференции, и простая человеческая жизнь, спорт, не связанные с ИТ увлечения. Этим нужно заниматься, чтобы не превратиться в придаток к компьютеру.
Что делать эксперту? Это самый сложный вопрос. У кого получается — учить. А в своей области, не опускать руки и двигаться вперед. Двигайте то, что делаете, ищите новые пути и реализации. Либо меняйте область, если не получается.
PHP Russia 2021 пройдет 28 июня в Москва, Radisson Slavyanskaya. А сегодня можно ознакомиться с расписанием и присмотреть доклады, которые вы точно не захотите пропустить.
Билеты на офлайн проданы. Но вы можете принять участие в онлайн-формате. Не забудьте купить билеты уже сегодня!