I have read several posts on SO about writing and compiling dynamic C# code. For example, this post. I understand it can be done several ways.
However, calling the code invoker is slow. I did a simple benchmark, and it's some 500 X slower than calling a native method.
What I want to be able to do is the equivalent of loading a DLL and calling one of its methods directly ("natively"), which will give the speed benefits I want.
What is the easiest way to go about this? Compile the dynamic code to a dll and then load it? Can it be done in memory?
EDIT
I don't care about compilation time. Only execution.
EDIT 2, 3
Here is the benchmark code I wrote:
public static int Execute(int i) { return i * 2; }
private void button30_Click(object sender, EventArgs e)
{
CSharpCodeProvider foo = new CSharpCodeProvider();
var res = foo.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true,
CompilerOptions = @"/optimize",
},
@"public class FooClass { public static int Execute(int i) { return i * 2; }}"
);
var type = res.CompiledAssembly.GetType("FooClass");
var obj = Activator.CreateInstance(type);
var method = type.GetMethod("Execute");
int i = 0, t1 = Environment.TickCount, t2;
//var input = new object[] { 2 };
//for (int j = 0; j < 10000000; j++)
//{
// input[0] = j;
// var output = method.Invoke(obj, input);
// i = (int)output;
//}
//t2 = Environment.TickCount;
//MessageBox.Show((t2 - t1).ToString() + Environment.NewLine + i.ToString());
t1 = Environment.TickCount;
for (int j = 0; j < 100000000; j++)
{
i = Execute(j);
}
t2 = Environment.TickCount;
MessageBox.Show("Native: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString());
var func = (Func<int, int>) Delegate.CreateDelegate(typeof (Func<int, int>), method);
t1 = Environment.TickCount;
for (int j = 0; j < 100000000; j++)
{
i = func(j);
}
t2 = Environment.TickCount;
MessageBox.Show("Dynamic delegate: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString());
Func<int, int> funcL = Execute;
t1 = Environment.TickCount;
for (int j = 0; j < 100000000; j++)
{
i = funcL(j);
}
t2 = Environment.TickCount;
MessageBox.Show("Delegate: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString());
}
Yes, if you invoke via a MethodInfo
or a non-specific Delegate
, then it will indeed be slow. The trick is: don't do that. Various approaches:
for individual methods, go via a basic but typed delegate, such as Action
, or as a generic catch-all, Func<object[], object>
- and use Delegate.CreateDelegate
to create a typed delegate:
Action doSomething = (Action)Delegate.CreateDelegate(typeof(Action), method);
another variant of this is to use the Expression
API (which has a .Compile()
method), or DynamicMethod
(which has CreateDelegate()
). The key thing: you must get a typed delegate and invoke using typed invoke (not .DynamicInvoke
).
for more complex cases where you are generating whole types, consider implementing an interface you know about, i.e.
IFoo foo = (IFoo)Activator.CreateInstance(...);
again; after the initial cast (which is very cheap) you can just use static code:
foo.Bar();
Do not use someDelegate.DynamicInvoke(...)
or someMethod.Invoke(...)
if you are after any kind of performance.
Besides Marc's advice you could improve speed by specifying the "optimize" compiler option:
var res = foo.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true,
CompilerOptions = "/optimize"
},
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With