Дослідженні швидкості виклику методу в php
Давно цікавило мене питання, якою є швидкість виклику методів класів та функцій в мові php. Насправді питання доволі важливе, так як може суттєво вплинути на проектування системи, в тому випадку коли важливою є швидкодія системи. Всі відомі мені сучасні системи CMS та framewrok'и об'єктно орієнтовані. Раніше не раз зустрічав інформацію, що виклик методу класу ресурсомістке завдання, навіть у порівнянні з викликом функції. Чи справді це так? Наскільки? Вирішив присвяти годину-дві вільного часу дослідженню цього питання
Що мене цікавить та як я отримував результати
По-перше, мене цікавить приблизний час виклику функції та методу класу. Як це оцінити? Виконати деякі дії поза функцією, та виміряти час виконання, та в середині функції. Як можна вимірювати час, я колись вже писав. Зрозуміло, що навіть на моїй скромній домашній системі швидкість одного виклику функції дуже незначна, тому для більш-менш зрозумілих результатів я виконую одну й ту саму дію 1 млн. разів. А з точки зору статистики, слід проводити цей тест декілька разів та знаходити матсподівання. Ну в цьому плані завдання собі я спростив і без усіляких оцінок та доведень прийняв, що середнє значення за 20 тестами не буде суттєво відрізнятись від матсподівання.
Ну досить вже сипати термінами цензурними тільки для людини з курсом математичної статистики за плечима. Краще розповім, що ще я хотів оцінити. Також мене цікавило порівняння швидкості виклику метода порівняно з функцією. Ну з цим аналогічно: одні й ті ж дії у методі класу та функції та заміри часу.
Доволі багато цікавих досліджень можна провести з самими класами. Так я вирішив порівняти швидкість виклику метода для статичного метода, звичайного метода, та метода, що звертається до статичних даних з метода класу. І на додачу спробував оцінити швидкість доступу до глобальних змінних (створений об'єкт класу додається в $GLOBALS, а на кожному кроці виклик виконується з $GLOBALS) та швидкість створення нового об'єкту.
На останньому зупинюсь трохи детальніше. Кожних 100 викликів методу класу, я створюю новий екземпляр і працюю вже з ним. Для того щоб позбутись помилок спричинених необхідністю рахувати кількість ітерацій, що саме по собі займає доволі багато часу, я додав аналогічний тест, тільки без створення іншого екземпляру (продовжуємо працювати зі старим екземпляром тільки з використанням вкладеного циклу).
Що я отримав на практиці
Спочатку наведу код класу, який я використовував для тестування:
class Test {
private static $a;
private $b;
public static function a() {
self::$a++;
}
public function b() {
$this->b++;
}
public function c() {
self::$a++;
}
public static function d() {
$GLOBALS['obj']->c();
}
public static function staticInit() {
self::$a = 0;
}
public function init() {
$this->b = 0;
}
public function printAll() {
echo 'Results '.self::$a.' '.$this->b."\n";
}
}
А тест виглядає для функції виглядає таким чином:
include 'class.php';
// Some function
function inc() {
$i = 0;
}
// Test method
$average = 0;
$e = microtime(true);
for ($t = 0; $t < TIMES; $t++) {
$b = $e;
for ($i = 0; $i < 1000000; $i++)
inc();
$e = microtime(true);
echo 'Function call time: '.($e - $b)." s. \n";
$average += ($e-$b)/TIMES;
}
echo 'Average time for '.TIMES.' calls: '.$average.' s'."\n\n";
Всі інші тести виглядають аналогічно, тому не бачу сенсу наводити їх всі. Знайти їх можна в архіві. Краще перейду до результатів. Відсортую за швидкістю виконання:
1. Без функції (0.3957).
2. Функція (1.1538).
3. Звичайний метод (1.2106).
4. Вкладений цикл (1.2524).
5. Новий екземпляр на кожні 100 викликів (1.2562).
6. Виклик методу, що звертається до static змінних всередені класу (1.4349).
7. Виклик методу глобальної змінної (1.5755).
8. Виклик статичного методу (1.5893).
(В дужках вказано середній час в секундах.)
Зрозуміло, що найкращий варіант, коли функція зовсім не викликається. :-) Виклик звичайної функції займає 60 відсотків виміряного часу. Тут нічого нового і особливого я для себе не знайшов. Навіть цифра не налякала.
А от те, що виклик функції займає максимум на 10% відсотків (а за результатом тесту відсотків на 5) більше часу ніж виклик методу класу мене відверто кажучи змусило зрадіти. Якщо вірити різним тестам кількарічної давнини, то результати були гірші.
Що здивувало, так це низька швидкість виклику static-методів та низька швидкість доступу до статичних полів класу. Я відверто кажучи думав, що результат буде іншим. Дешевше виходить створювати нові екземпляри класу та викликати метод ніж викликати статичний метод (якщо для кожного екземпляру можна зробити виклик декілька разів).
Прикрим є результат швидкості доступу до змінних в $GLOBALS. Знову ж деяких випадках значно дешевше створити новий екземпляр класу.
Деякі висновки
Не вірю в те, що наведені мною результати змінять чийсь підхід до написання коду на php. Я й сам лише приймаю ці результати до уваги, бо як не крути а зручність написання, подальшого використання та модифікації коду, звички та купа вже написаного коду візьмуть своє. До того ж революційних та якихось особливих результатів тести не показали.
Якщо розбиратись глибше, то для об'єктивної оцінки необхідно відкинути час, що займають додаткові обчислення та дії: лічильники ті їх перевірка. Тоді результати будуть чистішими і вплив чинників можна буде оцінити краще. З іншого боку, в будь-якому проекті додаткових обчислень значно більше. Так при роботі з БД кожний запит займає десятки мілісекунд (а бувають і такі, що грають і по декілька секунд), тоді як сам виклик методу займає близько одної тисячної мілісекунди. В такому співвідношенні оптимізація викликів методів є не надто розумним рішенням, якщо їх не сотні тисяч чи вже більше нічого оптимізувати.
Цікаво, а як виглядають подібні результати для інших мов?
Коментарі:
В любом случае следует использовать объекты и методы, если это облегчит понимание и использование системы, какой бы плохой не была скоростная характеристика. Если важна скорость, обычно доплачивают и используют прекомпилированный PHP код(акселераторы типа Zend optimizer, Turck MMCache), который выполняется во много раз быстрее и даже очень большая разница во времени вызова становится незначительной.
В любом случае следует использовать объекты и методы, если это облегчит понимание и использование системы, какой бы плохой не была скоростная характеристика. Если важна скорость, обычно доплачивают и используют прекомпилированный PHP код(акселераторы типа Zend optimizer, Turck MMCache), который выполняется во много раз быстрее и даже очень большая разница во времени вызова становится незначительной.
Вибачте, а Ви не маєте матеріалів, що до показників наскільки компілятори-оптимізатори підвищують швидкодію коду?



