Самый быстрый шаблонизатор для PHP +7



Выбирая шаблонизатор для проекта Comet, я решил сравнить все популярные в PHP-коммьюнити движки. Обычно такой выбор диктует фреймворк: симфонист шаблоны завернет в Twig, программист Laravel вооружится Blade. Но меня интересовал вопрос — как эти варианты отличаются в плане производительности? После тестирования семи движков и чистого PHP я получил ответ. Данные, графики, чемпионы и лузеры — под катом!



Имена топовых претендентов вспомнил сам, остальные нашел, вооружившись статистикой GitHub и обсуждениями на Reddit. Вот такой список получился:

Smarty: github.com/smarty-php/smarty
Plates: github.com/thephpleague/plates
Mustache: github.com/bobthecow/mustache.php
Twig: github.com/twigphp/Twig
Blade: github.com/jenssegers/blade
BladeOne: github.com/EFTEC/BladeOne
Latte: github.com/nette/latte

Если знаете интересный вариант — пишите, добавлю в тест. Blade довольно глубоко интегрирован в Laravel, поэтому пришлось взять пару его standalone-реализаций. К сожалению, ни одна из них не поддерживает компоненты Blade-X.

Чтобы понять суть бенчмарка, проще всего взглянуть на версию кода с чистым PHP:

$data = [ 
    (object) [
    "code" => 200, 
    "message" => "OK"
    ],
    (object) [
        "code" => 404, 
        "message" => "Not Found"
    ],
    (object) [
        "code" => 500, 
        "message" => "Internal Server Error"
    ],		 
];

$html = '<html><head></head><body>';
foreach ($data as $message) {
    $html .= "<p>$message->code : $message->message</p>";
}
$html .= '</body></html>';

Это синтетический тест вывода в HTML-шаблон массива из трех объектов, содержащих два свойства: HTML-код и его краткое описание.

Так выглядит аналог на Twig:

<html><head></head><body>

    {% for message in data %}
        <p>{{ message.code }} : {{ message.message }}</p>
    {% endfor %}

</body></html>

А это Blade:

<html><head></head><body>

    @foreach ($data as $message)
        <p>{{ $message->code }} : {{ $message->message }}</p>
    @endforeach

</body></html>

Тесты прогонялись в контейнере Ubuntu 20.04 / PHP 7.4 / Comet 0.6 на виртуалке с 4 ядрами Ryzen 3600 и 4G памяти:

wrk --connections=500 --threads=2 --duration=10s http://comet:8080/php

Получился такой расклад, каждый график отражает среднее количество успешно отработанных за одну секунду запросов:



Чистый PHP — ожидаемо первый, но неожиданно, что Blade отстает аж в два раза! И почему «легковесный» Plates отстает от «мощного» Twig? Все фреймворки используют штатное кеширование, так что результаты максимально приближены к реальным.

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

На правах рекламы: посмотрите на Comet, в ближайших планах — сделать его самым быстрым и удобным PHP-фреймворком для создания restful API и микросервисов :)




Комментарии (42):

  1. mr_tron
    /#21683404 / +5

    Решил я как-то побенкмарчить шаблонизаторы на шаблонизаторе и выяснилось что сам шаблонизатор быстрее чем шаблонизаторы на шаблонизаторе.

    • gotz
      /#21683414

      Воистину :) Но «сам шаблонизатор» не делает базовых вещей типа экранирования HTML-тегов, поэтому я даже удивлен, что наш эталон быстрее не в разы, а всего лишь на 20%

      • SamDark
        /#21683536

        Чего тут удивляться? Тот же Twig по факту "компилит" шаблон в PHP код, да ещё и оптимизированный и вот он уже кешируется и запускается.

        • gotz
          /#21683572

          Здесь не очень уместно приводить скомпилированный Twig, но это внушительный файлик из восьмидесяти строк… против четырех для чистого PHP. Скорее всего, разница нивелируется архитектурой Comet, в которой все классы подгружаются один раз и остаются в памяти. В классическом FPM-приложении разница была бы более драматической. Один раздел импортов в скомпилированном шаблоне Твиг чего стоит:

          use Twig\Environment;
          use Twig\Error\LoaderError;
          use Twig\Error\RuntimeError;
          use Twig\Extension\SandboxExtension;
          use Twig\Markup;
          use Twig\Sandbox\SecurityError;
          use Twig\Sandbox\SecurityNotAllowedTagError;
          use Twig\Sandbox\SecurityNotAllowedFilterError;
          use Twig\Sandbox\SecurityNotAllowedFunctionError;
          use Twig\Source;
          use Twig\Template;

          • SamDark
            /#21685148

            Опкеш и preload никто не отменял. И да, срезать время на предзагрузке — это нормальная идея. Ещё больше удастся получить если вместо Workerman взять Swoole или RoadRunner. Мы под это дело готовим Yii 3, результаты отличные.

            • gotz
              /#21685290 / +1

              Techempower Benchmarks считают что workerman самый быстрый :)

              www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=fortune&l=zik073-1r

              • SamDark
                /#21685632

                О, значит у меня устаревшие данные. Спасибо.

                • morozovsk
                  /#21691738

                  Оказывается несколько дней назад рейтинги пересчитали. Я этого момента уже несколько месяцев жду. Очень круто, что workerman так высоко поднялся. С последнего пересчёта удалось ускорить в 2 раза, ну а spiral оказался даже ниже чем, я ему пророчил, в абсолютных цифрах то же самое, а в относительных — 31 место вместо 16.

                  image

                  Мы под это дело готовим Yii 3, результаты отличные.

                  Было бы круто запускать yii3 под workerman из коробки. Сейчас у меня есть свой велосипед для запуска yii2 из под workerman, но его трудно поддерживать.

                  • gotz
                    /#21691798

                    Эти тесты циклически гоняются по кругу каждые 5-7 дней, не надо ждать результатов несколько месяцев — надо пользоваться ссылками в описании, чтобы отслеживать свежие результаты :) Топовые позиции workerman и php-ngx наблюдаю в нем весь 2020 год. Сегодня приняли мой пулл реквест в мастер с фреймворком Comet на базе Workerman. Надеюсь к Round 20 тоже выйти в топ

  2. kozar
    /#21683584

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

    • gotz
      /#21683600

      Да, но мне нравится дефолтная экранизация в шаблонизаторах, вложение partials, фильтры в духе Twig… но все это в разной мере доступно во фреймворках, это отдельные критерии для оценки.

      • SamDark
        /#21685164

        Вложенные partials довольно легко реализуются, как и декораторы. А вот с экранированием беда, это верно. Хотя и в Twig частенько встречаю в проектах |raw.

    • edogs
      /#21683640

      Адекватного наследования шаблонов не хватает, вот прям очень сильно, из-за этого скатились постепенно к шаблонизаторам.
      А еще не хватает возможности дать стороннему верстальщику доступ к коду не думая о безопасности и работоспособности и не объясняя ему основы php.

    • gluck59
      /#21697950

      Да, у PHP многое отлично получается. Но нужно обязательно использовать фреймворк, потому что нужно использовать фреймворк.

  3. bo0rsh201
    /#21683668

    Первое, что приходит в голову — github.com/alexeyrybak/blitz/wiki
    Хотя я не большой эксперт по шаблонизаторам в php и почти уверен, что по сравнению с участниками вашего теста, у блица весьма скудный функционал.
    Но посмотреть на бенчмарк было бы интересно :)

    • gotz
      /#21684280

      Ох, похоже Blitz не развивается :( Последние коммиты от 2017 года

      • bo0rsh201
        /#21684346 / +1

        Он просто настолько стабилен и хорош, что все идеально работает и больше ничего добавлять не нужно!

        • gotz
          /#21685020

          Еще раз вернулся к Blitz и обнаружил более серьезный стоп-фактор: Blitz is a PHP extension distributed in source code (Win32 users can download compiled DLL's).

          Для меня принципиально было использовать нативный шаблонизатор, поэтому и Phalcon Volt не взял в тестирование.

        • NickyX3
          /#21689332

          Именно так и есть. Собственно там есть все, что нужно.

      • NickyX3
        /#21689292

        Смотрите ветку PHP7, ошибки правятся. Юзаем на двух проектах. По скорости близко к нативному PHP. Ну как бы и понятно, оно расширение.

  4. trawl
    /#21683922

    А если бенчмарк приблизить к реальности? Ну там лайауты, эмбеды…

    • gotz
      /#21684290

      Да, решил приблизить к реалиям — более сложные шаблоны в течение недели протестировать.

  5. a-tk
    /#21683926

    Бенчмарк был полноценный или один прогон на холодных данных?
    Мастерство представления масштабов на диаграмме — это вообще верх мастерства. Если сделать на графике диапазон от 30 до 50, то для некоторых шаблонизаторов будет казаться, что их производительность отрицательная.

    • gotz
      /#21684292 / -2

      Я сам люблю графики от ноля, но в данном случае график рисует онлайн-сервис чартов, он решил масштабировать от минимального значения

      • a-tk
        /#21684558 / +3

        Выбор подходящих инструментов — непростая работа.

      • morozovsk
        /#21691760

        А ещё у вас из-за выбранного красивого шрифта названия очень трудно читаются.
        В следующий раз можете попробовать в google spreadsheets.
        Я там делаю, получается например вот так:

        • gotz
          /#21691800

          да, на Reddit тоже были нарекания на шрифт. попробую поменять, хотя с эстетической точки зрения мне хорошо зашел :)

  6. vdem
    /#21684026

    А не лучше ли вместо

    $html = '<html><head></head><body>';
    foreach ($data as $message) {
        $html .= "<p>$message->code : $message->message</p>";
    }
    $html .= '</body></html>';
    

    делать
    <html><head></head><body>
    <?php foreach ($data as $message) { ?>
        <p><?= $message->code ?> : <?= $message->message ?></p>
    <?php } ?>
    </body></html>
    


    Такой подход мало чем отличается от прочих шаблонизаторов.

    • gotz
      /#21685002

      Можно и такой вариант попробовать, через eval() кода с шаблоном. Спасибо за идею.

      • vdem
        /#21685050 / +1

        Вместо eval может больше подойти что-то вроде

        class Renderer
        {
        ...
            function render($viewPath, array $params = [])
            {
                extract($params);
                include($viewPath);
            }
        ...
        }
        

        Ну и использовать ob_* функции если надо вывод перехватить.

        • gotz
          /#21685270

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

          • vdem
            /#21685316

            Ну чтоб совсем быстро было, можно вообще не париться с фреймворками и обойтись даже без подключения дополнительных файлов. Т.е. и конфиг для доступа к БД, и подключение к ней, и выборка/запись данных и прочая логика, и HTML — в одном файле. Будет ОЧЕНЬ быстро :D Так делали 20-25 лет назад. А если серьезно, то например само подключение к БД займет намного больше времени (если оно используется, конечно).

            P.S. Я хочу сказать, что это не тот случай, когда следует беспокоиться о производительности. Ну выиграете вы с eval несколько лишних микросекунд (если, конечно, выиграете), толку с этого?

            P.P.S. Ведь view все равно подключать надо как отдельный файл, или вы это в контроллере хранить собираетесь? И параметры ему распаковать тоже надо.

            • gotz
              /#21685496

              Так я же и не ратую за экономию микросекунд :) Выбираю полноценный шаблонизатор, реализованный «по всей красоте» современных стандартов. Но план именно в том, чтобы взять готовый движок, поддерживаемый и используемый в коммьюнити, а не написать свой :) Вот эти все полноценные фреймворки в тесте — они как раз хранять шаблоны в отдельных файлах и подгружают / компилируют их в процессе вызова. Но надо расшарить бенчмарки на GitHub, чтобы это было более наглядно.

  7. Get-Web
    /#21686830

    Было бы интересно сравнение разных более сложных конструкций
    Вот этот шаблонизатор возможно заинтересует github.com/fenom-template/fenom

  8. psychodelist
    /#21687598

    Есть ещё такой Fenom

    • vdem
      /#21687654

      А в предыдущем комменте (за ~3 часа до вашего) на какой шаблонизатор ссылка?

  9. GordeyUA
    /#21689388

    Интересная идея, жду продолжения. Большая просьба — не используйте «маркетологические» диаграммы, начинающиеся не от нуля. Это затрудняет визуальную оценку результатов — когда в тексте «отстает в два раза», а на диаграмме разница в высоте столбцов раз в пять.

  10. gluck59
    /#21697924 / +1

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