c# - 为什么不同的传委托(delegate)方式在性能上会有如此大的差异?

标签 c# .net performance delegates

我试图比较在 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/

相关文章:

c# - 是否可以将一个表格放在另一个表格之上,而不是 TopMost?

performance - 在 MsBuild 中启用/禁用序列化程序集的生成

performance - Java 8流产生的代码是否比普通命令式循环慢?

C# “does not contain a constructor that takes ' 1' arguments”

c# - IQueryable where 子句

c# - 带 IoC 的工厂隔离模式

python - 加载 txt 文件时跳过行

c# - 让 while 循环在 C# 中为 HP 游戏工作

c# - 如何将 JSON 数组存储到动态列表<>?

c# - 为什么此后台线程中未处理的异常不会终止我的进程?