Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allocation free delegate or other way to call method by address?

I need to be able to call a single method based on a function pointer in C# using Mono. Delegates work fine for this and it's their purpose, but they seem to allocate 52 bytes each time I set the delegate (not +=, but just setting it using = so there's always one and only one method referenced by the delegate).

This delegate changes many times per second and it causes the GC to kick in periodically which I'd like to avoid.

I don't mind the initial memory allocation, but is there a way to prevent an allocation each time I change the single delegate value?

If not, is there any other dynamic way to call a method in C# besides delegates that wouldn't allocate any memory each time the address is changed?

like image 692
mr.b Avatar asked Dec 30 '13 09:12

mr.b


People also ask

How many methods can be called using a delegate?

Only one method can be called using a delegate.

Which function do you use to delegate memory?

using System; delegate int MyDelegate(int a,int b);

What is the delegate method?

A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance.

Why delegates why not call methods directly?

If you think of delegates as being similar to interface definitions for a specific type of method, you can start to see why delegates exist. They allow clients of our delegates to ignore all the details of their implementations - even their names!


1 Answers

Any code you write like this

Action action = foo.DoSomething;

Ends up being compiled into this

Action action = new Action(foo.DoSomething);

Which is where the allocations are coming from. There aren't any perfect ways around this but to prevent the allocations you need to cache and reuse the delegate.

Implementation Side Fix

You could achieve this on the implementation side by creating a delegate for each of your methods.

public class Foo
{
    public void DoSomething() { /*nop*/ }

    private Action _doSomethingDelegate;
    public Action DoSomethingDelegate
    {
        get { return _doSomethingDelegate ?? (_doSomethingDelegate = DoSomething); }
    }
}

Then you would just reference the existing delegate rather than the method

Action action = foo.DoSomethingDelegate;

Cache Fix

Another option is to use some sort of cache class but this introduces a whole pile of object lifetime issues which you probably don't want in a game scenario. This is a bit of a crude implementation are real one would probably want to use weak references.

public static class DelegateCache
{
    private static readonly Dictionary<object, Dictionary<string, Delegate>> Cache = new Dictionary<object, Dictionary<string, Delegate>>();

    private static Dictionary<string, Delegate> GetObjectCache(object instance)
    {
        Dictionary<string, Delegate> delegates;
        if (!Cache.TryGetValue(instance, out delegates))
        {
            Cache[instance] = delegates = new Dictionary<string, Delegate>();
        }
        return delegates;
    }

    public static T GetDelegate<T>(object instance, string method)
        where T: class
    {
        var delegates = GetObjectCache(instance);
        Delegate del;
        if (!delegates.TryGetValue(method, out del))
        {
            delegates[method] = del = Delegate.CreateDelegate(typeof(T), instance, method);
        }
        return del as T;
    }
}

Using this would then look like this

Action action = DelegateCache.GetDelegate<Action>(foo, "DoSomething");

Summary

Running some tests both these methods have only a single allocation per object/method pair. I would probably go the implementation side fix it is a lot cleaner even though it is a lot of work. If there are a lot of methods and you plan on adding many more you could use T4 to generate a partial class with the delegate implementation for your methods.

like image 193
David Ewen Avatar answered Oct 02 '22 15:10

David Ewen