Что делает jit-компилятор?

Подробный лог компиляции

Если требуется более подробный лог процесса компиляции, вам поможет флаг

Этот флаг — диагностический и для его включения требуется дополнительный флаг -XX:+UnlockDiagnosticVMOptions. При запуске приложения с этим флагом, логи компиляции будут писаться в лог-файл, который по умолчанию будет называться

где NNNNNN — это цифры идентификатора Java-процесса. Название лог-файла можно указать в дополнительном параметре:

Лог-файл компиляции представляет собой XML-файл весьма внушительного размера, который лучше всего смотреть с помощью специальной утилиты JITWatch, которую я упоминаю в отдельной статье.

Представление

Одна из возможных оптимизаций, используемых виртуальной машиной Sun HotSpot Java, — это объединение интерпретации и JIT-компиляции. Код приложения изначально интерпретируется, но JVM отслеживает, какие последовательности байт-кода часто выполняются, и переводит их в машинный код для прямого выполнения на оборудовании. Для байт-кода, который выполняется всего несколько раз, это экономит время компиляции и снижает начальную задержку; для часто выполняемого байт-кода JIT-компиляция используется для работы на высокой скорости после начальной фазы медленной интерпретации. Кроме того, поскольку программа тратит большую часть времени на выполнение меньшей части своего кода, сокращение времени компиляции является значительным. Наконец, во время первоначальной интерпретации кода статистика выполнения может быть собрана перед компиляцией, что помогает выполнить лучшую оптимизацию.

Правильный компромисс может варьироваться в зависимости от обстоятельств. Например, виртуальная машина Java Sun имеет два основных режима — клиентский и серверный. В клиентском режиме выполняется минимальная компиляция и оптимизация, чтобы сократить время запуска. В серверном режиме выполняется обширная компиляция и оптимизация, чтобы максимизировать производительность после запуска приложения, жертвуя временем запуска. Другие JIT-компиляторы Java использовали измерение времени выполнения, определяющее количество выполнений метода в сочетании с размером байт-кода метода, в качестве эвристики, чтобы решить, когда выполнять компиляцию. Еще один использует количество выполненных операций в сочетании с обнаружением циклов. В общем, гораздо сложнее точно предсказать, какие методы следует оптимизировать в краткосрочных приложениях, чем в долго работающих.

Native Image Generator (Ngen) от Microsoft — еще один подход к уменьшению начальной задержки. Ngen предварительно компилирует (или «pre-JIT») байт-код из образа Common Intermediate Language в машинный код. В результате компиляция среды выполнения не требуется. .NET Framework 2.0, поставляемый с Visual Studio 2005, запускает Ngen во всех библиотеках DLL Microsoft сразу после установки. Предварительное срабатывание позволяет сократить время запуска. Однако качество генерируемого кода может быть не таким хорошим, как у JIT-компилятора, по тем же причинам, по которым код, скомпилированный статически, без оптимизации на основе профиля , не может быть таким же хорошим, как JIT-скомпилированный код в крайнем случае: отсутствие профилирования данных для управления, например, встроенным кэшированием.

Также существуют реализации Java, в которых компилятор AOT (опережающий время) сочетается либо с JIT-компилятором ( Excelsior JET ), либо с интерпретатором ( компилятор GNU для Java ).

Встраивание методов

Эти оптимизации часто уменьшают объем кода и практически всегда уменьшают время выполнения, замещая последовательность инструкций вызова метода его телом. Виртуальные методы не встраиваются JIT-компилятором (даже при вызове конечного (sealed) метода у экземпляра производного типа); методы интерфейсов подвергаются частичному встраиванию; только статические и не виртуальные могут встраиваться всегда. Там где важна высокая производительность, например, в простых свойствах и методах часто используемых базовых классов, желательно избегать виртуальных методов и реализации интерфейсов.

