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