Yii2 тестируем на HHVM

    Всем привет! Недавно зарелизился Yii2 с кучей новых фич и поддержкой HHVM, разработчики говорят о совместимости в 99%. Попробуем завести все это дело и опробовать в действии на живых примерах, где будут выборки из БД, сериализация (десирализация) данных, json — encode, decode, работа с ActiveRecord. Но прежде немного о самой машине. HHVM — экспериментальная виртуальная машина от Facebook для исполнения и JIT компиляции PHP кода. За счет неё можно увеличить производительность в несколько, а то и пять — девять раз на ресурсозатратных задачах. Проект живет и активно развивается. По поводу выхода новых версий хорошо написано в статье на хабре.

    Что было сделано, оптимизировано в HHVM:


    — Поддержка функционала php в частности 5.6, поддержку функций: eval и create_function добавили в последних версиях.
    — Написан новый язык программирования Hack — php — подобный язык со статической типизацией.
    — Переработан APC сache, альтернативу которому включает в себя HHVM, были убраны функции сериализации (десириализации), которые как известно, очень накладны по ресурсам.
    — Ускорены функции по JSON кодированию данных,  UTF8 / UTF16 преобразований.
    — Менее затратный подсчет ссылок — каждая строка, массив или объект в php имеют счетчик ссылок, счетчик увеличивается, когда переменная связываться со значением, и уменьшаться когда переменная выходит из области видимости. Такие операции занимают значительное процессорное время. Был разработан отдельный компилятор, который стараются избежать подсчет ссылок, когда это не нужно.
    — Улучшено распределение памяти — были оптимизированы проблемные места. Там где память выделяется и в дальнейшем не используется, она освобождается.

    Установка HHVM:


    В настоящее время доступна инсталляция пакетов и компиляция исходных кодов для всех популярных дистрибутивов.
    Посмотреть поддержку можно здесь:
    HHVM (версия 3.3.1) завелась без проблем из пакетов на Debian 7.7 и Ubuntu 14.04
    Установка
    wget -O - http://dl.hhvm.com/conf/hhvm.gpg.key | sudo apt-key add -
    echo deb http://dl.hhvm.com/debian wheezy main | sudo tee /etc/apt/sources.list.d/hhvm.list
    sudo apt-get update
    sudo apt-get install hhvm
    


    После установки создаеться файлик конфигурации /etc/nginx/hhvm.conf, в котором уже записаны базовые настройки для location. нам остается только создать хост для yii в /etc/nginx/sites-available/
    Пример минимального конфига
    server {
       root /www/hhvm.re/public_html;
       index index.html index.htm index.php;
       server_name hhvm-yii.local; 
       include hhvm.conf; 
       location / {
           try_files $uri $uri/ /index.php?$args;
       }
    
       location ~ /\.ht {
           deny all;
       }
    }
    


    Все, устанавливаем Yii привычным способом через composer.
    Cтартуем hhvm, перезапускаем nginx.
    HHVM должна запуститься. Если нет, то можно посмотреть логи /var/log/hhvm/error.log
    Так же можно подкрутить конфигурацию php, в hhvm она своя /etc/hhvm/server.ini.

    Время тестов.


    Тестирование проводилось на стареньком 2х ядерном ноутбуке, amd 64, @ 1,9 ГГц 4 гб ОЗУ:
    Debian 7.7,
    Nginx 1.2.1,
    MySQL 5.5.38
    Одна конфигурация: php-fpm 5.6
    Вторая: hhvm 3.3.1

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

    1. Запуск Yii2 из коробки показал практически одинаковую производительность. Видимо, фреймворк и так достаточно легкий, чтобы что-то оптимизировать. Привет, WordPress )

    2. Вывод 300 новостей с пагинацией и разными виджетами:

    $newsList = new ActiveDataProvider([
              'query' => News::find(),
              'pagination' => [
                  'pageSize' => 30,
              ],
       'sort' => false,
    ]);
    


    3. А теперь возьмем данных побольше, например, 5к товаров, с прописанными связями к поставщикам, магазинам, и категориям. Выведем по 300 товаров на страницу, чтобы не мелочиться. Профит есть, но пока не такой как хотелось бы:

    $productList = ActiveDataProvider([
        'query' => Product::find()
            ->where([
                'statusId' => 1,
            ]),
        'pagination' => [
            'pageSize' => 300,
        ],
        'sort' => false,
    ]);
    


    4. Стандартная задача. У нас 500 категорий. Подсчитать количество товаров к каждой категории. Конечно, результаты можно и в кэш положить и хранить count где-то в отдельном поле. Но нам вот хочется их в runtime отрабатывать, посмотрим:

    $categoryList = ProductCategory::find()->all();
    $listCount = [];
    foreach ($categoryList as $category){
        $listCount[] =  Product::find()
            ->where(['id_category' => $category->id])
            ->count();
    }
    

    Тут результаты уже поинтереснее, прирост почти в 4 раза. Неплохо да?

    5. Сериализация (десериализация) Например, захотели мы объекты товаров хранить в каком-нибудь Memcache. Посмотрим, с какой скоростью они будут запаковываться/ распаковываться. Операция бывает достаточно дорогая, не поспоришь, особенно на больших данных. Прирост в 3,67 раз:

    $productList = Product::find()->all();
    foreach ($productList as $product){
        $serialize = serialize($product);
        unserialize($serialize);
    }
    


    6. Часто приходиться кодировать/ декодировать данные в json. Особенно актуально для разных REST Api сервисов или выборку данных для Single Page Application. Обработка c HHVM впечатляет, быстрее в 5 раз:

    $productList = Product::find()->all();
    foreach ($productList as $product){
        $encode = json_encode($product);
        json_decode($encode);
    }
    


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

    for ($i=0; $i < 5000; $i++){
        $customer = new Customer();
        $customer->attributes = ['name' => $i];
        $customer->save();
    }
    


    Таблица результатов:
    Название теста Yii2 php5-fpm (сек.) Yii2 HHVM (сек.) Прирост по скорости
    1. Из коробки 0,10 0,09 1,1
    2. Вывод новостей 0,17 0,16 1,06
    3. Вывод 5к. товаров 1,51 1,12 1,34
    4. Подсчет товаров в категории 2,82 0,63 3,61
    5. Сериализация / Десериализация 3,24 0,88 3,68
    6. JSON (encode, decode) 2,73 0,51 5,35
    7. Redis (Model ActiveRecord) 10,53 4,43 2,37

    Ну, вот и все, результаты в цифрах и на лицо, в принципе достаточно не плохо, от HHVM остались только положительные впечатления, не надо никаких магических костылей и танцев, чтобы все это завести. Все что нужно в PHP и Yii прекрасно поддерживается. Думаю надо еще погонять на каких то небольших проектах. Посмотреть стабильность работы, не будет ли падать, если есть у кого-то опыт использования в продакшене было бы интересно послушать. Да, если у вас есть предложения по тестированию, пишите, попробуем прогнать. Всем Удачи!

    Немного ссылок.:
    Yii2
    HHVM
    Поделиться публикацией

    Похожие публикации

    Комментарии 27

      –3
      4. Стандартное решение:

      class ProductCategory extends CActiveRecord
      {
          ...
          public function relations()
          {
              return [
                  'productsCount'=>array(self::STAT, 'Product', 'id_category'),
                  
              ];
          }
          ...
      }
      

        +1
        во-первых здесь речь про yii2, а не про yii1
          0
          Сути не меняет, все равно нужно будет делать count($model->productsCount).
            +1
            Разве бывают случаи когда 500+ запросов к базе — нормально, что-то мне подсказывает, что стандартное решение содержит count() и group by…

            Небольшой оффтоп, есть ли красивое решение для yii с использованием группировки?
              –1
              Конечно запросик вроде:

              SELECT COUNT(1),category.id
              FROM `productCategory` as `category`, `product`
              WHERE category.id = product.categoryId
              GROUP BY product.categoryId
              

              Для этого случачая будет быстрее, тот же QueryBulder легко его построит, но к сожелению не всегда все задачи укладываються в один запрос, притом в тестах все-таки хотелось разной нагрузки.
          0
          разработчики говорят о совместимости в 99%

          Можно узнать, что осталось несовместимым в этом одном проценте?
            0
            Я решил погуглить «hhvm incompatibilities». Там какие-то ооочень специфические штуки. Например:
            Values of bit data type are returned as binary strings (e.g. "\0" or "\x1F") with libmysql and as decimal strings (e.g. «0» or «31») with mysqlnd. If you want the code to be compatible with both libraries then always return bit fields as numbers from MySQL with a query like this: SELECT bit + 0 FROM table.
              0
              Просто у кого-то одного по каким то причинам не завелось.
                +1
                samdark Сказал что сначала они указали 100% совместимость, но потом ему прилетело письмо от кого-то что возникли проблемы, поэтому они снизили до 99,99% :)
                Говорилось это на конференции в Воронеже (если я не ошибаюсь)
                0
                — Поддержка функционала php 5.6 кроме некоторых функций вроде eval, create_function.

                В Yii2 используется eval(). В expression dependency, в частности.
                  0
                  Спасибо за замечание, проверил сейчас eval и create_function в hhvm работают, видимо в последних версиях добавили поддержку. исправлю в статье!
                0
                у hhvm есть проблемы с serialize,unserialize — github.com/facebook/hhvm/issues/1620
                через 9 месяцев решили все же разобраться :)
                  +1
                  9% прирост… это стоило того да. Можно поиграться с opcache и думаю добиться того же прироста производительности. В целом же за счет того что в Yii1 (да и во втором) все построено на магических методах то оптимизации сильно не добиться так как для магических методов не работает JIT.
                    0
                    ну собственно вы, как человек скептически настроенный к yii, увидели то, что хотели увидеть — бенчмарк Hello world'а. Очень объективно.
                      +1
                      А тут больше и видеть то нечего, все остальное к Yii непосредственно имеет слабое отношение. Там где производительность поднялась больше чем на 10% был профит за счет оптимизаций связанных с обходом массивов, сериализацией и конструирование/разбор запросов/ответов в случае с reddis (в принципе работа со строками). В целом же сам фреймворк ускорился на каких-нибудь 10%, хотя справедливости ради стоит отметить что у Yii с оверхэдом всегда все было хорошо за счет очень простой архитектуры.

                      Меня больше напрягает что не приводятся данные по потреблению памяти, тестирование производилось судя по всему в один поток, никакого конкуренси… Между тем можно было бы оформить например в Docker контейнеры стэнды что бы люди могли позапускать на своем железе, возможно подправить чего (вдруг автор не сделал composer dump-autoload -o или еще чего), можно было бы поиграться с opcache… А так…
                        0
                        а тут уже дело в другом — тема поста должна быть «Тестируем HHVM на примере yii2», мне так кажется.
                          0
                          Стэндов для тестов все же не хватает… Делать свои лень.
                      0
                      Та ладно не так и много там магии. Всякие Yii::$app->cache, всегда имеют аналог Yii::$app->getCache(); Хотя конечно совсем без магии не обойтись.
                        0
                        Весь AR на магии построен. Бехейверы, почти все используют $app->cache вместо $app->getCache или IoC. А между тем взять ту же задачу обхода 5К элементов AR. Простенький бенчмарк даже такого рода показывает что для php5.5 скажем отказ от магических методов в пользу обычных геттеров/сеттеров составляет ~30% (если мы просто дергаем данные из атрибутов), а для HHVM аж 60% за счет умения оной инлайнить обращения к полям, что по производительности делает обход списка объекто лишь немного медленнее старых добрых массивов.
                          0
                          Кому не нужно удобство AR то можно просто использовать как масив, или вообще писать по старинке sql код вручную. AR без магии с доступами к свойствам раздует модель жутко, зачем оно надо. Я вообще сторонник того что если надо быстрей, то нечего себе мозг… просто возьмите какой-нибудь GO там и кода будете писать побольше, но и оптимизация будет на порядок выше.
                            0
                            GO там и кода будете писать побольше

                            Если говорить об ORM, вы видели GORP? Как по мне намного элегантнее всего что есть в PHP.

                            Мой довод был не за то что бы не пользоваться ORM, меня вполне себе они устраивают. Я использую Doctrine2 в повседневной работе а ORM жирнее в PHP нет.
                              0
                              На go библиотек много, но если глянуть в сторону github, то есть один негативный момент, активность в репозиториях мала, в следствие чего автор забивает на проект, по тому что у него нет фидбеков. Хотя сейчас с этих улучшается бесспорно. Но все-равно набор библиотек и их простота не сравнимы с тем что есть на php, python… По этому и всплывает расплата за производительность, которая выливается в большие временные затраты. Как говорится нужно просто выбрать инструмент под задачу, и не пытаться путем отсечения всего удобного, что есть в технологии выгрызть миллисекунды.

                              Хотя вашу точку зрения я понимаю, и понимаю что бывает разное. Но на такой случай как я писал ниже. Просто переходим на уровень проще. Их в yii 2 выходит аж 3 штуки, выбирай — не хочу.
                      0
                      А почему HHVM это экспериментальная виртуальная машина? По-моему она уже очень давно в продакшене и вполне успешно там используется.
                        0
                        Если убрать AR в тех местах где он не нужен то можно выиграть думаю больше. Если пользовать самый неудобный метод построения запросов — commandBuilder (или как он там), то будет прирост раз в 10 на запросах. Если QueryBuilder, то раз в 5. QueryBuilder в принципе можно не использовать, а вызывать просто asArray().

                        Хотя если вообще все выкидывать и экономить на спичках, то это уже не то. Проще тогда сразу брать какой-нибудь Go.
                          0
                          Экономия на спичках должна быть тогда, когда больше неначем экономить. Если у вас проект помещается на одном сервере при том что вы используете ORM то беспокоиться неочем, а оверхэд работы AR в Yii невилируется если с ним грамотно работать. Тяжелые операции или операции которые с AR делаются как-то нелогично естественно можно делать напрямую через querybuilder какой и т.д. AR в Yii дико простой, хотя возможно вкрутить если туда прокси классы какие можно было бы много чего оптимизировать… но думаю что профит не стоит подобны усложнений. Вот в Doctrine это да.

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