Точные критерии, используемые JIT-компилятором для определения, какие методы могут встраиваться, недоступны. Однако экспериментальным путем нам удалось вскрыть некоторые из них:

  • методы со сложной структурой вызовов (например, циклы) никогда не встраиваются;

  • методы, включающие обработку исключений, никогда не встраиваются;

  • рекурсивные методы никогда не встраиваются;

  • методы, имеющие параметры составных типов значений, локальные переменные или возвращаемые значения никогда не встраиваются;

  • методы, размеры тел которых превышают 32 байта на языке IL никогда не встраиваются (это ограничение можно преодолеть с помощью значения MethodImplOptions.AggressiveInlining атрибута MethodImpl.

В последних версиях среды выполнения CLR были убраны некоторые искусственные ограничения, препятствующие встраиванию методов.

Например, начиная с версии .NET 3.5 SP1, 32-разрядный JIT-компилятор способен встраивать методы, принимающие параметры некоторых составных типов значений, таких как структура Point2D, которая описывалась при обсуждении типов значений. В этой версии некоторые операции с составными типами значениями замещаются эквивалентными операциями с простыми типами, при определенных условиях (операции с экземпляром типа Point2D преобразуются в операции с двумя значениями типа int), что обеспечивает лучшую оптимизацию кода, выполняющего операции со структурами в целом. Например, взгляните на следующий простой код:

JIT-компилятор в среде выполнения CLR 4.5 весь этот фрагмент кода скомпилирует в функциональный эквивалент Console.WriteLine(6), где аргумент 6 является результатом выражения 3^5. JIT-компилятор способен выполнять встраивание и распространение констант пользовательских типов значений. В версии CLR 2.0 JIT-компилятор фактически вызывает метод без какой-либо видимой оптимизации:

Несмотря на отсутствие возможности обеспечить принудительное встраивание методов, когда JIT-компилятор не считает это необходимым, у нас есть механизм, позволяющий запретить встраивание. Значение MethodImplOptions.NoInlining атрибута MethodTmpl запрещает встраивание метода, снабженного таким атрибутом — кстати говоря, это весьма полезная возможность для микрохронометража.

3.6 Настройка кэша кода

Приложения, работающие в клиентском режиме или режиме многоуровневой компиляции,Поскольку существует больше классов, которые необходимо скомпилировать (компилятор C1 имеет низкий порог компиляции и его легче достичь стандарта компиляции), проще исчерпать codeCache. Если вы обнаружите, что codeCache недостаточно (с помощью метода мониторинга, упомянутого в предыдущем разделе), вы можете настроить размер codeCache с помощью параметров запуска.

Конкретная настройка должна быть соответствующей и оценена на основе данных мониторинга, таких как увеличение за единицу времени, максимальное время непрерывной работы системы и т. Д.Если нет соответствующих статистических данных, рекомендуется установить значение, в 2 раза превышающее текущее значение (или значение по умолчанию).。

Следует отметить, что значение этого codeCache не очень велико.。Для 32-битной JVM, Максимальный объем памяти, который можно использовать, составляет 4g. Это пространство памяти 4g включает не только память кучи Java, но также память, занимаемую самой JVM, внутреннюю память, используемую в программе (например, directBuffer), и codeCache. Если codeCache установлен слишком большим, даже если он не используется так часто, JVM резервирует для него эти области памяти, что приводит к уменьшению объема памяти, который может использоваться самим приложением.Для 64-битной JVM, Поскольку объем памяти достаточно велик, слишком большой codeCache не окажет существенного влияния на приложение.

В JDK 8 предоставляется параметр запуска Распечатайте использование codeCache, когда JVM остановлена.Где max_used — максимальное использование codeCache во время всей операции. Вы можете использовать это значение, чтобы установить разумный размер codeCache, чтобы уменьшить использование памяти при обеспечении нормальной работы приложения.

Использует

JIT-компиляция может применяться к некоторым программам или может использоваться для определенных возможностей, особенно динамических возможностей, таких как регулярные выражения . Например, текстовый редактор может скомпилировать регулярное выражение, предоставленное во время выполнения, в машинный код, чтобы обеспечить более быстрое сопоставление: это невозможно сделать раньше времени, поскольку шаблон предоставляется только во время выполнения. Некоторые современные среды выполнения полагаться на JIT компиляции для выполнения высокоскоростных кодов, в том числе большинство реализаций Java вместе с Microsoft «s .NET Framework . Точно так же многие библиотеки регулярных выражений поддерживают JIT-компиляцию регулярных выражений либо в байт-код, либо в машинный код. JIT-компиляция также используется в некоторых эмуляторах для перевода машинного кода из одной архитектуры ЦП в другую.

Распространенной реализацией JIT-компиляции является сначала AOT-компиляция в байт-код (код виртуальной машины ), известная как компиляция байт-кода , а затем JIT-компиляция в машинный код (динамическая компиляция), а не интерпретация байт-кода. Это улучшает производительность во время выполнения по сравнению с интерпретацией за счет задержки из-за компиляции. Компиляторы JIT выполняют перевод непрерывно, как и интерпретаторы, но кэширование скомпилированного кода минимизирует задержку при выполнении того же кода в будущем во время данного прогона. Поскольку компилируется только часть программы, задержка значительно меньше, чем если бы вся программа была скомпилирована перед выполнением.

Компиляция инструкций MSIL в машинный код

Перед запуском MSIL его необходимо скомпилировать в машинный код в среде CLR для архитектуры конечного компьютера. Платформа .NET предоставляет два способа такого преобразования:

  • JIT-компилятор платформы .NET.

Компиляция с помощью JIT-компилятора

При JIT-компиляции язык MSIL преобразуется в машинный код во время выполнения приложения по требованию, когда загружается и выполняется содержимое сборки. Поскольку среда CLR предоставляет JIT-компилятор для каждой поддерживаемой архитектуры процессора, разработчики могут создавать набор сборок MSIL, которые могут компилироваться с помощью JIT-компилятора и выполняться на разных компьютерах с разной архитектурой. Если управляемый код вызывает специфический для платформы машинный API или библиотеку классов, то он будет выполняться только в соответствующей операционной системе.

При JIT-компиляции учитывается возможность того, что определенный код может никогда не вызываться во время выполнения. Чтобы не тратить время и память на преобразование всего содержащегося в PE-файле MSIL в машинный код, при компиляции MSIL преобразуется в машинный код по мере необходимости во время выполнения. Полученный таким образом машинный код сохраняется в памяти, что позволяет использовать его при дальнейших вызовах в контексте этого процесса. Загрузчик создает и присоединяет заглушки к каждому методу в типе, когда тип загружается и инициализируется. При первом вызове метода заглушка передает управление JIT-компилятору, который преобразует MSIL для этого метода в машинный код и заменяет заглушку на созданный машинный код. Поэтому последующие вызовы метода, скомпилированного с помощью JIT-компилятора, ведут непосредственно к машинному коду.

Создание кода во время установки с помощью NGen.exe

Тот факт, что JIT-компилятор преобразует MSIL-код сборки в машинный код при вызове отдельных методов, определенных в этой сборке, отрицательно сказывается на производительности во время выполнения. В большинстве случаев снижение производительности приемлемо

Что более важно, код, созданный JIT-компилятором, будет привязан к процессу, вызвавшему компиляцию. Его нельзя сделать общим для нескольких процессов

Чтобы созданный код можно было использовать в нескольких вызовах приложения или в нескольких процессах, которые совместно используют набор сборок, среда CLR предоставляет режим предварительной компиляции. В таком режиме компиляции для преобразования сборок MSIL в машинный код в стиле JIT-компилятора используется генератор образов в машинном коде (Ngen.exe). Однако, работа Ngen.exe отличается от JIT-компилятора в трех аспектах.

  • Ngen.exe выполняет преобразование из MSIL-кода в машинный код перед выполнением приложения, а не во время его выполнения.

  • При этом сборка компилируется целиком, а не по одному методу за раз.

  • Она сохраняет созданный код в кэше образа машинного кода в виде файла на диске.

Проверка кода

В процессе компиляции в машинный код MSIL-код должен пройти проверку, если только администратор не установил политику безопасности, разрешающую пропустить проверку кода. MSIL-код и метаданные проверяются на типобезопасность. Это подразумевает, что код должен обращаться только к тем адресам памяти, к которым ему разрешен доступ. Типобезопасность помогает изолировать объекты друг от друга и способствует их защите от непредумышленного или злонамеренного повреждения. Она также гарантирует надежное применение условий безопасности для кода.

Среда выполнения основывается на истинности следующих утверждений для поддающегося проверке типобезопасного кода:

  • ссылка на тип строго совместима с адресуемым типом;

  • для объекта вызываются только правильно определенные операции;

  • удостоверения являются подлинными.

В процессе проверки кода MSIL делается попытка подтвердить, что код может получать доступ к расположениям в памяти и вызывать методы только через правильно определенные типы. Например, код не должен разрешать доступ к полям объекта так, чтобы можно было выходить за границы расположения в памяти. Кроме того, проверка определяет, правильно ли был создан код MSIL, поскольку неверный код MSIL может приводить к нарушению правил строгой типизации. В процессе проверки передается правильно определенный типобезопасный код. Однако иногда типобезопасный код может не пройти проверку из-за ограничений процесса проверки, а некоторые языки по своей структуре не позволяют создавать поддающийся проверке типобезопасный код. Если в соответствии с политикой безопасности использование типобезопасного кода является обязательным и код не проходит проверку, то при выполнении кода создается исключение.

О компиляции C# кода

Исходный код C # проходит через 2 этапа компиляции, чтобы стать инструкциями CPU, которые могут быть выполнены.

Обычно первый этап происходит на вашем CI сервере, а второй шаг происходит позже, во время работы самого приложения. Когда же мы работаем локально в Visual Studio, то она все эти шаги выполняет перед запуском приложения из меню Debug.

Шаг первый. Компиляция приложения
Ваш код превращается в Common Intermediate Language (CIL), который уже может быть выполнен в любом окружении, которое поддерживает CIL

Обратите внимание, что собранная сборка не является читаемым текстом IL, а фактически метаданными и байтовым кодом в виде двоичных данных

На данном шаге будет выполнена некоторая оптимизация кода (будет описано дальше).

Шаг второй. JIT компилятор
JIT компилятор конвертирует IL код в инструкции процессора, которые можно выполнить на вашей машине. Однако не вся компиляция происходит заранее — в нормальном режиме, код компилируется, только тогда когда его вызывают в первый раз, после чего он кэшируется.

Компилятор JIT — это всего лишь один из целого ряда сервисов, которые составляют Common Language Runtime (CLR), позволяя ему выполнять код .NET.

Основная часть оптимизации кода будет проведена на этом шаге.

Настройка свойств упаковки

Многие из этих свойств, например Использовать общую среду выполнения и Использовать Fast Deployment, предназначены для режима отладки. Тем не менее для режима выпуска приложения предусмотрены другие настройки, которые определяют, как приложение , как обеспечивается и как следует упаковывать приложения для поддержки различных архитектур и ограничений по размеру.

Указание поддерживаемых архитектур

При подготовке приложения Xamarin.Android к выпуску необходимо указать поддерживаемые архитектуры процессоров. Один APK-файл может содержать код для нескольких различных архитектур. О поддержке нескольких архитектур процессоров см. раздел Архитектуры ЦП.

Создание одного пакета (APK) для каждого выбранного ABI

Если этот параметр включен, один пакет APK создается для каждого поддерживаемого ABI (выбираемого на вкладке Дополнительно, как описано в разделе Архитектуры ЦП), а не один большой пакет APK для всех поддерживаемых ABI. Этот параметр доступен, только если проект настроен для режима «Выпуск». По умолчанию параметр отключен.

Multi-DEX

Если параметр Включить Multi-Dex включен, инструменты Android SDK Tools используются для преодоления предела в 65 тысяч методов для формата DEX-файлов. Ограничение метода предел 65 тысяч основано на количестве методов Java, на которые ссылается приложение (включая те, которые от них зависит приложение) — оно не зависит от количества методов, которые записываются в исходном коде. Если приложение определяет лишь несколько методов, но использует много методов (или большие библиотеки), вероятно, что предел в 65 тысяч будет превышен.

Бывает, что приложение не использует каждый метод в каждой ссылаемой библиотеке. Поэтому можно воспользоваться таким средством, как ProGuard (см. выше), чтобы удалить неиспользуемые методы из кода. Параметр Включить Multi-Dex рекомендуется включать только в случае крайней необходимости, т. е. приложение по-прежнему ссылается на более чем 65 тысяч методов Java даже после использования ProGuard.

Дополнительные сведения о Multi-Dex см. в статье о настройке приложений с более чем 64 тысячами методов.

Пакеты приложений Android

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

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

Теперь можно создать пакет приложений, выполнив . Будет создан пакет приложений для вашего приложения.

Дополнительные сведения о пакетах приложений Android см. в разделе Пакеты приложений Android.

Как это работает?

Есть так называемый «монитор», который будет смотреть на код во время его работы. Когда этот монитор обнаруживает повторяющиеся части вашего кода, он помечает эти части как «теплые» или «горячие», в зависимости от частоты использования.

Эти горячие части могут быть скомпилированы как оптимизированный машинный код и использоваться на лету, в нужный момент, вместо реального кода. Конечно на самом деле все гораздо сложнее, если вы хотите узнать больше, вы можете чекнуть ускоренный курс Mozilla в JIT-компиляторах.

Для этого поста в блоге достаточно понять, что JIT-компилятор может значительно улучшить производительность вашей программы…ну или нет :))

