-
Notifications
You must be signed in to change notification settings - Fork 264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Substitute.For delegate much slower than for interfaces and classes #248
Comments
I'm wondering if the commonly used |
Hi Jamie, |
It spends most of the time in There are faster compilers e.g. https://github.com/dadhi/FastExpressionCompiler but I'm not sure we should touch it at all. What do you think? |
I now have a workaround that lets me substitute an interface rather than a delegate (which is ~80 times faster). It looks something like this: static Delegate createDelegateFor(Type delegateType)
{
var dele = quickCreateDelegateFor(delegateType);
if(dele != null)
{
return dele;
}
return (Delegate)Substitute.For(new Type[] { delegateType }, new object[0]);
}
// TODO: Add support for all Actions and Funcs.
static Delegate quickCreateDelegateFor(Type delegateType)
{
Type type;
switch (delegateType.Name)
{
case "Func`1":
type = typeof(IFunc<>).MakeGenericType(delegateType.GenericTypeArguments);
break;
case "Func`2":
type = typeof(IFunc<,>).MakeGenericType(delegateType.GenericTypeArguments);
break;
default:
return null;
}
var target = Substitute.For(new Type[] { type }, new object[0]);
var invokeMethod = type.GetTypeInfo().GetMethod("Invoke");
return invokeMethod.CreateDelegate(delegateType, target);
}
public interface IFunc<R> { R Invoke(); }
public interface IFunc<T, R> { R Invoke(T t); } I'm guessing there may be dragons that I'm missing, but this seems to work for my purposes. 😄 |
I've investigated this issue a bit and basically I see two options:
The second option is probably more lightweight, however it will force to have a mess in our code, as it's non-supportable. Also I'm not sure that the emitted code will be 100% valid (so it doesn't fail on some Mono environment), as sometimes opcodes are very specific depending on the operand type (e.g. different opcode is used to read Therefore, the safest way (and the preferable one) would be to use the @alexandrnikitin @dtchepak What do you think? 😉 |
@zvirja Sounds good 👍 |
Previously delegates were generated using the expressions, which proxied all arguments to ICallRouter and later handled the result. I've found that there is a better way - we can use the standard Castle intercepting feature. The idea is that our delegate proxy could be a regular delegate over the Caslte proxy's method. That allowed to significantly improve the performance, as expression compilation is pretty expensive. Workflow: 1. Generate an interface for the requested delegate type, so it contains the "Invoke" method with the required signature. 2. Ask Castle Proxy to generate a proxy for that interface. 3. Create a delegate from the "Invoke" method of the generated proxy. It appears that this change is almost transparent for the engine and works perfectly. Fixes #248.
First, congratulations on a very slick project. :)
I've been doing some performance tests using BenchmarkDotNet and noticed that substituting delegates is much slower than for other types (something like 80 times slower). I wondered if you know why this might be and if it could be improved?
Here are the results and benchmarking code:
The text was updated successfully, but these errors were encountered: