Pull to refresh

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

Reading time 3 min
Views 7.2K
Сделал я недавно некий тест, который выдал любопытные данные. Коими хочу поделиться, дабы помочь многим прояснит спорные ситуации.
Везде только и пишут, что насколько быстры массивы, насколько медлителен 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() выдуман и свойства выбраны приблизительно по смыслу.
Tags:
Hubs:
+4
Comments 27
Comments Comments 27

Articles