Зеев Сураски, один из разработчиков ядра PHP, недавно продемонстрировал демо с генерацией фракталов:

Необычно для PHP, не правда ли? «Слон в комнате»: Часто ли вы видели, чтобы PHP использу для создания фрактальных анимаций :)))

Зная, что JIT-компилятор пытается идентифицировать «горячие» части вашего кода, вы можете догадаться, почему он оказывает такое влияние на фрактальный пример: множество одних и тех же вычислений происходит снова и снова.

Однако, поскольку PHP чаще всего используется при создании веб-приложений, мы также должны измерить влияние JIT именно на эту область.

А оказывается, что во время обработки веб-запроса кода гораздо меньше. Это не означает, что JIT вообще не может улучшить производительность сети, но мы уже не увидим подобных улучшений, как в случае с фрактальным примером. Дело в том, что в большинстве случаев PHP-приложения ограничены по вводу-выводу (I/O bound, обработка сетевых соединений, чтение и запись файлов, обращение к СУБД, кэширование и т.п.), а JIT лучше всего работает с кодом, который ограничен по процессору (CPU bound).

Это причина, чтобы отказаться от JIT? Точно нет! Есть веские аргументы, чтобы добавить его, хотя это может и не оказать должного влияния на производительность, на которую мы все же надеемся.

