framework.zend.com
Stable релиз 2.0 / 1.12

Сборка Zend Framework в один файл, как способ повышения производительности

К комментариям

В процессе подготовки доклада о производительности для ZFConf 2010, я проверял распространенное мнение, что сборка Zend Framework в один файл существенно повышает скорость работы приложения, которое на нем базируется. "Классическим" трудом на эту тему является вот эта статья - http://dklab.ru/chicken/nablas/49.html уважаемого Дмитрия Котерова. Удивительно, но я получил другие результаты, скорость работы моего приложения не только не увеличилась, но и немного замедлилась. Я сам очень удивился, но времени тщательно все проверить у меня не было. На выступлении я рассказал об этой ситуации, многих это удивило и вызвало недовольство, упоминали ту же самую статью Котерова и говорили что так не может быть. Я пообещал перепроверить и выложить результаты на сайте сообщества. Быстро это сделать не получилось, но лучше поздно чем никогда.

Я решил пройти весь процесс заново и посмотреть, что получится во второй раз.
Еще раз поставлю задачу.
Необходимо сравнить производительность приложения на Zend Framework, когда ZF поставляется в виде библиотеки с набором файлов, и в виде одного большого файла (сборки) в который "слиты" все классы ZF.

Для тестирования используется демо сайт:

  • Сайт содержащий в себе функции среднестатистического, не очень большого сайта типа портала:
  • ZF ~ 160 файлов
  • Приложение ~ 25 файлов
  • Zend_Application, Zend_Loader, Zend_Controller, Zend_View, Zend_Layout, Zend_Filter, Zend_Form, Zend_Db, Zend_Captcha, Zend_Session

Протестировал на двух серверах, первый:

  • Intel(R) Core(TM)2 Duo CPU E7200  @ 2.53GHz
  • 2 GB RAM
  • Linux Debian lenny 8
  • Apache 2.0
  • PHP 5.2.6 mod_php + Suhosin Patch 0.9.6.2
  • Mysql 5.0.51a
  • Zend Framework 1.10.4

второй:

  • Intel(R) Celeron(R) CPU 2.53GHzIntel(R) Celeron(R) CPU 2.53GHz
  • 400 MB RAM
  • Linux Debian lenny 4
  • Apache 2.0
  • PHP 5.2.6 mod_php + Suhosin Patch 0.9.6.2
  • Mysql 5.0.51a

Что замерял:

  • Количество запросов в секунду
    Apache Benchmark
    - ab -c 50 -n 100
  • Время выполнения скрипта (сек)
    - PHP функция microtime
  • Память выделяемая для скрипта (мб)
    - PHP функция memory_get_peak_usage

Для создания сборки я воспользовался сервисом http://isalmin.ru/zfp/. Спасибо создателям, сервис работает довольно качественно. У меня возникла только одна ошибка: Fatal error: Uncaught exception 'Zend_Application_Exception' with message 'Bootstrap class not found'. Я дописал следующую строчку require_once $path; в нужном месте и ошибка исчезла. Сборку я создавал не всего фреймворка, а только тех классов что реально используются в моем приложении. В этом существенное отличие от того что сделал Дмитрий Котеров, как я понял он тестировал на хелло ворлд, в который подключил полностью весь фреймворк. Весь фреймворк весит ~ 5mb, моя же сборка занимает ~ 600kb. Думаю даже очень большой сайт с трудом выйдет за 2-3 мегабайта кода ZF.

Параметры eAccelerator

  • eaccelerator.shm_size=0 (32 Mb)
  • eaccelerator.check_mtime=0
  • eaccelerator.shm_only=1
  • eaccelerator.compress=0

Рекомендую пользоваться веб панелью control.php, которая поставляется вместе с eAccelerator, таким образом можно удобно следить за количеством занятой и свободной памяти, отключаться кешер и оптимайзер, менять параметр check_mtime.

Результаты

Первый сервер, eAccelerator отключен

Server 1 eAccelerator off
файлами сборкой
время (cек) память (mb) req/s время (cек) память (mb) req/s
0.083525896 11.04298401 19.61 0.063402176 11.93631744 24.08
0.0844872   19.78 0.070171118   24.2
0.083405018   19.33 0.069756985   24.06
0.083350897   20.44 0.069437981   24.22
0.086952925   20.91 0.069513083   24.31
0.083252907   20.48 0.069675922   24.51
0.083763123   20.43 0.069641113   23.66
0.083220959   20.37 0.070962191   23.83
0.083575964   20.94 0.069312811   24.09
0.083059072   20.69 0.06942296   24.66
0.083859396   20.298 0.069129634   24.162
           
