Оптимизация использования моделей от Active Record, и немного массивов

в 13:21, , рубрики: php, yii, yii framework, метки: , ,

Сделал я недавно некий тест, который выдал любопытные данные. Коими хочу поделиться, дабы помочь многим прояснит спорные ситуации.
Везде только и пишут, что насколько быстры массивы, насколько медлителен Active Record… Но когда видишь конкретные цифры гораздо легче понимать что на сколько лучше другого.

Используемые материалы:

— обычный ноутбук: Intel core 2 duo 2.13GHz, RAM 6 GB
— php 5.3
— apache 2.2
framework Yii 1.1.10
Active Record

Описание теста:

Существуюет класс —

class Address extends CActiveRecord { … }

Есть цикл из миллиона(1 000 000) итераций. На каждой итерации:

  1. Объявляется новый объект и заполнялся данными.
    $address = new Address();

  2. Используется паттерн singlton — объект объявляется один раз и на каждой итерации и перезаполняется.
    $address = Address::model();

  3. Объявляется и заполняется массив.
    $address = array();

  4. Простой stdClass().
    $address = new stdClass();

  5. Объявляем объект (как в варианте 1) но без инициализации всех сопутствующих данных AR
    $address = new Address(null);

Результаты (сек.):

  1. 28.490615844727
  2. 7.2354989051819
  3. 4.5744869709015
  4. 5.9930000305176
  5. 9.5185680294037

Выводы:

  • Конечно же для хранения и обработки данных можно использовать объекты, а не только массивы.
  • Конечно же классы можно наследовать от CActiveRecord, даже если не собираешься использовать классический подход Active Record – для этого не забываем добавлять null в конструктор (обращаю внимание — не false, не пустую строку, не 0):
    $address = new Address(null);

    и избегать записи:

    $address->attributes = $_POST['Address']

    т.к. при этом все равно происходит подключение мета данных AR и увеличиться время работы объекта.
    Делаем присвоение в ручную:

    
    $address->street = $_POST['Address']['street'];
    

  • Конечно же нужно использовать метод – model() – Осторожно! Это касается Yii < v.2
  • Не стоит упираться только в массивы – их тоже надо использовать с умом. По удобству они не идут практически ни в какое сравнение с объектами.
  • Самый главный вывод в том, что надо всегда (включать мозги) находить «золотую середину» между скоростью разработки продукта и скоростью работы продукта. К примеру, если в день нет хотя бы 50 000 посещений и нет выводов списков каких-то данных из базы на сайте, то про массивы вообще можно забыть и не вспоминать пока не появиться узкое место и не будет тормозов.

Хочется узнать, почему же класс, наследуемый от ActiveRecord тратит так много времени. В результате тестов, мы видим, что в основном время затрачивается на инициализацию/присвоение свойств/элементов, т.е. чем больше приходится использовать свойств/элементов тем дольше будет работать объект/массив. А поскольку ActiveRecord должен описывать в себе все поля таблицы (для этого существует классы CActiveRecordMetaData и CmysqlTableSchema и CmysqlColumnSchema у которых своих свойств дофига), то на это описание мета данных и тратиться основное время исполнения класса наследуемого от ActiveRecord.
Из этого можно сделать

еще Выводы:

  • Чем больше столбцов в таблице, которую будет поднимать ActiveRecord, тем дольше наш класс будет инициализироваться.
  • Обратите внимание, что это не зависит от того, сколько вы полей поставите в SELECT – мета данные в любом случае будут подниматься все (если я, конечно, не ошибаюсь). И на против, от количества полей в SELECT будет напрямую зависеть скорость работы DAO.

Пример тестового кода:


public function actionIndex() {
        $mk = microtime(true);
        for ($i = 0; $i < 1000000; $i++) {
            $this->test1();
        }
        echo microtime(true) - $mk, '< br/ >';
…

}

public function test1(){
        $address = new Address();
        $address ->zip = 3423423;
        $address ->state_ id  = 23332;
        $address ->house = 2234;
        $address ->street = 'asdfasdf';
        $address ->street_type = 'asdfasdfasdfsdf';
        $address ->address = 'asdfasdfsdf';
        $address ->code = 's';
        $address ->name = 'asdfasdfasf';
        $address ->latitude = 23.23232;
        $address ->longitude = 23.342342;
}

Остальное по аналогии.

PS

  1. Обращаю внимание, что этот тест затрагивает только php и никоем образом не зависит оптимизаций mysql или apache.
  2. Стоит учитывать то, что временные данные тестов это не идеальное время работы массива или объекта в идеальных условиях, но позволяют сравнить скорость работы относительно друг друга.

PPS

Класс Address() выдуман и свойства выбраны приблизительно по смыслу.

Автор: AstRonin

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


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