Reputation: 1038
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:
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.
Upvotes: 3
Views: 1887
Reputation: 842
Maybe you want to give a try to the ObjectMethodExecutor
According to Hanselman:
If you ever need to invoke a method on a type via reflection and that method could be async, we have a helper that we use everywhere in the ASP.NET Core code base that is highly optimized and flexible called the ObjectMethodExecutor.
The team uses this code in MVC to invoke your controller methods. They use this code in SignalR to invoke your hub methods. It handles async and sync methods. It also handles custom awaitables and F# async workflows
Upvotes: 3
Reputation: 160
You could use open delegates, which are about ten times faster than MethodInfo.Invoke
. You would create such a delegate
from a MethodInfo
like this:
delegate void OpenCommandCall(Callee element, parameters);
OpenCommandCall occDelegate = (OpenCommandCall)Delegate.CreateDelegate(typeof(OpenCommandCall), methodInfo));
You then would call this delegate like:
occDelegate.Invoke(callee, params);
Where callee
is the element you want to call the method on, methodInfo
is the MethodInfo
of the method and parameters
are a placeholder for various other parameters.
Upvotes: 4