Ускорение парсинга INI-файлов для Zend Framework веб-приложений (в 4-5 раз)
В этой статье я расскажу, как можно увеличить скорость обработки запросов в вашем проекте на ZF с помощью уменьшения времени, необходимого на парсинг INI-файлов конфигурации.
Не секрет, что при создании большого приложения на Zend Framework большинство разработчиков используют для хранения настроек конфигурационные файлы. Некоторые любят XML-формат, другие модный YAML, я же возможно из-за своего админского прошлого предпочитаю INI-формат.
В Zend_Framework для парсинга INI файлов используется компонент Zend_Config_Ini. Но парсинг сам по себе вещь довольно накладная.

Как его ускорить? Можно использовать кэшер, и кэшировать результат парсинга. Но иногда такой возможности нет, т.к. в самом конфиге хранятся настройки системы кэширования. Какой есть ещё вариант?
Yaf Framework
Этот китайский проект очень заинтересовал меня в последнее время. Создатели Yaf (Yet Another Framework) поставили своей целью написать ZF на C. Устанавливается он просто:
1 |
pecl install yaf |
Потом вам надо будет включить расширение, добавив строчку:
1 |
extension=yaf.so |
в ваш php.ini (или отдельный yaf.ini файл). И перезапустить интерпретатор php(fpm или mod_php вместе с apache). Этот фреймворк содержит следующие классы:
- Yaf_Application
- Yaf_Dispatcher
- Yaf_Bootstrap_Abstract
- Yaf_Plugin_Abstract
- Yaf_Router
- Yaf_Route_Static
- Yaf_Route_Simple
- Yaf_Route_Supervar
- Yaf_Route_Rewrite
- Yaf_Route_Regex
- Yaf_Route_Map
- Yaf_Config_Ini
- Yaf_Config_Simple
- Yaf_Controller_Abstract
- Yaf_Action_Abstract
- Yaf_Request_Http
- Yaf_Request_Simple
- Yaf_Exception
- Yaf_Exception
- Yaf_View_Simple
- Yaf_Response_Abstract
- Yaf_Response_Simple
- Yaf_Response_Http
Как вы можете видеть, классы аналогичны ZF классам. В этой статье я рассмотрю использование Yaf_Config и его бенчмарки по сравнению с Zend_Config.
Zend_Config_Ini vs Yaf_Config_Ini
Для проведения тестовых испытаний возмём вот такой конфиг:
/cfg/application.config.ini
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
; Общие настройки для всех типов окружений [bootstrap] module_paths[] = APPLICATION_PATH'/module' module_paths[] = ROOT_PATH'/vendor' modules[] = 'Application' module_listener_options.config_cache_enabled = false module_listener_options.cache_dir = APPLICATION_PATH . '/data/cache' module_listener_options.application_environment = APPLICATION_ENV ; Настройки для авторизации из социальных сетей social.facebook.app_id = "456247645747" social.facebook.api_key = "trfyrthfghfghfgh" social.facebook.app_secret = "fghfghsytyty" social.vkontakte.app_id = "4564646" social.vkontakte.secret_key = "hfghdhgfd" social.main_url = "site.ru" social.twitter.app_id = "456456456" social.twitter.api_key = "fghfghfghrtyy" social.twitter.app_secret = "retertergfdhgfhfhgfhfg" resources.frontController.throwExceptions = false; Для перехватчика исключений и ErrorController'а = false ; Настройки проекта в окружении development [development : bootstrap] server.data_root = '/home/www/site.ru/data' ; Настройки подключения к базам данных для Doctrine resources.doctrine.connections.default.dsn = "mysqli://user:pass@localhost/db" resources.doctrine.connections.default.profiler = true resources.cachemanager.backend.memcached.servers.default.host = 127.0.0.1 ; Настройка php phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1 phpSettings.error_reporting = E_ALL & ~E_NOTICE phpSettings.html_errors = 1 ; Настройки проекта в окружении stage [production: bootstrap] server.data_root = '/home/www/site.ru/data' ; Настройки подключения к базам данных для Doctrine resources.doctrine.connections.default.dsn = "mysqli://user:pass@localhost/db" resources.doctrine.connections.default.profiler = false resources.cachemanager.backend.memcached.servers.default.host = 127.0.0.1 ; Настройка php phpSettings.display_startup_errors = 0 phpSettings.display_errors = 0 phpSettings.error_reporting = 0 |
В ./lib/Zend сохраним копию ZendFramework, Yaf у нас уже установлен. Поэтому напишем простенький тест:
zf.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php define('ROOT_PATH', '/home/andrey/sandbox/yaf.new/html/'); error_reporting(E_ALL); ini_set("display_errors", 1); ini_set("html_errors", 1); set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_PATH . DIRECTORY_SEPARATOR . "lib" ); require_once ('Zend/Config/Ini.php'); require_once ('Zend/Registry.php'); // Загружаем ZF $localConfigPath = '/cfg/local.config.ini'; $config = new Zend_Config_Ini(ROOT_PATH . '/cfg/application.config.ini'); $appConfig = $config->toArray(); Zend_Registry::set('appconfig', $appConfig); print 'Ok'; |
yaf.php
1 2 3 4 5 6 7 8 9 10 |
<?php define('ROOT_PATH', '/home/andrey/sandbox/yaf.ru/html/'); error_reporting(E_ALL); ini_set("display_errors", 1); ini_set("html_errors", 1); $config = new Yaf_Config_Ini(ROOT_PATH . '/cfg/application.config.ini'); $appConfig = $config->toArray(); Yaf_Registry::set('appConfig', 'appConfig'); print('ok'); |
Результаты тестирования
Ну что-же, теперь время сухих бенчмарков.
zf.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
andrey@z11:~/sandbox/yaf.new/html$ clear; ab -c 10 -n 1000 http://yaf.new/zf.php Server Software: nginx/0.7.67 Server Hostname: yaf.ru Server Port: 80 Document Path: /engine.php Document Length: 2 bytes Concurrency Level: 10 Time taken for tests: 1.187 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 160000 bytes HTML transferred: 2000 bytes Requests per second: 842.64 [#/sec] (mean) Time per request: 11.867 [ms] (mean) Time per request: 1.187 [ms] (mean, across all concurrent requests) Transfer rate: 131.66 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 0 Processing: 4 12 7.3 9 50 Waiting: 4 12 7.3 9 50 Total: 4 12 7.3 9 50 Percentage of the requests served within a certain time (ms) 50% 9 66% 12 75% 14 80% 16 90% 21 95% 27 98% 34 99% 40 100% 50 (longest request) |
yaf.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
andrey@z11:~/sandbox/yaf.new/html$ clear; ab -c 10 -n 1000 http://yaf.new/yaf.php Server Software: nginx/0.7.67 Server Hostname: yaf.new Server Port: 80 Document Path: /index.php Document Length: 2 bytes Concurrency Level: 10 Time taken for tests: 0.245 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 160000 bytes HTML transferred: 2000 bytes Requests per second: 4076.21 [#/sec] (mean) Time per request: 2.453 [ms] (mean) Time per request: 0.245 [ms] (mean, across all concurrent requests) Transfer rate: 636.91 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 0 Processing: 1 2 1.1 2 11 Waiting: 1 2 1.1 2 11 Total: 1 2 1.1 2 11 Percentage of the requests served within a certain time (ms) 50% 2 66% 2 75% 3 80% 3 90% 4 95% 5 98% 6 99% 7 100% 11 (longest request) |
Выводы
Скорость парсинга ZF 1.10.3: 842.64 запросов в секунду.
Скорость парсинга Yaf: 4076.21 запросов в секунду.
Ускорение: почти в 5 раз (4.83)!
К сожалению в Yaf не так много компонентов как в ZF, с поддержкой и сообществом там совсем туго, однако некоторые простые и часто-используемые компоненты (например Zend_Config_Ini и Zend_View) вполне можно заменить на их аналоги из Yaf, или сделать обертку.
Я бы не сказал, что Yaf – это Zend на C, потому что есть различия в API и достаточно серьёзные. Лучше относится к Yaf как к отдельному фрейморвку и разрабатывать проект с учётом его особенностей.
По поводу скорости разбора config файлов – не думаю, что имеет смысл оптимизировать эту задачу, так как разобранный config хранится в кеше и по сути эта операция должна выполнятся только один раз при изменении конфигурационного файла.
Ну по крайней мере, ближайший аналог Yaf это как раз таки ZF. Естественно в YAF нет ооочень многого, но думаю что за скелет его вполне можно взять, а потом уже на php добавлять “мяса”. По поводу разбора конфигов – да, это скорее синтетический тест. Но думаю в некоторых задачах такое решение может пригодиться.
Зачем вообще хранить конфиги в ini, если можно их хранить в виде подключаемого php файла, содержащего в себе define и $_GLOBALS[‘config’], при чём с APC их данные уже закэшированны в памяти.
Холиварить можно тут: http://zendframework.ru/forum/index.php?topic=1462.0
Думается мне что у каждого программиста свое “правильное” мнение на этот счет 😉