我试图比较在 C# 中将委托(delegate)传递给函数的三种不同方式——通过 lambda、通过委托(delegate)和通过直接引用。真正让我吃惊的是直接引用方法(即 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
。您创建的其他委托(delegate)是为了引用非虚函数(快速),它直接调用虚函数(也快)。修改生成的 IL 改变时可以看出是这个原因
ldelem.ref
dup
ldvirtftn instance string object::ToString()
到
ldelem.ref
ldftn instance string object::ToString()
它总是引用 object.ToString
,从不是覆盖函数。这三种方法都需要大约相同的时间。
更新:一个额外的方法,直接绑定(bind)到 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();
}
}
也给出了与其他非虚拟委托(delegate)大致相同的时间。
关于c# - 为什么不同的传委托(delegate)方式在性能上会有如此大的差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12063123/