C#中代理的意外性能不佳

我之前发布了关于在C#中动态编​​译代码的问题 ,答案又引出了另一个问题。

一个建议是我使用代表,我尝试过并且它们运行良好。 然而,它们比直接呼叫慢大约8.4 X,这没有任何意义。

这段代码有什么问题?

我的结果,.Net 4.0,64位,直接运行exe: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) Delegate.CreateDelegate(typeof (Func), 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 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()); } 

这说得通。 代表不是函数指针。 它们意味着类型检查,安全性和许多其他东西。 即使性能影响来自完全不同的东西,它们也更接近虚拟函数调用的速度(参见本文 )。

为了更好地比较不同的调用技术(其中一些没有在问题中提到),请阅读本文 。

正如汉斯在关于你的问题的评论中提到的那样, Execute方法非常简单,几乎肯定会被你的“原生”测试中的抖动所勾解。

所以你所看到的不是标准方法调用和委托调用之间的比较,而是内联i * 2操作和委托调用之间的比较。 (并且i * 2操作可能只归结为一条机器指令,大约可以达到最快速度。)

使您的Execute方法更复杂,以防止内联(和/或使用MethodImplOptions.NoInlining编译器提示); 那么你将在标准方法调用和委托调用之间得到更现实的比较。 在大多数情况下,差异可以忽略不计:

 [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 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)Delegate.CreateDelegate(typeof(Func), 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 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); } }