Курівництво, Набли, 49
Більшість традиційних мов Web-програмування (Perl, Ruby, Python тощо) підтримують написання скриптів, що працюють у так званому "FastCGI-режимі". Понад те, Ruby on Rails, наприклад, взагалі неможливо використовувати у CGI-режимі, т.к. він витрачає підключення всіх своїх численних бібліотек десятки секунд.
Нижче я розповім про метод, який дозволяє у ряді випадків прискорити завантаження об'ємного PHP-коду більш ніж у 20 разів, не отримуючи при цьому побічних ефектів та значних незручностей. Але спочатку поговоримо про основи.
Що таке FastCGI?
| один з лідерів за швидкістю трансляції), однак, якщо бібліотеки, що включаються, "важать" кілька мегабайтів, трансляція затягується. |
Існує ряд інструментів, що кешують в оперативній пам'яті (shared memory), що розділяється, відтрансльоване внутрішнє уявлення PHP-коду. Таким чином, при повторному включенні PHP-файлу він уже не транслюється, а байт-код береться з кешу у пам'яті. Звичайно, це значно прискорює роботу.
Одним з таких інструментів є eAccelerator. Він встановлюється у вигляді розширення PHP (підключається до php.ini) і вимагає найменшої настройки. Рекомендую включити в ньому режим кешування байт-коду виключно в оперативній пам'яті та вимкнути стиск (установити параметри та ). Також встановіть і налаштуйте контрольну панель, що йде в дистрибутиві eAccelerator, щоб в реальному часі відстежувати стан кешу. Без контрольної панелі вам буде дуже важко проводити діагностику, якщо eAccelerator з якихось причин не почне працювати.
Перевага eAccelerator у тому, що він працює дуже стабільно і швидко навіть на великих обсягах PHP-коду. У мене жодного разу не виникало проблем із цим інструментом(На відміну від Zend Accelerator, наприклад).
"Мій скрипт разом із бібліотеками займає 5 МБ, як же бути."
Думаєте, 5 МБ це занадто багато для PHP? Нічого подібного. Спробуйте скористатися такими системами, як Zend Framework та Propel, щоб переконатися у зворотному. Zend Framework повністю займає близько 5 МБ. Класи, згенеровані Propel-ом, також дуже об'ємні і можуть відібрати ще кілька мегабайтів.
Багато хто на цьому місці посміється і скаже, що не треба використовувати Zend Framework і Propel, т.к. вони "гальмівні". Але реальність полягає в тому, що гальмівні зовсім навіть не вони. Погана продуктивність має метод, який за замовчуванням використовує PHP для завантаження коду. На щастя, ситуацію неважко виправити.
Щоб не бути голослівним, я наведу результати невеликого тестування, яке спеціально провів у "чистому" оточенні, не прив'язаному до якогось конкретного проекту. Тестували швидкість підключення всіх файлів Zend Framework (за винятком Zend_Search_Lucene). Попередньо з коду були вирізані всі виклики, а завантаження залежностей вироблялося лише через механізм autoload.
Отже, всього підключалися790 PHP-файлів загальним обсягом 4.9 МБ. Чимало, правда? Підключення здійснювалося приблизно так:
Лістинг 3: Підключення всіх 790 файлів ZF (4.9 МБ)Завдяки тому, що використовується autoload, дзвінок змушує PHP підключити файл відповідного класу. (Це найпростіший спосіб "смикнути" autoload-функцію.) Порядок підключення я вибрав таким, щоб кожен наступний клас вже мав завантажені всі свої залежні класи на момент запуску. (Цей порядок неважко встановити просто друкуючи в браузер значення $fname у функції __autoload).
Ось результати тестування зeAccelerator-ом і без на моєму не дуже потужному ноутбуці (Apache, mod_php):
- Підключення всіх файлів по одному, eAccelerator вимкнено:911 мс.
- Підключення всіх файлів по одному, eAccelerator включений:435 мс. Зайнято 15 М кеш-пам'яті під байт-код.
Як бачите, eAccelerator дає приблизно дворазове прискорення на 790 файлах загальним обсягом 4.9 МБ. Слабкувато. До того ж, 435 явно надто для скрипту, який тільки й робить, що підключає бібліотеки.
А тепер додамо стероїдів
Ходять чутки, що PHP набагато швидше завантажує один великий файл, ніж десять маленьких того самого сумарного обсягу. Я вирішив перевірити це твердження, поєднавши весь Zend Framework в один файл розміром 4.9 МБ і підключивши його єдиним викликом. Погляньмо, що вийшло.
- Увімкнення одного великого злитого файлу, eAccelerator вимкнено:458 мс.
- Увімкнення одного великого злитого файлу, eAcceleratorвключено:42 мс. Зайнято 31 МБ кеш-пам'яті під байт-код.
Перший рядок говорить про те, що один великий файл розміром 4.9 МБ і справді завантажується швидше, ніж 790 маленьких: 458 мс проти 911 мс (див. вище). Економія у 2 рази.
А ось другий рядок змусив мене від здивування підстрибнути на стільці і перевіряти ще раз результат кілька разів. Сподіваюся, це станеться і з вами. Справді, 42 це в 11 разів швидше, ніж з відключеним eAccelerator-ом! Виходить, що eAccelerator ще менше любить дрібні файли (до речі, навіть у режимі): економія в 11 разів.
Отже, ми дійсно отримали прискорення завантаження у 22 рази, як і було обіцяно у заголовку. Раніше весь Zend Framework, розбитий на файли, підключався 911 мс, а з використанням eAccelerator і об'єднання всіх файлів в 42мс. І це, зауважте, не на реальному сервері, а лише на рядовому ноутбуці.
Висновок: прискорення у 22 рази
Підведемо підсумки.
- Злиття всіх файлів в один великий плюс включення eAccelerator для цього великого файлу дає прискорення в 22 рази за обсягом коду 4.9 МБ і числа файлів 790.
- У разі невеликої кількості файлів значного обсягу eAccelerator може дати 10-кратне прискорення. Якщо файлів багато, а сумарний обсяг великий, прискорення приблизно в 2 рази.
- Витрата кеш-пам'яті залежить від кількості файлів розбиття: при фіксованому обсязі чим більше файлів, тим витрата менше.
При цьому eAccelerator позбавлений основних проблем "справжнього" FastCGI-сервера. Скрипти позбавлені витоків пам'яті і запускаються в гарантовано "чистому" оточенні. Вам також не потрібно стежити за зміною коду і перезапускати сервер щоразу, коли внесені модифікації в більш-менш глибинний код системи.
Зауважте також, що ми підключаливесьZend Framework. У реальних скриптах обсяг коду буде значно меншим, т.к. Зазвичай до роботи потрібно лише незначна частина ZF. Але навіть за умови, що бібліотеки займають 4.9 МБ, ми отримуємо час завантаження 42 цілком прийнятний для PHP-скрипту. Адже в навантажених проектах PHP-скрипти можуть працювати і кілька сотень мілісекунд (Facebook, Мій Круг і т.д.).
| втомлююча вправа. Краще написати утиліту, яка буде автоматично аналізувати список PHP-файлів, підключених скриптом, а при наступному об'єднувати ці файли і записувати в тимчасову директорію (якщо це ще не зроблено), потім підключати по . Сьогодні я залишаю написання такої утиліти (плюс-мінус деталі) на совісті читача. |
Також рекомендую вам відмовитись від явного включенняфайлів і переходити на autoload. Тільки не намагайтеся використовувати для цього Zend_Loader: він дуже повільний (за моїми вимірами, підключення ста файлів забере додатково близько 50 мс). Краще напишіть власну нескладну autoload-функцію, яка швидко виконуватиме всю роботу. Autoload дозволить вам безпечно поєднувати кілька файлів бібліотек в один, не думаючи про те, як боротися з "ручними" require_once.
Нарешті, застосовуйте функцію set_include_path(), щоб код підключення бібліотек виглядав так:
Константи, що визначають шлях до директорії бібліотек явним чином, велике зло і ускладнення програми. Вони також побічно суперечать стандартам кодування Zend Framework і PEAR, яких я також рекомендую якомога дотримуватися.
Отже, хочете використовувати "важкі" бібліотеки в PHP-на здоров'я! скриптова мова, що по-справжньому дозволяє це робити без огляду на незручності FastCGI і проблеми "вбудованих" серверів.