首页 > 解决方案 > C#/.NET Most performant way to call a method dynamically

问题描述

We are developing a system, which reads commands from a tcp/ip stream and then executes those commands. Commands consist of a method call on an object also identified by an id int the command. You could think of a command as the information of an element id (addressing element we want to call a the command on) and an command id (addressing the method which should be called on the element). Additionally we also have the issue that we need to check some kind of permissions on every command and also how this command should be executed. (Should it be started in a new Thread, etc.)

An example of how such a command call could look like would be this:

class Callee
{
    public void RegularCall(int command, parameters)
    {
        switch (command)
        {
            case 1: // Comand #1
                // Check if the permissions allow this command to be called.
                // Check if it should be outsourced to the ThreadPool and
                // call it accordingly. +Other Checks.
                // Finally execute command #1.
                break;
            case 2: // Comand #2
                // Check if the permissions allow that command to be called.
                // Check if it should be outsourced to the ThreadPool and
                // call it accordingly. +Other Checks.
                // Finally execute command #2.
                break;
            // Many more cases with various combinations of permissions and
            // Other flags.
        }
    }
}

And somewhere:

static Dictionary<int, Callee> callees = new Dictionary<int, Callee>();

static void CallMethod(int elementId, int commandId, parameters)
{
    callees[elementId].RegularCall(commandId, parameters);
}

However, this approach is some kind of unelegant:

My first approach was by using reflection, which would have looked that way:

class Callee
{
    [Command(1)]
    [Permissions(0b00111000)]
    [UseThreadPool]
    public void SpeakingNameForCommand1(parameters)
    {
        // Code for command #1.
    }

    [Command(2)]
    [Permissions(0b00101011)]
    public void SpeakingNameForCommand2(parameters)
    {
        // Code for command #2.
    }

    // Again, many more commands.
}

This code must have been initialized with some reflection heavy code:

  1. Find all classes which may represent an element.
  2. Find all methods which have a command attribute, etc.
  3. Store all those information in a dictionary, including the corresponding MethodInfo.

A call of a received command would look like this, where CommandInfo is a class containing all the information required for the call (MethodInfo, run in ThreadPool, permissions...):

static Dictionary<int, CommandInfo> commands = new Dictionary<int, CommandInfo>();

static void CallMethod(int elementId, int commandId)
{
    CommandInfo ci = commands[commandId];

    if (ci.Permissions != EVERYTHING_OK)
        throw ...;

    if (ci.UseThreadPool)
        ThreadPool.Queue...(delegate { ci.MethodInfo.Invoke(callees[elementId], params); });
    else
        ci.MethodInfo.Invoke(callees[elementId], params);
}

When I micro-benchmark this, the call to MethodInfo.Invoke is about 100x slower than the direct call. The question is: Is there a faster way of calling those "command" methods, without losing the elegance of the attributes defining the way how those commands should be called?

I also tried deriving a delegate from the MethodInfo. However, this didn't work well, because I need to be able to call the method on any instance of the Callee class and don't want to reserve the memory for the delegate for every possible element * commands. (There will be many elements.)

Just to make this clear: MethodInfo.Invoke is 100x slower than the function call including the switch/case statement. This excludes the time to walk over all classes, methods and attributes, because those informations have already been prepared.

Please refrain from informing me about other bottlenecks like the network. They are not the issue. And they are no reason to use slow calls on another location in the code. Thank you.

标签: c#.netperformancereflection

解决方案


也许您想尝试一下ObjectMethodExecutor

根据汉塞尔曼

如果您需要通过反射调用类型上的方法并且该方法可能是异步的,我们有一个帮助器,我们在 ASP.NET Core 代码库中的任何地方都可以使用它,它高度优化和灵活,称为 ObjectMethodExecutor。

该团队在 MVC 中使用此代码来调用您的控制器方法。他们在 SignalR 中使用此代码来调用您的集线器方法。它处理异步和同步方法。它还处理自定义等待对象和 F# 异步工作流


推荐阅读