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

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

问题描述

我刚刚遇到一个代码设计问题。说,我有一个模板方法调用一些可能改变的功能。直观的设计是遵循模板设计模式。将更改功能定义为要在子类中覆盖的虚拟函数。或者,我只能使用没有虚拟的委托功能。代理功能被注入,以便它们也可以定制。



最初,我认为第二个委托的方式将比虚拟的方式更快,但是一些编码片段证明是不正确的。



在下面的代码中,第一个DoSomething方法遵循模板模式。它调用虚拟方法IsTokenChar。第二个DoSomthing方法不依赖于虚拟函数。相反,它有一个传入代理。在我的电脑中,第一个DoSomthing总是比第二个更快。结果就像1645:1780。



虚拟调用是动态绑定的,比直接委托调用要花时间更长,对吧?但结果显示不是。



有人可以解释一下吗?

  using System; 
使用System.Diagnostics;

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

//这是一个模板方法
public int DoSomething(string word)
{
int trueCount = 0; (int i = 0; i< repeat; ++ i)
{
if(IsTokenChar(word))
{
++ trueCount;
}
}
return trueCount;
}

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

private int repeat = 200000000;
}

类程序
{
static void Main(string [] args)
{
Foo f = new Foo();

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

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


解决方案

想想在每种情况下需要什么:



虚拟通话




  • 检查无效

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

  • 在指令中查找方法地址表

  • (不确定 - 即使Richter不覆盖)如果方法不被覆盖,请转到基本类型?重复,直到找到正确的方法地址。 (我不这么认为 - 请参阅底部的编辑。)

  • 将原始对象指针推入堆栈(this)

  • 调用方法



代表电话




  • 检查无效

  • 从对象指针导航到调用数组(所有代理都可能是多播)

  • 循环数组,并且对于每个调用:


    • 获取方法地址

    • 确定是否将目标作为第一个参数传递

    • 将参数推入堆栈(可能已经完成 - 不确定)

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

    • 调用方法




可能会有一些优化,因此在单个通话的情况下不会有循环,但即使这样也可以快速检查。



但是基本上有正如亩参与代表的ch间接。给定我在虚拟方法调用中不确定的位置,可能在大量深层类型层次结构中调用未覆盖的虚拟方法会更慢...我会尝试使用答案进行编辑。 / p>

编辑:我已经尝试过继承层次结构的深度(最多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天全站免登陆