Это достаточно веские аргументы в пользу JIT. К сожалению, есть аргументы и против JIT:

— Сложность в обслуживании

Поскольку JIT генерирует машинный код, вы можете представить его как достаточно сложный материал для понимания «программистом языка более высокого уровня».

Имея машинный код в качестве вывода, будет сложнее отлаживать возможные ошибки в JIT-компиляторе PHP. К счастью, есть инструменты, помогающие отладке. Но все же, это машинный код.

Скажем, в JIT-компиляторе есть ошибка, вам нужен разработчик, который знает, как ее исправить. Дмитрий Стогов — тот, кто до сих пор делал большую часть кода, и да, надо помнить, что разработка ядра PHP осуществляется на добровольной основе сообществом контрибьюторов.

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

Конечно, люди могут узнать, как работает компилятор. Но это все же сложный материал. Прямо сейчас он насчитывает около 50+ тысяч строк кода

И обратите внимание: это не обычная база кодов клиент-веб-приложение. Это почти-так-же-близко-как-программирования-CPU

Опять же, это не должно быть причиной отказа от JIT, но стоимость обслуживания должна быть тщательно продумана.

Стоит упомянуть еще одну деталь:

Кроссплатформенность. Начиная с последних версий, JIT также работает в Windows и Mac! Большой шаг вперед.

