Пост навеян статьей Сколько памяти потребляют объекты в PHP…, размышлениями над самописной ORM и книгой Мэтта Зандстра «PHP. Объекты, шаблоны и методики программирования» (ISBN 978-5-8459-1689-1).
Мэтт в главе «Шаблоны баз данных» пишет о том, что если нужно создать несколько тысяч объектов из базы, то для экономии памяти, нужно решать эту задачу не «в лоб», а генерировать объекты по требованию, используя интерфейс Iterator.
Первая мысль была: «Если мы достали 5000 записей из базы, значит мы хотим все их как-то обработать, и какая разница. сразу будут созданы объекты или по требованию?», но потом понял — если вся работа с каждым объектом происходит внутри цикла foreach или while( next() ), то создание объекта по требованию и автоматическое его уничтожение на следующем витке цикла даст существенную экономию памяти. На деле оказалось — очень существенную.
На чем тестировал: nginx + fast-cgi PHP 5.4.10 + APC.
Код:
Мэтт в главе «Шаблоны баз данных» пишет о том, что если нужно создать несколько тысяч объектов из базы, то для экономии памяти, нужно решать эту задачу не «в лоб», а генерировать объекты по требованию, используя интерфейс Iterator.
Первая мысль была: «Если мы достали 5000 записей из базы, значит мы хотим все их как-то обработать, и какая разница. сразу будут созданы объекты или по требованию?», но потом понял — если вся работа с каждым объектом происходит внутри цикла foreach или while( next() ), то создание объекта по требованию и автоматическое его уничтожение на следующем витке цикла даст существенную экономию памяти. На деле оказалось — очень существенную.
На чем тестировал: nginx + fast-cgi PHP 5.4.10 + APC.
Код:
// тестовый класс модели Class Model { public $data = ''; public function __construct($data) { $this->data = $data; } public function process() { $this->data = strtoupper($this->data); } } // тестовые данные $data = str_repeat("jhsdfweurhjk234n", 500); $start = microtime(1); $collection = new Collection; // добавляем в коллекцию 5000 записей for ( $i=0; $i<5000; ++$i ) { $collection->add($data); } // что-то делаем со всеми объектами коллекции foreach( $collection as $item ) { $item->process(); } $end = microtime(1); echo "Time: ". round( ($end-$start)*1000 , 2) . " ms <br>"; echo "Memory: ". round(memory_get_usage()/1024 , 2) . " kb <br>";
Класс Collection реализован был в двух вариантах:
в первом, объекты создаются сразу при добавлении данных в коллекцию
// коллекция объектов, реализует интерфейс Iterator Class Collection implements Iterator { protected $position = 0; protected $items = array(); public function add( $data ) { // заранее создаем объекты $this->items[] = new Model($data); } function rewind() { $this->position = 0; } function current() { return $this->items[$this->position]; } function key() { return $this->position; } function next() { ++$this->position; } function valid() { return isset($this->items[$this->position]); } }
Во втором, объекты создаются только по-требованию, а в коллекции хранятся только данные
Class Collection implements Iterator { protected $position = 0; protected $items = array(); public function add( $data ) { // храним только данные $this->items[] = $data; } function rewind() { $this->position = 0; } function current() { // создаем объект по требованию return new Model($this->items[$this->position]); } function key() { return $this->position; } function next() { ++$this->position; } function valid() { return isset($this->items[$this->position]); } }
Результаты тестирования:
Способ | Время, мс | Расход памяти, кб |
---|---|---|
Создание всех объектов сразу | 1389±10 | 40279,82 |
Создание объектов по-требованию | 1344±10 | 415,28 |
Экономия памяти получилась в буквальном смысле в 100 раз.
Популярная ORM Doctrine 1.* уже содержит гидратор Doctrine_Core::HYDRATE_ON_DEMAND, который вроде как реализует ту же логику docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/data-hydrators.html#on-demand
ссылка на оригинал статьи http://habrahabr.ru/post/164123/
Добавить комментарий