Skip to content
This repository has been archived by the owner on Jun 16, 2022. It is now read-only.

Commit

Permalink
Work on speeding up method invocation.
Browse files Browse the repository at this point in the history
  • Loading branch information
DJGosnell committed Jul 27, 2017
1 parent e9abfc1 commit b67bc9f
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/DtronixMessageQueue/DtronixMessageQueue.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
<Compile Include="Rpc\RpcServer.cs" />
<Compile Include="Rpc\RpcSession.cs" />
<Compile Include="Rpc\SerializationCache.cs" />
<Compile Include="Rpc\ServiceMethodCache.cs" />
<Compile Include="Socket\BufferManager.cs" />
<Compile Include="Socket\ISetupSocketSession.cs" />
<Compile Include="Socket\SessionEventArgs.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ public class RpcCallMessageHandler<TSession, TConfig> : MessageHandler<TSession,
/// <summary>
/// Contains all services that can be remotely executed on this session.
/// </summary>
public readonly Dictionary<string, IRemoteService<TSession, TConfig>> Services =
private readonly Dictionary<string, IRemoteService<TSession, TConfig>> _services =
new Dictionary<string, IRemoteService<TSession, TConfig>>();



/// <summary>
/// Proxy objects to be invoked on this session and proxied to the recipient session.
/// </summary>
Expand All @@ -50,6 +52,17 @@ public RpcCallMessageHandler(TSession session) : base(session)
Handlers.Add((byte) RpcCallMessageAction.MethodReturn, ProcessRpcReturnAction);
}

public void AddService<T>(T instance) where T : IRemoteService<TSession, TConfig>
{
_services.Add(instance.Name, instance);
var methods = instance.GetType().GetMethods();
}

/// <summary>
/// Cancels the specified action.
/// </summary>
/// <param name="actionId">byte associated with the RpcCallMessageAction enum.></param>
/// <param name="message">Message containing the cancellation information.</param>
public void MethodCancelAction(byte actionId, MqMessage message)
{
var cancellationId = message[0].ReadUInt16(0);
Expand Down Expand Up @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions src/DtronixMessageQueue/Rpc/RpcRemoteService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace DtronixMessageQueue.Rpc
{
/// <summary>
/// Represents a remote service accessible through a RpcProxy object.
/// </summary>
/// <typeparam name="TSession">Session type.</typeparam>
/// <typeparam name="TConfig">Configuration type.</typeparam>
public abstract class RpcRemoteService<TSession, TConfig>
where TSession : RpcSession<TSession, TConfig>, new()
where TConfig : RpcConfig
{
/// <summary>
/// Name of this service.
/// </summary>
public abstract string Name { get; }

/// <summary>
/// Session for this service instance.
/// </summary>
public TSession Session { get; set; }
}
}
4 changes: 2 additions & 2 deletions src/DtronixMessageQueue/Rpc/RpcSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,8 @@ public T GetProxy<T>() where T : IRemoteService<TSession, TConfig>
/// <param name="instance">Instance to execute methods on.</param>
public void AddService<T>(T instance) where T : IRemoteService<TSession, TConfig>
{
RpcCallHandler.Services.Add(instance.Name, instance);
instance.Session = (TSession) this;
instance.Session = (TSession)this;
RpcCallHandler.AddService(instance);
}
}
}
199 changes: 199 additions & 0 deletions src/DtronixMessageQueue/Rpc/ServiceMethodCache.cs
Original file line number Diff line number Diff line change
@@ -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<TSession, TConfig>
where TSession : RpcSession<TSession, TConfig>, new()
where TConfig : RpcConfig
{

private delegate object InvokeHandler(object target, object[] paramters);


private Dictionary<string, Dictionary<string, ServiceMethodInfo>> _serviceMethods =
new Dictionary<string, Dictionary<string, ServiceMethodInfo>>();

public ServiceMethodCache()
{

}

public void AddService<T>(T instance) where T : IRemoteService<TSession, TConfig>
{
// 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;
}

/// <summary>
/// Black magic box to create IL code invoker.
/// </summary>
/// <param name="methodInfo">Method to create an invoker for.</param>
/// <returns>Invoker delegate crafted for the specified method.</returns>
/// <remarks>
/// https://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker?msg=1555655#xx1555655xx
/// </remarks>
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);
}
}


}






}

0 comments on commit b67bc9f

Please sign in to comment.