История

Самые ранние опубликованные JIT компилятор , как правило , связано с работой на LISP от Джона Маккарти в 1960 г. В его семенные бумажными Рекурсивные функции символических выражений и их вычисление с помощью машины, часть I , он упоминает функции, которые переведены во время выполнения, тем самым избавляя от необходимости в сохраните вывод компилятора на перфокарты (хотя это было бы более точно известно как «система компиляции и запуска »). Другой ранний пример был от Кена Томпсона , который в 1968 году дал одно из первых приложений регулярных выражений здесь для сопоставления с образцом в текстовом редакторе QED . Для ускорения работы Томпсон реализовал сопоставление регулярных выражений с помощью JIT- кода с кодом IBM 7094 в Совместимой системе разделения времени . Влиятельный метод получения скомпилированного кода из интерпретации был впервые предложен Джеймсом Г. Митчеллом в 1970 году, который он реализовал для экспериментального языка LC² .

Smalltalk (ок. 1983 г.) стал пионером в новых аспектах JIT-компиляций. Например, перевод в машинный код выполнялся по запросу, а результат кэшировался для дальнейшего использования. Когда памяти становилось мало, система удаляла часть этого кода и регенерировала его, когда он снова понадобился. Язык Sun’s Self значительно улучшил эти методы и в какой-то момент был самой быстрой системой Smalltalk в мире; достижение до половины скорости оптимизированного C, но с полностью объектно-ориентированным языком.

