C#:虚拟函数调用的速度甚至比委托调用? [英] C#: Virtual Function invocation is even faster than a delegate invocation?

查看:107
本文介绍了C#:虚拟函数调用的速度甚至比委托调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这恰好给我介绍一个代码设计问题。我说,我有一些调用函数,可以改变一模板的方法。一个直观的设计是按照模板设计模式。定义改变功能是在子类中重写虚拟功能。或者,我可以只使用代理功能,而虚。委托功能被注入,使得它们可以被太定制。



原来我以为第二个委托的方式会比虚的方式更快,但一些编码片断证明它是不正确的。



在下面的代码,第一DoSomething的方法遵循模板模式。它呼吁虚方法IsTokenChar。第二DoSomthing方法不依赖于虚拟函数。相反,它具有一通入委托。在我的电脑中,第一DoSomthing总是比第二快。其结果是像1645:1780



虚拟调用是动态绑定,应该更多的时间成本比直接调用委托,对不对?但结果表明,事实并非如此。



任何人都可以解释一下吗?

 使用系统;使用System.Diagnostics程序
;

类Foo
{
公共虚拟BOOL IsTokenChar(字符串字)
{
返回String.IsNullOrEmpty(字);
}

//这是一个模板方法
公众诠释DoSomething的(字符串字)
{
INT trueCount = 0;
的for(int i = 0; I<重复; ++ I)
{
如果(IsTokenChar(字))
{
++ trueCount;
}
}
返回trueCount;
}

公众诠释DoSomething的(谓语<串GT;预估器,串字)
{
INT trueCount = 0;
的for(int i = 0; I<重复; ++ I)
{
如果(谓词(字))
{
++ trueCount;
}
}
返回trueCount;
}

私人INT重复= 2亿;
}

类节目
{
静态无效的主要(字串[] args)
{
美孚F =新的Foo();

{
秒表SW = Stopwatch.StartNew();
f.DoSomething(NULL);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}

{
秒表SW = Stopwatch.StartNew();
f.DoSomething(海峡=> String.IsNullOrEmpty(STR),NULL);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
}


解决方案

想想在每一种情况下需要的:



虚拟呼叫




  • 检查无效

  • 导航从对象指针类型的指针

  • 在指令查找法地址表

  • (不知道 - 即使发生里氏不包括本)进入基本类型,如果方法不重写?递归直到我们找到正确的方法地址。 (我不这么认为 - 见底部的编辑。)

  • 推原始对象的指针到堆栈(本)

  • 调用方法



代表通话




  • 检查无效

  • 导航从对象指针调用数组(所有代表都是潜在的多播)

  • 遍历数组,并为每个调用:

    • 获取方法的地址

    • 出是否不起作用传递目标作为第一个参数

    • 推参数入栈(可能已经完成已经 - 不知道)

    • 可选(取决于调用是打开还是关闭)推调用目标到堆栈

    • 调用方法




有可能是一些优化,使没有循环涉及单一通话情况,但即便如此,这将需要一个很快速检查。



但基本上有同样多的间接涉及的委托。鉴于位我不确定在虚拟方法调用,它可能是一个大规模深类型层次到unoverridden虚拟方法的调用会慢......我给它一个尝试和编辑的答复。



编辑:我试着继承层次结构(最多20个级别),双方的深度打转转,大部分衍生压倒一切,并声明变量类型的点 - 。他们都不似乎有所作为



编辑:我只是试着使用一个接口(这是传递)的原始程序 - 这最终有大约相同的性能委托。


It just happens to me about one code design question. Say, I have one "template" method that invokes some functions that may "alter". A intuitive design is to follow "Template Design Pattern". Define the altering functions to be "virtual" functions to be overridden in subclasses. Or, I can just use delegate functions without "virtual". The delegate functions is injected so that they can be customized too.

Originally, I thought the second "delegate" way would be faster than "virtual" way, but some coding snippet proves it is not correct.

In below code, the first DoSomething method follows "template pattern". It calls on the virtual method IsTokenChar. The second DoSomthing method doesn't depend on virtual function. Instead, it has a pass-in delegate. In my computer, the first DoSomthing is always faster than the second. The result is like 1645:1780.

"Virtual invocation" is dynamic binding and should be more time-costing than direct delegation invocation, right? but the result shows it is not.

Anybody can explain this?

using System;
using System.Diagnostics;

class Foo
{
    public virtual bool IsTokenChar(string word)
    {
        return String.IsNullOrEmpty(word);
    }

    // this is a template method
    public int DoSomething(string word)
    {
        int trueCount = 0;
        for (int i = 0; i < repeat; ++i)
        {
            if (IsTokenChar(word))
            {
                ++trueCount;
            }
        }
        return trueCount;
    }

    public int DoSomething(Predicate<string> predicator, string word)
    {
        int trueCount = 0;
        for (int i = 0; i < repeat; ++i)
        {
            if (predicator(word))
            {
                ++trueCount;
            }
        }
        return trueCount;
    }

    private int repeat = 200000000;
}

class Program
{
    static void Main(string[] args)
    {
        Foo f = new Foo();

        {
            Stopwatch sw = Stopwatch.StartNew();
            f.DoSomething(null);
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }

        {
            Stopwatch sw = Stopwatch.StartNew();
            f.DoSomething(str => String.IsNullOrEmpty(str), null);
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
    }
}

解决方案

Think about what's required in each case:

Virtual call

  • Check for nullity
  • Navigate from object pointer to type pointer
  • Look up method address in instruction table
  • (Not sure - even Richter doesn't cover this) Go to base type if method isn't overridden? Recurse until we find the right method address. (I don't think so - see edit at bottom.)
  • Push original object pointer onto stack ("this")
  • Call method

Delegate call

  • Check for nullity
  • Navigate from object pointer to array of invocations (all delegates are potentially multicast)
  • Loop over array, and for each invocation:
    • Fetch method address
    • Work out whether or not to pass the target as first argument
    • Push arguments onto stack (may have been done already - not sure)
    • Optionally (depending on whether the invocation is open or closed) push the invocation target onto the stack
    • Call method

There may be some optimisation so that there's no looping involved in the single-call case, but even so that will take a very quick check.

But basically there's just as much indirection involved with a delegate. Given the bit I'm unsure of in the virtual method call, it's possible that a call to an unoverridden virtual method in a massively deep type hierarchy would be slower... I'll give it a try and edit with the answer.

EDIT: I've tried playing around with both the depth of inheritance hierarchy (up to 20 levels), the point of "most derived overriding" and the declared variable type - and none of them seems to make a difference.

EDIT: I've just tried the original program using an interface (which is passed in) - that ends up having about the same performance as the delegate.

这篇关于C#:虚拟函数调用的速度甚至比委托调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