[PHP, Программирование] PHP 8: встроенное наблюдение (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 2 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
06-Июл-2021 14:30


За последние два десятилетия виртуальная машина Zend Engine, лежащая в основе PHP, претерпела определенные улучшения. Значительный скачок производительности произошел с выходом версии PHP 7, в которой ощутимо выросла производительность WordPress и других традиционных веб-приложений. В версии PHP 8, в свою очередь, появился JIT-компилятор, значительно ускоривший выполнение ресурсоемких алгоритмов.Однако основная ловушка наблюдения, используемая трассировщиками, профилировщиками и отладчиками для наблюдения за поведением вызова функций PHP, не развивалась параллельно улучшениям Zend Engine и все сильнее замедляла работу наблюдаемых PHP-приложений. Например, трассировщик Datadog PHP просто не мог развиваться с учетом нововведений PHP 8 без изменения ловушки наблюдения.Выпуск PHP 8 включает в себя изменения, которые привносят в среду выполнения PHP современный уровень наблюдения. Наша команда совместно с сообществом разработчиков движка PHP разработала и выпустила новый API наблюдателя. Перед выпуском этого API мы, как и другие PHP-разработчики, столкнулись со множеством проблем.Чтобы лучше понять, как ловушки наблюдения изменяют и ограничивают процесс разработки, рассмотрим ограничения в области наблюдения в версиях, предшествующих PHP 8.Ситуация с наблюдением в версиях до PHP 8Ловушка выполнения ВМВиртуальная машина (ВМ) PHP отвечает за поддержку выполнения PHP. В ВМ есть указатель с именем zend_execute_ex, который может переопределяться расширениями. Если какое-либо расширение использует эту ловушку, все вызовы функций или методов, определенных в PHP, выполняются только через нее.Хотя эта ловушка была одной из самых популярных ловушек вызовов функций до появления PHP 8, она имела ряд недостатков:
  • Стек вызовов в PHP был почти неограниченным. Однако если какое-либо расширение перехватывает zend_execute_ex, все вызовы функций PHP будут попадать во встроенный стек C, который ограничен значением параметра ulimit -s. В результате может возникнуть переполнение стека и аварийное завершение процесса PHP.
  • Если расширение перехватывает zend_execute_ex, перехватываются вызовы всех пользовательских функций (PHP), а не только тех, за которыми расширение должно наблюдать. Это приводит к увеличению затрат на обработку, особенно если сценарий PHP вызывает много функций.
  • Компилятор генерирует оптимизированные опкоды вызова функций, различая вызовы пользовательских функций (PHP) (DO_UCALL) и вызовы внутренних функций (DO_ICALL), но, если расширение перехватывает zend_execute_ex, компилятор не может этого сделать.
  • Ловушка требует, чтобы расширения перенаправляли ее соседним расширениям, которым она также может быть нужна. Это может приводить к проблеме «шумного соседа», а при неправильном перенаправлении ловушки — к неожиданному поведению и даже сбою.
  • Ловушка несовместима с новым JIT-компилятором в PHP 8.
Учитывая все побочные эффекты перехвата zend_execute_ex, разработчикам расширений приходится подыскивать другие ловушки движка для реализации наблюдения.Специальные обработчики опкодаРасширения PHP могут предоставлять специальные обработчики существующих опкодов. Специальные обработчики опкода, связанные с вызовом функций, позволяют реализовать наблюдение без «взрыва стека» в ловушке zend_execute_ex.Однако у специальных обработчиков опкода, как и у ловушки выполнения виртуальной машины, есть ряд недостатков:
  • Специальные обработчики опкода должны вызывать обработчики опкода соседних расширений для одной ловушки. Вероятно, это связано с отсутствием документации или с тем, что два расширения, которые используют ловушку для одного и того же опкода, встречаются редко, но исторически многие расширения не перенаправляли обработчики опкода соседним расширениям.
  • Специальные обработчики опкода могут изменять состояние ВМ во время выполнения. Например, расширения могут запретить ВМ вызывать стандартный обработчик опкода. (Именно так реализована функция Xdebug scream.) Если расширение должно пропустить обработчик опкода, надежная пересылка ловушки соседнему расширению не представляется возможной. В результате возникает неопределенность: два расширения предоставляют специальный обработчик для одного и того же опкода, но одно из расширений изменяет состояние ВМ.
  • Из-за способа реализации генераторов их невозможно полностью охватить специальными обработчиками опкода.
  • Ловушка несовместима с новым JIT-компилятором в PHP 8.
Из-за сложности охвата специальными обработчиками опкода в некоторых расширениях для профилирования используется подход Zend Extension.Ловушка Zend ExtensionРасширения PHP могут регистрироваться как специальное расширение, называемое Zend Extension. Это специальное расширение может обращаться к тем частям движка, которые недоступны обычным расширениям, включая доступ к обработчикам начала и конца вызовов функций.Основным недостатком этого метода является то, что компилятору приходится генерировать два дополнительных опкода для каждого вызова функции: один для обработчика начала вызова (EXT_FCALL_BEGIN), а другой для обработчика конца вызова (EXT_FCALL_END). Эти дополнительные опкоды сильно снижают производительность и не подходят для трассировщика рабочей среды.Исходные соображенияИз-за недостатков вышеупомянутых ловушек движков некоторые разработчики предлагают другие способы перехвата вызовов функций. Например, была создана и подтверждена концепция трассировщика, который вставляет узлы в абстрактное синтаксическое дерево (AST) во время компиляции. Эти узлы вызывают функции наблюдения. Наша команда создала концептуальный трассировщик на основе этой идеи вставки узлов в AST. Однако на данный момент нам неизвестны готовые к использованию трассировщики, использующие этот подход, — вероятно, потому что вставка ловушек наблюдения перед вызовом каждой функции и после него также сильно повышает накладные расходы, как и в случае Zend Extension.Из-за низкой эффективности ловушки движка, приводящей к падению производительности и ухудшению поведения и стабильности PHP, а также по причине скорого выхода нового JIT-компилятора возник серьезный риск, что ловушки наблюдения вообще перестанут работать в PHP 8. Эта неопределенность вынудила нас обратиться к сообществу PHP и изложить свои предварительные соображения о трассировочных ловушках для PHP в октябре 2019 года. В свою очередь, это привело к обсуждению встраивания телеметрических ловушек прямо в Zend Engine, что привело бы к ощутимым улучшениям по сравнению с существующими средствами наблюдения. Так мы пришли к идее создания нового API-интерфейса наблюдателя в PHP 8.API-интерфейс наблюдателя в PHP 8Изначально для нас было важно, чтобы изменения в области наблюдения улучшили бы не только собственный трассировщик PHP (Datadog), но и всю экосистему расширений PHP, включая широкий спектр трассировщиков, профилировщиков и отладчиков.После нашего обращения к сообществу разработчиков движка PHP несколько энтузиастов откликнулись на эту инициативу в области наблюдения. Бенджамин Эберлей (Benjamin Eberlei)Никита Попов и Дмитрий Стогов оказали неоценимую помощь нашей команде в реализации API-интерфейса наблюдателя и проверили большой объем его кода. Джо Уоткинс (Joe Watkins)Боб Вайнанд (Bob Weinand) и еще несколько участников сообщества разработчиков движка PHP также внесли весомый вклад и дали ряд полезных советов.API-интерфейс наблюдателя предназначен для устранения всех упомянутых выше побочных эффектов ловушек вызовов функций. Мы одну за другой решили все проблемы.Больше никаких ограничений стекаИспользуя API-интерфейс наблюдателя, расширения могут поддерживать почти неограниченный стек вызовов. Во время фазы запуска процесса (также известной как инициализация модуля, или MINIT) расширение может зарегистрироваться в качестве наблюдателя вызова функции с помощью zend_observer_fcall_register. Это означает, что Zend Engine следует использовать специальные обработчики наблюдателя для связанных с вызовом функций опкодов и полностью устраняет искусственное ограничение стека, возникающее при переопределении zend_execute_ex.Повышение производительности для ненаблюдаемых запросов. При наличии расширения-наблюдателя компилятор может генерировать специфичные для наблюдателя опкоды, такие как OBSERVE_DO_UCALL и OBSERVE_DO_ICALL. Проблема этого подхода состоит в том, что он усложняет компилятор и добавляет множество (а именно восемь) новых опкодов, которые должны учитываться отладочными расширениями.Альтернативой для подхода со множеством опкодов является специализация опкода. При специализации опкода с одним опкодом могут быть связаны несколько обработчиков. При этом компилятор сам определяет, какой обработчик следует запускать для специального опкода.Одним из примеров специализации опкода является SPEC(RETVAL) с двумя вариантами обработчика одного опкода, когда один обработчик запускается при возврате значения, а второй — при его использовании. Пример ниже иллюстрирует эту концепцию в действии:
<?php
$a = return_something();
return_something();
Компилятор генерирует опкод DO_UCALL для каждого из вызовов функций. Но так как DO_UCALL имеет специализацию опкода SPEC(RETVAL), то во время выполнения фактически запускаются два отдельных обработчика.API обозревателя вводит специализацию опкода SPEC(OBSERVER) для опкодов, связанных с вызовами функций. При наличии расширения-наблюдателя все опкоды, связанные с вызовом функций, будут приводить к запуску специальных обработчиков наблюдателей (например, ZEND_RETURN_SPEC_OBSERVER_HANDLER для опкода RETURN). Поскольку обработчики назначаются при компиляции, этот метод позволяет избежать ухудшения производительности обработки ненаблюдаемых запросов.Целевые функции и методыКроме того, API наблюдателя при необходимости может работать только с функциями и методами, определенными в PHP, которые известны как пользовательские функции. Новый дизайн позволяет каждому расширению наблюдать только за нужными функциями, а не за всеми вызовами подряд.На схеме ниже показано, как расширение-наблюдатель наблюдает за функцией foo() в следующем сценарии PHP:
<?php
# example.php
function foo() {}
function bar() {}
for ($x = 0; $x < 2; $x++) {
    foo();
    bar();
}
API наблюдателя не наблюдает за внутренними функциями (то есть функциями из стандартной библиотеки или из расширения). В целом интерес для расширений представляет только часть внутренних функций. Внутренние функции, которые обрабатывают операции ввода-вывода, часто интересны трассировщикам, и уже существуют механизмы, позволяющие наблюдать за этими функциями посредством специальных обработчиков внутренних функций.Наблюдение с новым JIT-компиляторомНовый JIT-компилятор в PHP 8 усложняет перехват вызовов функций трассировщиками, профилировщиками и отладчиками без побочных эффектов. В рабочем предложении JIT RFC прямо сказано о необходимости создать API трассировки, которое работает с JIT-компилятором:«Профилирование времени выполнения должно работать даже с JIT-кодом, но для этого может потребоваться разработка дополнительного API трассировки и соответствующего расширения JIT, чтобы генерировать обратные трассировочные вызовы».Благодаря поддержке автора JIT и давнего разработчика движка PHP Дмитрия Стогова API-интерфейс наблюдателя способен выполнить это требование и обеспечить наблюдение, даже если JIT в PHP 8 включен.Защита от «шумного соседа»Вся поддержка ловушек теперь выполняется движком, поэтому расширениям-наблюдателям больше не нужно заботиться о перенаправлении ловушек соседним расширениям. Это позволяет нескольким трассировщикам, профилировщикам и отладчикам работать параллельно, избегая давней проблемы «шумного соседа», которая была присуща старым ловушкам.ЗаключениеИсторически сложилось так, что различные ловушки вызовов функций в Zend Engine отставали от развития движка, что приводило к побочным эффектам при наблюдении. API-интерфейс наблюдателя в PHP 8 не только нейтрализует основные побочные эффекты перехвата вызовов функций, но и вводит в движок более общую концепцию — наблюдение.Теперь будущее развитие наблюдения в PHP будет проходить под эгидой ZEND_OBSERVER. В этот API-интерфейс уже вошли функции отслеживания ошибок, а в перспективе станут открытыми для наблюдения и многие другие части движка, например этапы компиляции.Если вы хотите поучаствовать в дальнейшем развитии наблюдения в PHP, поделитесь своими идеями с помощью списка рассылки по разработке движка PHP. Кроме того, мы приглашаем всех внести посильный вклад в разработку открытого трассировщика Datadog PHP, который используется в API наблюдателя, начиная с версии 0.52.0. А чтобы серьезно заняться разработкой расширений и опкодов для PHP, вышлите свое резюме команде по интеграции APM. Datadog ищет сотрудников!
Перевод материала подготовлен в рамках курса "PHP Developer. Professional". Если вам интересно узнать о курсе подробнее, приглашаем на день открытых дверей онлайн, на котором преподаватель подробнее расскажет о формате обучения, программе и перспективах для выпускников.

===========
Источник:
habr.com
===========

===========
Автор оригинала: datadoghq.com
===========
Похожие новости: Теги для поиска: #_php, #_programmirovanie (Программирование), #_apm, #_php_8, #_observability, #_engineering, #_blog_kompanii_otus (
Блог компании OTUS
)
, #_php, #_programmirovanie (
Программирование
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 30-Апр 00:09
Часовой пояс: UTC + 5