I posted this question earlier about dynamically compiling code in C#, and the answer has lead to another question.
One suggestion is that I use delegates, which I tried and they work well. However, they are benching at about 8.4 X slower than direct calls, which makes no sense.
What is wrong with this code?
My results, .Net 4.0, 64 bit, ran exe directly: 62, 514, 530
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());
}
As Hans mentions in the comments on your question, the Execute method is so simple that it's almost certainly being inlined by the jitter in your "native" test.
So what you're seeing isn't a comparison between a standard method call and a delegate invocation, but a comparison between an inlined i * 2 operation and a delegate invocation. (And that i * 2 operation probably boils down to just a single machine instruction, about as fast as you can get.)
Make your Execute methods a bit more complicated to prevent inlining (and/or do it with the MethodImplOptions.NoInlining compiler hint); then you'll get a more realistic comparison between standard method calls and delegate invocations. Chances are that the difference will be negligible in most situations:
[MethodImpl(MethodImplOptions.NoInlining)]
static int Execute(int i) { return ((i / 63.53) == 34.23) ? -1 : (i * 2); }
public static volatile int Result;
private static void Main(string[] args)
{
const int iterations = 100000000;
{
Result = Execute(42); // pre-jit
var s = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
Result = Execute(i);
}
s.Stop();
Console.WriteLine("Native: " + s.ElapsedMilliseconds);
}
{
Func<int, int> func;
using (var cscp = new CSharpCodeProvider())
{
var cp = new CompilerParameters { GenerateInMemory = true, CompilerOptions = @"/optimize" };
string src = @"public static class Foo { public static int Execute(int i) { return ((i / 63.53) == 34.23) ? -1 : (i * 2); } }";
var cr = cscp.CompileAssemblyFromSource(cp, src);
var mi = cr.CompiledAssembly.GetType("Foo").GetMethod("Execute");
func = (Func<int, int>)Delegate.CreateDelegate(typeof(Func<int, int>), mi);
}
Result = func(42); // pre-jit
var s = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
Result = func(i);
}
s.Stop();
Console.WriteLine("Dynamic delegate: " + s.ElapsedMilliseconds);
}
{
Func<int, int> func = Execute;
Result = func(42); // pre-jit
var s = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
Result = func(i);
}
s.Stop();
Console.WriteLine("Delegate: " + s.ElapsedMilliseconds);
}
}
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