Sun отказалась от Self, но исследования перешли на язык Java. Термин «JIT-компиляция» был заимствован из производственного термина « Just in time » и популяризирован Java, а Джеймс Гослинг использовал этот термин с 1993 года. В настоящее время JITing используется в большинстве реализаций виртуальной машины Java , например HotSpot. опирается на эту исследовательскую базу и широко использует ее.

Проект HP Dynamo был экспериментальным JIT-компилятором, в котором формат «байт-кода» и формат машинного кода были одинаковыми; система превратила машинный код PA-6000 в машинный код PA-8000 . Как ни странно, это привело к ускорению, в некоторых случаях на 30%, поскольку выполнение этого позволило оптимизировать на уровне машинного кода, например, встраивая код для лучшего использования кэша и оптимизируя вызовы динамических библиотек и многие другие оптимизации времени выполнения, которые обычно компиляторы не могут попытаться.

В ноябре 2020 года PHP 8.0 представил JIT-компилятор.

Отладка JIT (opcache.jit_debug)

PHP JIT обеспечивает способ выдачи отладочной информации JIT путем установки конфигурации INI. Когда установлен определенный флаг, он выводит код сборки для дальнейшей проверки.

Директива opcache.jit_debug принимает значение битовой маски для включения определенных функций. В настоящее время он принимает значение в диапазоне от 1 (0b1) до 20 двоичных разрядов. Значение 1048576 представляет максимальный уровень вывода отладки. 

JIT поддерживает несколько других параметров конфигурации, чтобы настроить, сколько вызовов функций делает ее «горячей» функцией, которая затем компилируется JIT, и порог для определения того, какие функции следует «JIT’ировать», на основе процента общих вызовов функций.

Полный список см. В разделе Конфигурация Opcache.  

Принцип работы

JIT или динамический компилятор ускоряет производительность приложений во время выполнения. Поскольку Java программа состоит из классов и объектов. По сути, представляет собой байт-код, который не зависит от платформы и выполняется JVM в различных архитектурах.

Рабочий процесс:

На диаграмме ниже показано, как происходит фактическая компиляция в среде выполнения Java.

Когда вы кодируете Java-программу, JRE использует компилятор javac для компиляции исходного кода высокого уровня в байт-код. После этого JVM загружает байт-код во время выполнения и преобразует его в двоичный код машинного уровня для дальнейшего выполнения с использованием Interpreter.