Время работы php скрипта echo 'hello';      
0.0000069          

Первый сервер, eAccelerator включен:

Server 1 eAccelerator on
файлами сборкой
время (cек) память (mb) req/s время (cек) память (mb) req/s
0.02612114 3.33895874 55.92 0.018589973 3.720977783 66.15
0.025952101   57.8 0.018706799   68.84
0.026070833   56.78 0.018576145   66.42
0.026207924   55.29 0.018529177   67.56
0.025972128   55.19 0.018697977   68
0.026178122   59.42 0.018503904   68.32
0.026070118   55.11 0.018584013   69.49
0.025858879   56.78 0.018531084   68.66
0.02649498   57.53 0.018540144   68.14
0.026036978   57.13 0.018594027   67.95
0.02609632   56.695 0.018585324   67.953

Второй сервер, eAccelerator выключен

Server 2 eAccelerator off
файлами сборкой
время (cек) память (mb) req/s время (cек) память (mb) req/s
0.329728127 6.870616913 3.6 0.202265978 7.079814911 4.58
0.275671005   3.01 0.194590806   4.66
0.302878141   3.56 0.20475316   4.69
0.264563084     0.210213184    
0.334601164     0.202888012    
0.304044008     0.202977896    
0.265103817     0.199849844    
0.286854982     0.219155073    
0.279711008     0.19554615    
0.260824919     0.19554615    
0.290398026   3.39 0.202778625   4.643333
           
           
Время работы php скрипта echo 'hello';      
0.00004          

Второй сервер, eAccelerator включен

Server 2 eAccelerator on
файлами сборкой
время (cек) память (mb) req/s время (cек) память (mb) req/s
0.100346088 1.899772644 8.61 0.065990925 2.054985046 13.43
0.096465111   9.02 0.064777851   13.38
0.097507   8.78 0.064805031   13.19
0.104003906     0.070662975    
0.097606897     0.070662975    
0.116338968     0.06330204    
0.098941803     0.063395023    
0.105087042     0.065068007    
0.098057032     0.064059973    
0.097778082     0.065031052    
0.101213193   8.803333 0.065775585   13.33333

Видим что скорость при использовании сборки увеличивается примерно в полтора раза (при этом увеличивается потребление памяти). Что противоречит тому что я получил перед своим докладом. Скорее всего, так произошло потому что сборка, которую я использовал в прошлый раз была избыточной, то есть содержала достаточно много файлов которые не использовались. Но тем не менее цифр о 22 кратном увеличении как у Дмитрия я тоже не получил. Возможно это потому что я не подключал целый фреймворк, а лишь его часть.

Выводы:

  • Сборку ZF в один файл можно рассматривать как способ повышения производительности;
  • Сборка должна содержать по минимуму избыточных файлов, в идеале только те которые нужны для запроса конкретного url;
  • На реальных больших проектах возможны различные подводные камни и профит от увеличения скорости может не стоить того

Здесь я выложил сайт, который подвергал тестам (на чистоту кода прошу не обращать внимания), в архив также входит упакованный Zend Framework (ZF.php) для этого сайта.
Все кто имеет желание могут провести тесты на своих серверах, также призываю поделиться результатами собственных экспериментов на эту тему, в идеале на реальных боевых проектах.

Лучший способ следить за обновлениями сайта это подписаться на RSS
Если информация была полезной для вас, вы можете поддержать сайт.
Комментарии:
IgorN 11.06.2010 10:50 #
Самое интерессное,  что очень хороший прирост при сборе зф в один файл я заметил только на версии зф 1.5, когда слил, на версии 1.7.х  прирост был но не таким уж большим 10-20%, недавно пробовал на 1.10.х особого прироста не заметил особенно под убунтой с файловой системой ext4 может прирост был но и до 10% не дотягивал.

