我试图比较三种不同的方法,通过lambda,委托和直接引用将代理传递给C#中的函数.真正让我感到意外的是直接参考方法(即ComputeStringFunctionViaFunc(object [i] .ToString))比其他方法慢六倍.有人知道这是为什么吗?
完整的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.CompilerServices; namespace FunctionInvocationTest { class Program { static void Main(string[] args) { object[] objectArray = new object[10000000]; for (int i = 0; i < objectArray.Length; ++i) { objectArray[i] = new object(); } ComputeStringFunction(objectArray[0]); ComputeStringFunctionViaFunc(objectArray[0].ToString); ComputeStringFunctionViaFunc(delegate() { return objectArray[0].ToString(); }); ComputeStringFunctionViaFunc(() => objectArray[0].ToString()); System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch(); s.Start(); for (int i = 0; i < objectArray.Length; ++i) { ComputeStringFunction(objectArray[i]); } s.Stop(); Console.WriteLine(s.Elapsed.TotalMilliseconds); s.Reset(); s.Start(); for (int i = 0; i < objectArray.Length; ++i) { ComputeStringFunctionViaFunc(delegate() { return objectArray[i].ToString(); }); } s.Stop(); Console.WriteLine(s.Elapsed.TotalMilliseconds); s.Reset(); s.Start(); for (int i = 0; i < objectArray.Length; ++i) { ComputeStringFunctionViaFunc(objectArray[i].ToString); } s.Stop(); Console.WriteLine(s.Elapsed.TotalMilliseconds); s.Reset(); s.Start(); for (int i = 0; i < objectArray.Length; ++i) { ComputeStringFunctionViaFunc(() => objectArray[i].ToString()); } s.Stop(); Console.WriteLine(s.Elapsed.TotalMilliseconds); Console.ReadLine(); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ComputeStringFunction(object stringFunction) { } public static void ComputeStringFunctionViaFunc(Func<string> stringFunction) { } } }
解决方法
固定代码以实际调用ToString()/ stringFunction(),并使用Mono 2.10.9进行测量:
ComputeStringFunctionViaFunc(objectArray [I]的ToString);因为object.ToString是虚拟的,所以很慢.检查每个对象,以防它覆盖ToString,并且应该调用重写的ToString.您的其他代表被创建来引用一个非虚函数(fast),它直接调用一个虚函数(也是fast).当修改生成的IL以更改时,可以看到这是事实的事实
ldelem.ref dup ldvirtftn instance string object::ToString()
至
ldelem.ref ldftn instance string object::ToString()
它总是引用object.ToString,永远不要重写函数.然后,这三种方法都需要大致相同的时间.
更新:一个额外的方法,直接绑定到objectArray [i],但仍然虚拟地调用ToString:
for (int i = 0; i < objectArray.Length; ++i) { ComputeStringFunctionViaFunc(objectArray[i].ToStringHelper); } static class Extensions { public static string ToStringHelper(this object obj) { return obj.ToString(); } }
也给出与其他非虚拟代表大致相同的时间.