From b67bc9f2b8d2039c600afccdc139ed25f751f111 Mon Sep 17 00:00:00 2001 From: DJGosnell Date: Thu, 27 Jul 2017 18:07:52 -0400 Subject: [PATCH] Work on speeding up method invocation. --- .../DtronixMessageQueue.csproj | 1 + .../MessageHandlers/RpcCallMessageHandler.cs | 19 +- .../Rpc/RpcRemoteService.cs | 22 ++ src/DtronixMessageQueue/Rpc/RpcSession.cs | 4 +- .../Rpc/ServiceMethodCache.cs | 199 ++++++++++++++++++ 5 files changed, 240 insertions(+), 5 deletions(-) create mode 100644 src/DtronixMessageQueue/Rpc/RpcRemoteService.cs create mode 100644 src/DtronixMessageQueue/Rpc/ServiceMethodCache.cs diff --git a/src/DtronixMessageQueue/DtronixMessageQueue.csproj b/src/DtronixMessageQueue/DtronixMessageQueue.csproj index 2060450..8256e82 100644 --- a/src/DtronixMessageQueue/DtronixMessageQueue.csproj +++ b/src/DtronixMessageQueue/DtronixMessageQueue.csproj @@ -69,6 +69,7 @@ + diff --git a/src/DtronixMessageQueue/Rpc/MessageHandlers/RpcCallMessageHandler.cs b/src/DtronixMessageQueue/Rpc/MessageHandlers/RpcCallMessageHandler.cs index 417c012..2a6fcfc 100644 --- a/src/DtronixMessageQueue/Rpc/MessageHandlers/RpcCallMessageHandler.cs +++ b/src/DtronixMessageQueue/Rpc/MessageHandlers/RpcCallMessageHandler.cs @@ -21,9 +21,11 @@ public class RpcCallMessageHandler : MessageHandler /// Contains all services that can be remotely executed on this session. /// - public readonly Dictionary> Services = + private readonly Dictionary> _services = new Dictionary>(); + + /// /// Proxy objects to be invoked on this session and proxied to the recipient session. /// @@ -50,6 +52,17 @@ public RpcCallMessageHandler(TSession session) : base(session) Handlers.Add((byte) RpcCallMessageAction.MethodReturn, ProcessRpcReturnAction); } + public void AddService(T instance) where T : IRemoteService + { + _services.Add(instance.Name, instance); + var methods = instance.GetType().GetMethods(); + } + + /// + /// Cancels the specified action. + /// + /// byte associated with the RpcCallMessageAction enum.> + /// Message containing the cancellation information. public void MethodCancelAction(byte actionId, MqMessage message) { var cancellationId = message[0].ReadUInt16(0); @@ -87,13 +100,13 @@ private void ProcessRpcCallAction(byte actionId, MqMessage message) var recArgumentCount = serialization.MessageReader.ReadByte(); // Verify that the requested service exists. - if (Services.ContainsKey(recServiceName) == false) + if (_services.ContainsKey(recServiceName) == false) { throw new Exception($"Service '{recServiceName}' does not exist."); } // Get the service from the instance list. - var service = Services[recServiceName]; + var service = _services[recServiceName]; // Get the actual method. TODO: Might want to cache this for performance purposes. var methodInfo = service.GetType().GetMethod(recMethodName); diff --git a/src/DtronixMessageQueue/Rpc/RpcRemoteService.cs b/src/DtronixMessageQueue/Rpc/RpcRemoteService.cs new file mode 100644 index 0000000..39b3ad0 --- /dev/null +++ b/src/DtronixMessageQueue/Rpc/RpcRemoteService.cs @@ -0,0 +1,22 @@ +namespace DtronixMessageQueue.Rpc +{ + /// + /// Represents a remote service accessible through a RpcProxy object. + /// + /// Session type. + /// Configuration type. + public abstract class RpcRemoteService + where TSession : RpcSession, new() + where TConfig : RpcConfig + { + /// + /// Name of this service. + /// + public abstract string Name { get; } + + /// + /// Session for this service instance. + /// + public TSession Session { get; set; } + } +} \ No newline at end of file diff --git a/src/DtronixMessageQueue/Rpc/RpcSession.cs b/src/DtronixMessageQueue/Rpc/RpcSession.cs index 3e1e4cd..1057878 100644 --- a/src/DtronixMessageQueue/Rpc/RpcSession.cs +++ b/src/DtronixMessageQueue/Rpc/RpcSession.cs @@ -388,8 +388,8 @@ public T GetProxy() where T : IRemoteService /// Instance to execute methods on. public void AddService(T instance) where T : IRemoteService { - RpcCallHandler.Services.Add(instance.Name, instance); - instance.Session = (TSession) this; + instance.Session = (TSession)this; + RpcCallHandler.AddService(instance); } } } \ No newline at end of file diff --git a/src/DtronixMessageQueue/Rpc/ServiceMethodCache.cs b/src/DtronixMessageQueue/Rpc/ServiceMethodCache.cs new file mode 100644 index 0000000..68f42c6 --- /dev/null +++ b/src/DtronixMessageQueue/Rpc/ServiceMethodCache.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; + +namespace DtronixMessageQueue.Rpc +{ + public class ServiceMethodCache + where TSession : RpcSession, new() + where TConfig : RpcConfig + { + + private delegate object InvokeHandler(object target, object[] paramters); + + + private Dictionary> _serviceMethods = + new Dictionary>(); + + public ServiceMethodCache() + { + + } + + public void AddService(T instance) where T : IRemoteService + { + // If this service has already been registered, do nothing. + if (_serviceMethods.ContainsKey(instance.Name)) + return; + + var methods = new + + // Get all the public methods for this + var methods = instance.GetType().GetMethods(); + + foreach (var methodInfo in methods) + { + methodInfo.Name + } + } + + public void Invoke(string service, string method, object serviceTarget, object[] parameters) + { + + } + + private class ServiceMethodInfo + { + public MethodInfo MethodInfo; + public Type[] ParameterTypes; + public bool hasCancellation; + } + + /// + /// Black magic box to create IL code invoker. + /// + /// Method to create an invoker for. + /// Invoker delegate crafted for the specified method. + /// + /// https://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker?msg=1555655#xx1555655xx + /// + private static InvokeHandler GetMethodInvoker(MethodInfo methodInfo) + { + if (methodInfo == null) + throw new ArgumentNullException(nameof(methodInfo), "Passed method must not be null."); + + var dynamicMethod = new DynamicMethod(string.Empty, typeof(object), + new[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module); + + var il = dynamicMethod.GetILGenerator(); + var ps = methodInfo.GetParameters(); + var paramTypes = new Type[ps.Length]; + + for (var i = 0; i < paramTypes.Length; i++) + { + if (ps[i].ParameterType.IsByRef) + paramTypes[i] = ps[i].ParameterType.GetElementType(); + else + paramTypes[i] = ps[i].ParameterType; + } + + var locals = new LocalBuilder[paramTypes.Length]; + + for (var i = 0; i < paramTypes.Length; i++) + locals[i] = il.DeclareLocal(paramTypes[i], true); + + for (var i = 0; i < paramTypes.Length; i++) + { + il.Emit(OpCodes.Ldarg_1); + EmitFastInt(il, i); + il.Emit(OpCodes.Ldelem_Ref); + EmitCastToReference(il, paramTypes[i]); + il.Emit(OpCodes.Stloc, locals[i]); + } + + if (!methodInfo.IsStatic) + il.Emit(OpCodes.Ldarg_0); + + for (var i = 0; i < paramTypes.Length; i++) + il.Emit(ps[i].ParameterType.IsByRef ? OpCodes.Ldloca_S : OpCodes.Ldloc, locals[i]); + + il.EmitCall(methodInfo.IsStatic ? OpCodes.Call : OpCodes.Callvirt, methodInfo, null); + + + if (methodInfo.ReturnType == typeof(void)) + il.Emit(OpCodes.Ldnull); + else + EmitBoxIfNeeded(il, methodInfo.ReturnType); + + for (var i = 0; i < paramTypes.Length; i++) + { + if (!ps[i].ParameterType.IsByRef) + continue; + + il.Emit(OpCodes.Ldarg_1); + EmitFastInt(il, i); + il.Emit(OpCodes.Ldloc, locals[i]); + + if (locals[i].LocalType.IsValueType) + il.Emit(OpCodes.Box, locals[i].LocalType); + + il.Emit(OpCodes.Stelem_Ref); + } + + il.Emit(OpCodes.Ret); + var invoker = (InvokeHandler)dynamicMethod.CreateDelegate(typeof(InvokeHandler)); + return invoker; + } + + private static void EmitCastToReference(ILGenerator il, System.Type type) + { + il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type); + } + + private static void EmitBoxIfNeeded(ILGenerator il, System.Type type) + { + if (type.IsValueType) + { + il.Emit(OpCodes.Box, type); + } + } + + private static void EmitFastInt(ILGenerator il, int value) + { + switch (value) + { + case -1: + il.Emit(OpCodes.Ldc_I4_M1); + return; + case 0: + il.Emit(OpCodes.Ldc_I4_0); + return; + case 1: + il.Emit(OpCodes.Ldc_I4_1); + return; + case 2: + il.Emit(OpCodes.Ldc_I4_2); + return; + case 3: + il.Emit(OpCodes.Ldc_I4_3); + return; + case 4: + il.Emit(OpCodes.Ldc_I4_4); + return; + case 5: + il.Emit(OpCodes.Ldc_I4_5); + return; + case 6: + il.Emit(OpCodes.Ldc_I4_6); + return; + case 7: + il.Emit(OpCodes.Ldc_I4_7); + return; + case 8: + il.Emit(OpCodes.Ldc_I4_8); + return; + } + + if (value > -129 && value < 128) + { + il.Emit(OpCodes.Ldc_I4_S, (SByte)value); + } + else + { + il.Emit(OpCodes.Ldc_I4, value); + } + } + + + } + + + + + + +}