Сделал я недавно некий тест, который выдал любопытные данные. Коими хочу поделиться, дабы помочь многим прояснит спорные ситуации.
Везде только и пишут, что насколько быстры массивы, насколько медлителен Active Record… Но когда видишь конкретные цифры гораздо легче понимать что на сколько лучше другого.
— обычный ноутбук: Intel core 2 duo 2.13GHz, RAM 6 GB
— php 5.3
— apache 2.2
— framework Yii 1.1.10
— Active Record
Существуюет класс —
Есть цикл из миллиона(1 000 000) итераций. На каждой итерации:
Хочется узнать, почему же класс, наследуемый от ActiveRecord тратит так много времени. В результате тестов, мы видим, что в основном время затрачивается на инициализацию/присвоение свойств/элементов, т.е. чем больше приходится использовать свойств/элементов тем дольше будет работать объект/массив. А поскольку ActiveRecord должен описывать в себе все поля таблицы (для этого существует классы CActiveRecordMetaData и CmysqlTableSchema и CmysqlColumnSchema у которых своих свойств дофига), то на это описание мета данных и тратиться основное время исполнения класса наследуемого от ActiveRecord.
Из этого можно сделать
Остальное по аналогии.
Класс Address() выдуман и свойства выбраны приблизительно по смыслу.
Везде только и пишут, что насколько быстры массивы, насколько медлителен 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) итераций. На каждой итерации:
- Объявляется новый объект и заполнялся данными.
$address = new Address();
- Используется паттерн singlton — объект объявляется один раз и на каждой итерации и перезаполняется.
$address = Address::model();
- Объявляется и заполняется массив.
$address = array();
- Простой stdClass().
$address = new stdClass();
- Объявляем объект (как в варианте 1) но без инициализации всех сопутствующих данных AR
$address = new Address(null);
Результаты (сек.):
- 28.490615844727
- 7.2354989051819
- 4.5744869709015
- 5.9930000305176
- 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
- Обращаю внимание, что этот тест затрагивает только php и никоем образом не зависит оптимизаций mysql или apache.
- Стоит учитывать то, что временные данные тестов это не идеальное время работы массива или объекта в идеальных условиях, но позволяют сравнить скорость работы относительно друг друга.
PPS
Класс Address() выдуман и свойства выбраны приблизительно по смыслу.