Хороши ли __call или «Скорость Магии»

от автора

Для кого написана эта статья

Автор не будет пытаться научить Хабраюзера всяческим штучкам. Равно как и показать что-то новое, старое и убедить кого-то что-то использовать. Поводом для написания послужили вынужденные оптимизации скорости работы медленного кода. Оказалось, что медленно он работает именно из-за «Магических» методов в моем контексте. Поискав, автор не нашел четкой информации, как медленно работают магические методы — все, что нашел автор просто «медленнее», поэтому пришлось получать результаты самостоятельно. Так посмотрим же на все это с точки зрения сухой статистики!

Магия

В свое время узнав о «Магических» методах php отсюда а также из ряда других источников, автору это все понравилось, ибо нет ничего лучше, чем интеллектуальный код, который в сочитании с кешем умеет сам понимать, находить, подтягивать и отдавать то, чего от него просят.

Ихнее все

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

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

Тестим

Автор не желает перегружать читателя массами кода и всякой всячиной, поэтому в свободное время написал мини-кусочки специально для наглядности.

Вот они два простейших класса, не претендующих на оригинальность:

Обычный себе класс

class A{     public $a;     public function foo($arg) {         return $arg;     } } 

Класс с магическим метидом

class B{     public $params;     public function __call($fName, $args) {         return $args[0];     }     public function __set($name, $value) {         $params[$name] = $value;     } } 

В этих самых классах методы специально сделаны очень простыми — они только возвращают то, что принимают.

Вот и тесты кусочки кода, которые вызывают методы по 1000000 10^6 раз:

__call

Тестируем на прием-отдачу обычный класс А

$a = new A(); for ($i = 0; $i < 1000000; $i++) {     $a->foo($i); } 

Тестируем на прием-отдачу обычный класс B с магической составляющей

$b = new B(); for ($i = 0; $i < 1000000; $i++) {     $b->foo($i); } 
Подоспели результаты, которые слегка удивили

Это занимало (в секундах) ~0.20 для A против ~0.63 для B

Теперь попробуем __set

Заметим, что дополнительные проверки не вводились специально для минимизации погрешностей.

Для А

$a = new A(); for ($i = 0; $i < 1000000; $i++) {     $a->a = 1; } 
И для B

$b = new B(); for ($i = 0; $i < 1000000; $i++) {     $b->b = 1; } 
Вот и инфографика результаты

Немножко модифицируем исходные классы

Т.е. просто совместим первый и второй тесты

class A{     public $a;     public function foo($arg) {         $this->a = $arg;         return $arg;     } } class B{     public $params;     public function __call($fName, $args) {         $this->b = $args[0];         return $args[0];     }     public function __set($name, $value) {         $params[$name] = $value;     } } 

И выполним тесты из первого примера — вызов метода.

Результаты говорят сами за себя

Выводы

Автор, порефакторив код, написав нужные методы, все-же оставил в ряде мест эту магию, но впредь будет вести себя значительно осторожнее с заклинаниями такими методами, т.к. известно, что Любая палка о двух концах. Спасибо за внимание!

ссылка на оригинал статьи http://habrahabr.ru/post/182836/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *