1

I see the benefit of using object pooling, and I also want to combine it with Vectors. But, reading about Vectors, I see that they can only be defined at compile time, meaning a separate pooler class for each of the pooled classes is required. On the other hand, I would like to have a random class instance of a set (all extending a certain class) to be pulled from a pool at runtime, so that I don't exactly know which object pooler is to be called. And, in order to not multiply the code sequences for pooler classes, I want to combine all pools into a UniversalPooler class, which then can serve requests like var foo:SomeClass=UniversalPool.getFromPool(SomeClass);. The question is, how can I implement such a universal pooler performance effectively, using Vectors if at all possible, and probably using random subclass selection?

Vesper
  • 18,599
  • 6
  • 39
  • 61

1 Answers1

1

Yes, unfortunately it's not possible to use Vectors, AS3 has very poor implementation of generics. I use the following pooling based on class map Dictionary and Arrays for objects storage:

package util.pool
{
import flash.utils.Dictionary;

public class ObjectPool
{
    private var storage:Dictionary = new Dictionary();

    public function ObjectPool()
    {
    }

    private function createNew(constructor:Class, ...args):Object
    {
        //as3 - (facepalm), why there isn't API to path array of params to constructor
        switch (args.length) {
            case 0:return new constructor();
            case 1:return new constructor(args[0]);
            case 2:return new constructor(args[0], args[1]);
            case 3:return new constructor(args[0], args[1], args[2]);
            case 4:return new constructor(args[0], args[1], args[2], args[3]);
            case 5:return new constructor(args[0], args[1], args[2], args[3], args[4]);
            case 6:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5]);
            case 7:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
            case 8:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
            case 9:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
            case 10:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
            default: throw new Error("too much arguments for constructor, add more switch cases :))");
        }       
    }

    public function create(constructor:Class, ...args):Object
    {
        var pool:Array = getOrCreatePool(constructor);

        var res:*;
        if(pool.length > 0)
        {
            res = pool.pop();
        }else
        {
            args.unshift(constructor);
            res = createNew.apply(null, args);
        }

        return res;
    }

    public function destroy(obj:Object):void
    {
        if(!obj) return;
        if(!(obj is Object)) return;

        var constructor:Class = obj["constructor"];
        var pool:Array = getOrCreatePool(constructor);
        pool[pool.length] = obj;
    }

    public function destroyArr(objs:Object):void
    {
        for each(var obj:Object in objs)
            destroy(obj);
    }

    private function getOrCreatePool(constructor:Class):Array
    {
        var pool:Array = storage[constructor];
        if(!pool)
        {
            pool = [];
            storage[constructor] = pool;
        }

        return pool;
    }
}
}

It can be used as one global pool. And I don't think that there can be much more effective implementation, it's a quite simple feature in general.

fsbmain
  • 5,267
  • 2
  • 16
  • 23
  • I was considering building a pool via Dictionary+arrays, indeed. I think this is changeable into unresizing arrays, IIRC calling `pop()` or `shift()`, as well as `push` and `unshift` is more costly than assigning to a known index. But since the index needs to be stored alongside the pool, and we are already calling `Dictionary[constructor]` once, another call or a nested Object {a:Array,i:current index} might make bigger impact on performance than shifting/popping does to GC. This needs some thinking and testing, while for my current project it's largely irrelevant. – Vesper May 30 '13 at 09:56
  • Yes, you're right, so I assign by index instead of `push` when adding, but use `pop` for removing (may be maintaining current index and "nulling" the last element will be more effective here, needs some tests). If you are going to make such test, I will looking forward the fastest pooling class ) – fsbmain May 30 '13 at 10:19
  • I see a flaw in your implementation of generic pool class, which purpose is to avoid actually creating a new instance every time you need an instance: by using ...args for each object creation you actually create an Array so you're replacing an object instantiation by another. Have you tried benchmarking your implementation, and compared it with a similar version with fixed number of args ? Duplicating methods for each number of args is not the most elegant solution but if you're really heading for performance, it should be worth trying – jauboux Sep 18 '14 at 13:33
  • Also, have you tried Vector.<*> ? on recent SDK / player, Vector is much fasterthan Array – jauboux Sep 18 '14 at 13:38
  • Last but not least, I think your pooled objects should have a constructor without arguments, or implement an interface like IInitializable with initialize() method with same signature as the constructor that would be called just after "res = pool.pop();" otherwise when you call create() with arguments, you don't know if the returned value has been instantiated with those args or if it's just a recycled instance which is NOT initialized. This can be a major source of bugs! – jauboux Sep 18 '14 at 13:48
  • It's a very simple pool implementation and of cause can be tuned/extended for more complex cases when you need to reset object before/after pooling. That one was created for quick replacing of object instantiation for objects different types with different number of parameters in constructor. And of cause if we are using some constructor params they should be the same for all objects in the pool (like factory parameters) or they should be changeable via getters/setters after pooling. – fsbmain Sep 18 '14 at 14:19