Конечно если важен, каждый процент то сливать следует однозначна. (думаю актуально для высоконагруженных систем)
Ответить
ZedroXyMur 11.06.2010 15:41 #
Забавненько :)
спасибо за проделанную работу.
От себя могу лишь добавить, что "овчинка выделки не стоит". Ибо появляется очень много коллизий (с повторными инклудами и автолоадером)на масштабных проектах. Т.е. может конечно и есть смысл использовать такой подход, но только на маленьких проектах... но по логике вещей, какой смысл оптимизировать малые проекты с аудиторией до 5-10к (зависит от нагрузки)?..
Мое мнение ИМХО - включайте акселераторы, кэшируте все что можно кешировать, оптимизируйте окружение и БД. Именно эти подходы могут дать прирост в производительности и в 22 и в 222 раза :)
Ответить
Ololo 12.06.2010 19:00 #
Я конечно ab не тестил, но после слияния части 1.10 в один файл (размер 350 кБ) в профайлере PhpEd время генерации страницы снизилось со 137 мс до 37 мс. Правда там было тупо Hello World. Поищу дома, попробую ab. Кстати, это было на Винде, и без eaccelerator на обычном денвере.

Сборку делал сам, поставив логгер в функцию autoload. Потом вручную убирал все require_once кроме Exceptions.
Ответить
freeneutron 17.12.2010 07:28 #
Бессмысленно проводить такие сравнения, пока используется внешняя (которая работает, как независящая от PHP служба) база данных. По моим оценкам, время обращения к внешней базе на порядок больше времени обращения к файлу. И если вы хотите измерить прирост производительности за счет объединения файлов, то вам нужно сперва избавиться от более медленных факторов. Уверен, что более ощутимый результат вы получите с использование SQLite.
Ответить
apostol 04.02.2011 14:48 #
Сервис по сборке не работает (пересылает на какой-то подиум).
Ответить
Александр Махомет 04.02.2011 20:57 #
Значит нет больше сервиса. Печально.
Ответить
Сергей 19.03.2011 05:02 #
Мне подобная схема помогла, время уменьшилось в 10 раз. Приложение стояло на VirtualBox с Ubuntu и APC на ноуте. Видимо, количество файлов было слишком велико, чтобы 100% в кэш попадать даже на одинаковых запросах (с одинаковым набором классов).
Насчёт оптимизации БД согласен, но лишь частично. Скорее всего, оптимизировать БД = менять структуру таблиц, добавлять кеширование, ещё индексы и т.п.. По-хорошему, на странице, даже если 20 запросов к БД (что в принципе немало, обычно меньше), да хоть 100, всё равно классов получается порядка 150, или даже ещё больше. То есть это 150 операций чтения и парсинга разных файлов. Кэш, конечно, спасает, но не на полную катушку - память общая, а нам не только файлы и опкод кэшировать надо, а с одним большим файлом это 1 большое чтение и 1 разбор большого файла. Фрагментация меньше.
Подводные камни встречаются ровно до тех пор, пока:
- не построится правильное дерево зависимостей (на примерах, что я юзал, классы в файл складывались в порядке подключения, то есть наследник шёл перед предком, в итоге Fatal Error)
- не уберутся к едреням все require_once перед использованием класса (в ZF и так очевидно в каком файле будет лежать автозагружаемый класс)
Ответить
Bob 25.09.2011 23:25 #
Добрый день

попробовал, склеить файл удалось в один,

но вопросик-то появился сам собой !!!!!!

я например хочу только библиотеку Zend (./library/Zend) в один файл склеить
и хочу оставить - как есть по умолчанию, все другие файлы такие как

myControllers.php
myModels.php

которые не относятся к библиотеке,


возможно будет интересное решение моя версия ZF 1.11.10

Ответить
Павел 04.11.2011 23:09 #
А в каком именно месте вы добавили строчку "require_once $path;"? У меня возникает такая же ошибка, но налету место куда следует вставлять эту строку я найти не могу
Ответить
Dune 06.03.2012 13:36 #
В полученной сборке ищем 'Bootstrap class not found' и перед условием, которое пустило до этого исключения, вставляем эту строчку.
Ответить
Уважаемые пользователи. Комментарии не для того чтобы:
  1. Спрашивать почему у вас не работает код, для этого есть тема форума закрепленная за статьей.
  2. Спрашивать как реализовать ту или иную функциональность, для этого необходимо создать свою тему на форуме.

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

Комментарии имеют древовидную структуру.
Если вы хотите ответить на определенный комментарий - нажмите на ссылку "Ответить" возле этого комментария.

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