JIT-компилятор активируется и включается по умолчанию, когда метод вызывается. Когда метод компилируется, виртуальная машина вызывает скомпилированный код метода напрямую, не интерпретируя его. Следовательно, не требует большого использования памяти и процессорного времени. Это в основном ускоряет производительность приложения.

Улучшит ли JIT производительность?

Короче, «это зависит от обстоятельств». Для веб-приложений может и не очень, но вот для PHP как экосистемы даже может чрезмерно.

PHP, по замыслу, обычно работает в конфигурации без совместного использования ресурсов. После обработки каждого запроса приложение полностью закрывается. Это дает JIT очень мало времени на анализ и оптимизацию кода, тем более, что большая часть кода в типичном веб-запросе выполняется только один раз, поскольку запрос обрабатывается линейно. Кроме того, большей частью этих приложений часто является ввод-вывод (в основном, общение с базой данных), и JIT вообще не может с этим помочь. Результаты тестов, которые были опубликованы на данный момент, показывают, что JIT предлагает лишь незначительное повышение производительности в типичных приложениях PHP, запускаемых через PHP-FPM.

JIT потенциально может быть действительно полезен в тех случаях, где PHP сегодня совсем не рассматривается. Реальные преимущества будут видны в работе постоянных демонов, парсерах, машинном обучении и других длительных процессах, интенсивно использующих CPU.

PHP-Parser — это «анализатор PHP, написанный на PHP». Он от того же Никиты Попова, и используется многими инструментами статического анализа, представленными сегодня на рынке, такими как, например, PHPStan и пр.  Никита сообщил, что PHP-Parser в некоторых случаях работает в два раза быстрее с новым движком JIT.

Асинхронные приложения, написанные на React PHP или AmPHP , вероятно, также увидят заметное улучшение, хотя и не настолько, как правило, они выполняют много операций ввода-вывода (где JIT бесполезен).

Может быть для вас будет шоком узнать, что для PHP доступны библиотеки машинного обучения, такие как Rubix ML или PHP-ML. Они не так широко используются, как их собраты на Python, отчасти потому, что при интерпретации они, как правило, медленнее, чем библиотеки C с красивыми оболочками Python. Однако с JIT эти задачи с интенсивным использованием CPU могут оказаться такими же быстрыми или, возможно, даже более быстрыми, чем те, которые доступны на других языках.

PHP больше не является «просто самым быстрым из основных языков веб-сценариев». Теперь это жизнеспособный высокопроизводительный язык общей обработки данных, с передачей постоянных воркеров, машинного обучения и других высокопроизводительных задач, в руки миллионов PHP-разработчиков по всему миру.

Отключение проверки границ

Когда осуществляется обращение к элементам массивов, среда выполнения CLR должна гарантировать, что индекс, используемый для доступа к элементам, не окажется за пределами массива. В отсутствие такой проверки снимаются гарантии безопасности доступа к памяти; вы могли бы инициализировать объект byte[] и обращаться с его помощью к произвольным участкам памяти по положительным и отрицательным индексам. Несмотря на абсолютную необходимость, эта проверка имеет накладные расходы, стоимостью в несколько инструкций. Ниже показан код, сгенерированный JIT-компилятором для типичной операции доступа к массиву:

Существуют особые ситуации, когда JIT-компилятор может отключить проверку границ при обращении к элементам массива — в цикле for, выполняющем обход всех элементов. Без этой оптимизации операции доступа к массивам всегда были бы медленнее, чем в неуправляемом коде, что является неприемлемой потерей производительности в приложениях, осуществляющих сложные расчеты и интенсивно работающих с памятью. Для следующего цикла JIT-компилятор отключит проверку границ:

В этом цикле выполняется единственная проверка — проверка условия выхода из цикла

Обратите внимание, что проверка доступа к элементам массива внутри цикла не выполняется — выделенная строка выполняет запись в k-й элемент массива без проверки выхода индекса k за пределы массива

К сожалению, эта оптимизация очень чувствительна. Некоторые, невинные на первый взгляд изменения в цикле, могут отрицательно сказаться на этой оптимизации и вынудить компилятор добавить проверку границ массивов:

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

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: