2

I have an object pool, and I need to call a delegate method OnFree(), whenever I call Free() on an object in the pool.

Free() is created externally and set on the object when the pool is created. OnFree differs from one object to another, and sometimes it is even null.

Objects in the pool inherit from the Poolable class.

class Poolable
{
    public Action Free();
    public Action OnFree();
}

Currently I create OnFree in the inheriting class by doing this:

class Light
{
    public Light()
    {
        // Create method to be called when Free() is called on this light.
        OnFree = () =>
        {
            DoStuffHere();
        };
    }
}

However, this will create a separate delegate for each light, which wastes a bunch of memory especially when there are tens of thousands of objects in the pool. Er, it does create a new delegate every time this constructor is called, right?

What is a good way to allow objects to create their own OnFree() delegate, so that there is only one delegate per object type, instead of one delegate per instance?

I can think of a way of course, but I'm hoping someone can think of a "good" way -- something that allows easy maintainability.


Edit: Can I make the OnFree() delegate static in the base class, so that it is static per inherited type somehow?


Edit: To clarify how Pool is used, and why Free() is a delegate, not a virtual method. Please let me know if you can think of a better way to do this.

public class Pool<T> where T : Poolable
{
    private int _liveCount;
    private T[] _pool;
    [...]
    public Pool(int capacity, Func<T> allocateFunction)
    {
        [...]
        // Fill pool with initial items:
        for (int i = 0; i < capacity; i++)
        {
            T item = _allocate();
            item.Free = () => Free(item);
            _pool[i] = item;
        }
    }

    /// <summary>
    /// Frees given object from this pool. Object is assumed to
    /// be in this pool.
    /// </summary>
    public void Free(Poolable obj)
    {
        obj.OnFree();

        _liveCount -= 1;
        [...]
    }
}
Olhovsky
  • 5,466
  • 3
  • 36
  • 47
  • I'm trying to keep the question simple, but I'm finding it difficult to ask clearly -- so please ask questions if I left any information out or if anything is unclear. – Olhovsky May 07 '11 at 22:19
  • IIRC, Lambda expressions are converted to anonymous delegates, which in turn are implemented as methods of compiler generated classes. So you get only one method. The only memory used is the one for the function pointer. – Etienne de Martel May 07 '11 at 22:19
  • Etienne: What does that imply? Does that mean that only one method is made even if I call the constructor many times? – Olhovsky May 07 '11 at 22:21
  • Etienne, thanks, I hope you're right. Some confirmation would be good, as I can't think of a fast way to test this. – Olhovsky May 07 '11 at 22:24

3 Answers3

2

It actually depends on where DoStuffHere() is defined. If this is an instance method, there is an implicit capture of this onto a compiler-generated type; likewise anything else (not shown in your example) might be captured.

In most normal cases the extra overhead of a delegate instance is minimal. One workaround to avoid passing creating a delegate is to have a parameterised delegate (an Action<SomeStateType>, perhaps stored in a static field), and feed the state in as a separate parameter... but of course, then you are creating an object for the state! The slight advantage of doing a manual capture is that you are probably (it depends on the exact code sample) reducing it from 2 (or more) allocations (1 delegate, 1-or-more capture classes) to 1 allocation (your manual capture; the delegate being held on a static field).

One way of another, there is likely going to be something created. Personally, until your profiling shows it is a bottleneck, I think you should relax a bit - allocations are very fast, and most times the object will be collected in GEN-0, which is very efficient.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Actually I'm trying to reduce space used in the L1 cache by all the extra delegate copies as cache misses are unusually expensive on my target platform. Also, I'm developing for the .NET compact CLR which has no GEN-0 sadly (it uses a sweep and prune garbage collector instead of a generational approach). Nevertheless, this was informative, +1. – Olhovsky May 08 '11 at 04:47
  • @Olhovsky - maybe use a static field delegate and a struct context then? – Marc Gravell May 08 '11 at 13:39
2

How about keeping it simple:

class Poolable
{
    public virtual void Free() { }
    public virtual void OnFree() { }  // naming not according to BCL std
}

class Light : Poolable
{
    public override void Free() { }
    ...
}
  • your example shows no need for delegates (over virtual methods)
  • proper encapsulation would require events instead of public delegates
  • looks like you are optimizing prematurely.
H H
  • 263,252
  • 30
  • 330
  • 514
  • Re optimizing prematurely: This pool class is used to hold most reference type objects, as I'm trying to avoid all garbage collection when developing a realtime application for the .NET Compact CLR. Since this code is used so often, I'm trying to make it reasonably efficient. – Olhovsky May 08 '11 at 17:22
  • My example doesn't show it, but I `Free()` does need to be a delegate (as it does the same thing in every object that inherits from Poolable, but I can't create it in poolable since `Free()` needs to access the Pool, whose T type is not known within Poolable. – Olhovsky May 08 '11 at 17:24
  • Making `OnFree` virtual does seem to be the right choice here, not sure how I missed it! In general when developing for the compact CLR, I avoid virtual calls as they are expensive -- I guess that's turned into a bad habit, in this case. – Olhovsky May 08 '11 at 17:25
  • I've added an edit to my question to better explain why I made `Free()` a delegate. Please let me know if you see a better way to do it. – Olhovsky May 08 '11 at 17:32
  • I think that all you might need is a MyPool property in Poolable. But we'd have to see how you invoke Free(). – H H May 08 '11 at 18:08
  • `Free()` is called whenever an object is no longer going to be used. It is used at a similar time that you'd do free(obj) in C/C++. The idea being that this object will be placed back in the pool when you call Free(), instead of letting the garbage collector handle it. So `Free()` can be called by anything, anywhere, I guess. – Olhovsky May 08 '11 at 19:28
  • You can't have a MyPool property in Poolable because you'd have to declare MyPool, but T is not known in Poolable. Is there a way around that? – Olhovsky May 08 '11 at 19:35
  • If you make Free() a method of Poolable and make sure a Poolable has a reference to the Pool then you should have everything you need. – H H May 08 '11 at 19:36
  • Yes but pool is of type Pool, so how do I create a reference to Pool in Poolable, before Poolable has type T? – Olhovsky May 08 '11 at 19:37
  • Derive `Pool` from IPool, or go low-tech: `private object _myPool;` – H H May 08 '11 at 19:40
  • One last question. If Free() is defined the base type Poolable, and it calls OnFree(), will it call the inheriting objects OnFree(), or will it call Poolable's OnFree()? I want it to call the inheriting objects OnFree(). (E.g. Light inherits from Poolable, and Light.OnFree() should be called from Poolable's `Free()`.) – Olhovsky May 08 '11 at 19:44
  • It is virtual, but virtual methods have a copy of the method in the base class that differs from the implementation in the child class. So if the base class has a method that calls OnFree(), won't it call the implementation of the virtual method in the base class, not the implementation in the child class? – Olhovsky May 08 '11 at 19:49
  • Nevermind, Jon Skeet demonstrates that in C# this doesn't happen. It might in Java, which was possibly the source of my confusion. http://stackoverflow.com/questions/448258/calling-virtual-method-in-base-class-constructor – Olhovsky May 08 '11 at 19:52
  • It will call the most-derived method (first). That's the whole and only point of `virtual`. – H H May 08 '11 at 19:52
  • Sigh, sorry to drag this on further, but your suggestion to use `private object _myPool;` doesn't seem to work, since you have to cast it to the Pool type (which you can't) to actually use _myPool in the Poolable implementation of Free(). – Olhovsky May 08 '11 at 19:54
  • But your IPool suggestion does. Thanks, you're awesome. – Olhovsky May 08 '11 at 19:58
  • If anyone is reading this, be sure to upvote this answer, Henk certainly deserves it! – Olhovsky May 08 '11 at 19:58
1

If you use a static generic class you get one "instance" per type - which is exactly what you were after. Hence, using such a class as the backstore for your type-specific delegates, and initialize them in the static constructor of each Poolable sub-class would solve your problem. See the sample code:

public class Poolable
{
    public Action Free { get; set; }
    public Action OnFree { get { return GetOnFree(); } }

    protected virtual Action GetOnFree() { throw new NotImplementedException(); }
}

public static class PoolHelper<T> where T : Poolable
{
    public static Action OnFree { get; set; }
}

public class Light : Poolable
{
    static Light()
    {
        PoolHelper<Light>.OnFree = () => 
        {
            // Define 'OnFree' for the Light type here...
            // and do so for all other other sub-classes of Poolable
        };
    }

    protected override Action GetOnFree()
    {
        return PoolHelper<Light>.OnFree;
    }
}
mthierba
  • 5,587
  • 1
  • 27
  • 29
  • +1 because this technically works and you clearly put effort into this response. However, you want to create a separate pool helper for each class that inherits from Poolable? I.e. `class Light : Poolable` will have a helper, and `class Shadow : Poolable` would also need a different helper? In that case I would rather just create a static field in the `Light` class to store the delegate. I also don't like that solution because it means potentially recreating the delegate at every constructor call. – Olhovsky May 08 '11 at 05:15
  • Actually this doesn't quite work. If you create a new light between calls to GetOnFree(), then you'll create new delegates. – Olhovsky May 08 '11 at 07:18
  • @Olhovsky: No, this creates one delegate per type, not per instance, as the delegate creation happens in the type constructor (`static Light()`), not the instance constructor. Of course, you can put a static field into the Light class, BUT then it's your responsibility to declare this for every single sub-type of Poolable, whereas the `PoolHelper` only has to be declared ONCE, and it fulfills the very same purpose. IMHO, a lot less coding effort. – mthierba May 08 '11 at 08:35
  • ServiceGuy: Oh I'm sorry you're right, I see now that there will be a static PoolHelper created per type -- my mistake! Unfortunately, this method also now uses a virtual method and a delegate, which is a performance hit. – Olhovsky May 08 '11 at 17:16