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

в 15:32, , рубрики: php, магические методы, Программирование, скорость работы, метки: , ,

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

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

Магия

В свое время узнав о «Магических» методах 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);
}
Подоспели результаты, которые слегка удивили

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

Это занимало (в секундах) ~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;
}
Вот и инфографика результаты

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

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

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

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;
    }
}

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

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

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

Выводы

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

Автор: aimodify

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js