Как что-то сделать, при этом ничего не делая
Пул объектов
Запись от gloomyBrain размещена 31.08.2011 в 23:27
Обновил(-а) gloomyBrain 28.09.2011 в 22:15 (Исправил и дополнил)
Обновил(-а) gloomyBrain 28.09.2011 в 22:15 (Исправил и дополнил)
Перед каждым программистом, так или иначе, периодически встают задачи оптимизации. Побуду немного Капитаном Очевидность - программы потребляют всего две вещи: память и процессорное время. Сегодня я хотел бы рассказать об одном из способов экономить память - объектном пуле.
Для чего нам это нужно?
Тут все достаточно просто. Каждый раз, когда мы создаем новый объект
мы просим систему выделить нам память. После того, как память найдена и предоставлена, мы заполняем ее необходимыми данными. После этого мы запускаем конструктор. А конструктор делает еще миллион всяких вызовов и создает кучу объектов. И все это тратит драгоценные процессорное время и память.
Пул объектов позволит нам не отдавать ненужные объекты сборщику мусора (GC), а, вместо этого, положить их на полочку, откуда, в случае необходимости, мы их снова заберем. То есть мы не будем искать память, не будем вызывать конструктор... Ничего не будем делать. Нужен объект - вот он, забирай!
Хватит теории, как это работает?
Сразу оговоримся - реализации могут быть разные, все со своими плюсами и минусами. Если у нас миллионы объектов, которые рождаются и погибают пачками по несколько тысяч за раз - выгоднее сначала насоздавать объектов, запихнуть их в пул, а потом уже забирать сколько влезет.
Если объектов немного или они, по крайней мере, не создаются тысячами за раз, то есть смысл генерировать их по одному.
Ну, и вправду, хватит теории. Так как что и как придется делать мы заранее никогда не знаем, давайте попробуем описать наш пул интерфейсом. У меня вышло примерно так:
package utils.pooling { /** * Интерфейс для пула объектов * * @author gloomyBrain */ public interface IPool { /** * Добавить объект в пул */ function addItem(value:Object):void; /** * Получить следующий объект из пула */ function getItem():Object; } }
Ну а где же сам пул?
Вот он:
package ugo.utils.pooling { import flash.errors.IllegalOperationError; import flash.utils.Dictionary; import ugo.core.IFactory; /** * Самый простой пул объектов * * @author gloomyBrain */ public final class GenericPool implements IPool { private var _pool:Vector.<Object>; private var _linker:Dictionary; private var _factory:IFactory; private var _initCount:uint; public function GenericPool(factory:IFactory, initCount:uint = 1) { super(); if (!factory) throw new ArgumentError("Параметр является обязательным!"); _factory = factory; _initCount = (initCount > 0) ? initCount : 1; _pool = new Vector.<Object>(); _linker = new Dictionary(); } /* INTERFACE ugo.utils.pooling.IPool */ /** * Добавить объект в пул */ public function addItem(value:Object):void { if (!value) throw new ArgumentError("Параметр не должен быть нулевым!"); if (value in _linker) throw new IllegalOperationError("Объект уже находится в этом пуле!"); if (!(value is _factory.instanceType)) throw new IllegalOperationError("Объект не может быть добавлен в пул из-за несоответствия типов!"); this.addItemInternal(value); } /** * Получить следующий объект из пула */ public function getItem():Object { var result:Object = null; if (_pool.length) { result = _pool.pop(); delete _linker[result]; }else { var len:int = _initCount - 1; while (len--) this.addItemInternal(_factory.newInstance()); result = _factory.newInstance(); } return result; } private function addItemInternal(value:Object):void { _pool.push(value); _linker[value] = true; } } }
PS
IFactory - это еще один интерфейс для генерации новых объектов. Он нужен для того, чтобы запускать конструктор объекта с нужными параметрами. Выглядит он примерно так:
package core { /** * Интерфейс для фабрики объектов * * @author gloomybrain */ public interface IFactory { /** * Тип данных экземпляров, возвращаемых фабрикой */ function get instanceType():Class; /** * Создает новый экземпляр объекта */ function newInstance():Object; } }
PPS
Да, пожалуй, многовато кода... Комментарии приветствуются!
PPPS
thx i.o., добавил Dictionary для быстрой проверки находится ли объект в пуле. Потому как 2 раза один и тот же объект помещать в пул нам незачем.
Всего комментариев 42
Комментарии
01.09.2011 00:12 | |
ЗдОрово.
Может имеет смысл добавить в IPoolable метод init() для инициализация каких-либо дефолтных свойств при извлечении из стека. IFactory меня смущает, пока не знаю чем. |
01.09.2011 00:50 | |
01.09.2011 00:51 | |
По-моему объекту должно быть до фени, можно ли его добавить в пул или нет. Не его это как бы беспокойства
|
01.09.2011 13:32 | |
@СлаваRa
А в чем смысл приводить int к int'у? @f.g.programmer Ну да. Так там писать-то 3 строчки: |
01.09.2011 14:27 | |
насчет int'ов, это было в какой-то статье по оптимизации, вспомню если, скину ссылку. Пишу на автомате уже.
|
01.09.2011 14:42 | |
01.09.2011 14:56 | |
я уверен, что это так.
|
02.09.2011 10:33 | |
А есть ли смысл, при каких-либо условиях очищать (частично) пул во время работы программы?
|
03.09.2011 17:08 | |
03.09.2011 19:00 | |
Я бы еще добавил maxSize, что бы там не хранилось 20000 элементов
|
04.09.2011 00:22 | |
to gloomyBrain, хитро.
|
10.09.2011 19:01 | |
2Azo
у меня, например, за создание экземпляра отвечает сам пул, т.е. мы не создаем ничего сами, мы берем из пула. |
13.09.2011 23:18 | |
В конце припишите, пожалуйста, к чему пришло сообщество.
|
19.09.2011 10:55 | |
Не знаю уместным ли будет мой пост. Читал как-то официальный справочник по оптимизации кода, там так же решается подобная задача, может кто-то не знал и это поможет http://help.adobe.com/ru_RU/as3/mobi...ng_content.pdf
Страница 10 |
28.09.2011 14:08 | |
вот это конечно эпично:
var cast:Object = value as _factory.instanceType; if (!cast) throw new IllegalOperationError("Объект не может быть добавлен в пул из-за несоответствия типов!"); this.addItemInternal(cast); |
|
Обновил(-а) BlooDHounD 28.09.2011 в 14:11
|
28.09.2011 17:02 | |
Цитата:
зачем мы в пул пихаем класс?
На счет is/as - согласен, если честно, даже не помню откуда там as =) |
28.09.2011 18:45 | |
Цитата:
даже не помню откуда там as =)
|
28.09.2011 18:53 | |
И помему в IPool, addItem принимает значение IPoolable, а в классе GenericPool - Object?
|
28.09.2011 22:16 | |
Спасибо, fixed
|
29.09.2011 17:24 | |
Цитата:
Я в своем коде запихивания класса в пул не вижу.
|
14.10.2013 14:38 | |
gloomyBrain Спасибо! Жду от Вас ещё каких нибудь интересных статей!
|
06.08.2015 18:44 | |
зачем удалять ячейку через pop() если можно ее просто занулить, не меняя размер массива, а потом положить в нее новое значение, что бы не вызывать каждый раз push()
|
26.05.2017 23:13 | |
Цитата:
А зачем нужен вызов
Код: super(); в конструкторе пула? |
Последние записи от gloomyBrain
- Очередной загрузчик. Часть 2. (05.12.2012)
- Очередной загрузчик. Часть 1. (04.12.2012)
- Пул объектов (31.08.2011)
- Мания чисел (08.12.2010)
- Отправка файлов на сервер (допилил) (24.10.2010)