首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >调用委托与调用方法的性能

调用委托与调用方法的性能
EN

Stack Overflow用户
提问于 2010-01-18 05:30:41
回答 5查看 60K关注 0票数 72

根据这个问题- Pass Method as Parameter using C#和我的一些个人经验,我想更多地了解在C#中调用委托与只调用方法的性能。

虽然委托非常方便,但我有一个通过委托进行大量回调的应用程序,当我们重写此应用程序以使用回调接口时,速度得到了数量级的提高。这是在.NET 2.0中,所以我不确定在3和4中事情发生了什么变化。

如何在编译器/CLR内部处理对委托的调用?这对方法调用的性能有何影响?

编辑-澄清我所说的委托和回调接口是什么意思。

对于异步调用,我的类可以提供调用者可以订阅的OnComplete事件和相关的委托。

或者,我可以使用调用者实现的OnComplete方法创建一个ICallback接口,然后将自己注册到类中,该类将在完成时调用该方法(即Java处理这些事情的方式)。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-01-18 06:19:59

我还没有看到这种效果--我肯定从来没有遇到过它是一个瓶颈。

这里有一个非常粗略的基准测试,它显示(至少在我的盒子上)委托实际上比接口更快:

代码语言:javascript
复制
using System;
using System.Diagnostics;

interface IFoo
{
    int Foo(int x);
}

class Program : IFoo
{
    const int Iterations = 1000000000;

    public int Foo(int x)
    {
        return x * 3;
    }

    static void Main(string[] args)
    {
        int x = 3;
        IFoo ifoo = new Program();
        Func<int, int> del = ifoo.Foo;
        // Make sure everything's JITted:
        ifoo.Foo(3);
        del(3);

        Stopwatch sw = Stopwatch.StartNew();        
        for (int i = 0; i < Iterations; i++)
        {
            x = ifoo.Foo(x);
        }
        sw.Stop();
        Console.WriteLine("Interface: {0}", sw.ElapsedMilliseconds);

        x = 3;
        sw = Stopwatch.StartNew();        
        for (int i = 0; i < Iterations; i++)
        {
            x = del(x);
        }
        sw.Stop();
        Console.WriteLine("Delegate: {0}", sw.ElapsedMilliseconds);
    }
}

结果(.NET 3.5;.NET 4.0b2大致相同):

代码语言:javascript
复制
Interface: 5068
Delegate: 4404

现在我并不特别相信这意味着委托真的比接口更快……但这让我相当确信,它们的速度不会慢一个数量级。此外,这在委托/接口方法中几乎什么也不做。显然,当你每次调用的工作越来越多时,调用成本将会产生越来越小的影响。

需要注意的一件事是,你不能多次创建一个新的委托,因为你只需要使用一个接口实例。如果你使用一个实例方法作为循环中的委托,你会发现在循环外声明委托变量,创建一个委托实例并重用它会更有效。例如:

代码语言:javascript
复制
Func<int, int> del = myInstance.MyMethod;
for (int i = 0; i < 100000; i++)
{
    MethodTakingFunc(del);
}

效率比:

代码语言:javascript
复制
for (int i = 0; i < 100000; i++)
{
    MethodTakingFunc(myInstance.MyMethod);
}

这会不会就是你看到的问题?

票数 84
EN

Stack Overflow用户

发布于 2010-01-18 06:51:31

我发现委托比虚拟方法快得多或慢得多是完全不可信的。如果说有什么不同的话,那就是代理的速度应该快得可以忽略不计。在较低的级别,委托通常被实现为类似于(使用C风格的表示法,但请原谅任何微小的语法错误,因为这只是一个示例):

代码语言:javascript
复制
struct Delegate {
    void* contextPointer;   // What class instance does this reference?
    void* functionPointer;  // What method does this reference?
}

调用代理的工作原理类似于:

代码语言:javascript
复制
struct Delegate myDelegate = somethingThatReturnsDelegate();
// Call the delegate in de-sugared C-style notation.
ReturnType returnValue = 
    (*((FunctionType) *myDelegate.functionPointer))(myDelegate.contextPointer);

一个类,翻译成C语言,应该是这样的:

代码语言:javascript
复制
struct SomeClass {
    void** vtable;        // Array of pointers to functions.
    SomeType someMember;  // Member variables.
}

要调用vritual函数,请执行以下操作:

代码语言:javascript
复制
struct SomeClass *myClass = someFunctionThatReturnsMyClassPointer();
// Call the virtual function residing in the second slot of the vtable.
void* funcPtr = (myClass -> vtbl)[1];
ReturnType returnValue = (*((FunctionType) funcPtr))(myClass);

它们基本上是相同的,除了在使用虚函数时要经过额外的间接层来获取函数指针。然而,这个额外的间接层通常是空闲的,因为现代CPU分支预测器将猜测函数指针的地址,并在查找函数地址的同时推测地执行其目标。我发现(尽管是在D中,而不是在C#中),紧密循环中的虚函数调用并不比非内联的直接调用慢,前提是对于循环的任何给定运行,它们总是解析到相同的真实函数。

票数 25
EN

Stack Overflow用户

发布于 2010-01-18 06:21:45

从CLR v2开始,委托调用的开销与虚方法调用的开销非常接近,虚方法调用用于接口方法。

请参阅Joel Pobar的博客。

票数 24
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2082735

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档