当前位置:  开发笔记 > 编程语言 > 正文

什么是更快,打开字符串或elseif类型?

如何解决《什么是更快,打开字符串或elseif类型?》经验,为你挑选了5个好方法。

假设我可以选择在字符串比较的基础上识别代码路径,或者如果确定类型:

哪个更快,为什么?

switch(childNode.Name)
{
    case "Bob":
      break;
    case "Jill":
      break;
    case "Marko":
      break;
}

if(childNode is Bob)
{
}
elseif(childNode is Jill)
{
}
else if(childNode is Marko)
{
}

更新:我问这个问题的主要原因是因为switch语句对于什么算是一个特例.例如,它不允许您使用变量,只允许移动到主程序集的常量.我认为它有这个限制,因为它正在做一些时髦的东西.如果它只是翻译成elseifs(作为一张海报评论)那么为什么我们不允许在case语句中使用变量?

警告:我是后期优化的.这种方法被称为许多在应用程序的缓慢一部分倍.



1> 小智..:

Greg的配置文件结果非常适合他所涉及的确切场景,但有趣的是,当考虑许多不同的因素(包括被比较的类型的数量)以及基础数据中的相对频率和任何模式时,不同方法的相对成本会发生显着变化. .

简单的答案是,没有人能够告诉您在特定情况下的性能差异,您需要在自己的系统中以不同的方式测量性能,以获得准确的答案.

If/Else链是少数类型比较的有效方法,或者如果您可以可靠地预测哪些类型将构成您看到的大部分类型.该方法的潜在问题是随着类型数量的增加,必须执行的比较次数也会增加.

如果我执行以下内容:

int value = 25124;
if(value == 0) ...
else if (value == 1) ...
else if (value == 2) ...
...
else if (value == 25124) ... 

必须在输入正确的块之前评估每个先前的if条件.另一方面

switch(value) {
 case 0:...break;
 case 1:...break;
 case 2:...break;
 ...
 case 25124:...break;
}

将执行一次简单的跳转到正确的代码位.

在你的例子中它变得更复杂的是你的另一个方法使用字符串而不是整数的开关,这会变得更复杂一些.在较低级别,字符串无法以与整数值相同的方式打开,因此C#编译器可以为您提供一些魔力.

如果switch语句"足够小"(编译器执行它认为最好的自动),则切换字符串会生成与if/else链相同的代码.

switch(someString) {
    case "Foo": DoFoo(); break;
    case "Bar": DoBar(); break;
    default: DoOther; break;
}

是相同的:

if(someString == "Foo") {
    DoFoo();
} else if(someString == "Bar") {
    DoBar();
} else {
    DoOther();
}

一旦字典中的项列表变得"足够大",编译器将自动创建一个内部字典,该字典从交换机中的字符串映射到整数索引,然后基于该索引切换.

它看起来像这样(想象更多的条目比我打扰打字)

静态字段在"隐藏"位置中定义,该位置与包含类型的switch语句的类相关联,Dictionary并给出错位名称

//Make sure the dictionary is loaded
if(theDictionary == null) { 
    //This is simplified for clarity, the actual implementation is more complex 
    // in order to ensure thread safety
    theDictionary = new Dictionary();
    theDictionary["Foo"] = 0;
    theDictionary["Bar"] = 1;
}

int switchIndex;
if(theDictionary.TryGetValue(someString, out switchIndex)) {
    switch(switchIndex) {
    case 0: DoFoo(); break;
    case 1: DoBar(); break;
    }
} else {
    DoOther();
}

在我刚刚运行的一些快速测试中,If/Else方法的速度是3种不同类型(其中类型随机分布)的开关的3倍.在25种类型中,开关速度较快(16%),50种类型,开关速度快两倍以上.

如果您要打开大量类型,我建议使用第三种方法:

private delegate void NodeHandler(ChildNode node);

static Dictionary TypeHandleSwitcher = CreateSwitcher();

private static Dictionary CreateSwitcher()
{
    var ret = new Dictionary();

    ret[typeof(Bob).TypeHandle] = HandleBob;
    ret[typeof(Jill).TypeHandle] = HandleJill;
    ret[typeof(Marko).TypeHandle] = HandleMarko;

    return ret;
}

void HandleChildNode(ChildNode node)
{
    NodeHandler handler;
    if (TaskHandleSwitcher.TryGetValue(Type.GetRuntimeType(node), out handler))
    {
        handler(node);
    }
    else
    {
        //Unexpected type...
    }
}

这类似于Ted Elliot所建议的,但运行时类型句柄而不是完整类型对象的使用避免了通过反射加载类型对象的开销.

以下是我机器上的一些快速计时:

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 5 types
Method                Time    % of optimal
If/Else               179.67  100.00
TypeHandleDictionary  321.33  178.85
TypeDictionary        377.67  210.20
Switch                492.67  274.21

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 10 types
Method                Time    % of optimal
If/Else               271.33  100.00
TypeHandleDictionary  312.00  114.99
TypeDictionary        374.33  137.96
Switch                490.33  180.71

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 15 types
Method                Time    % of optimal
TypeHandleDictionary  312.00  100.00
If/Else               369.00  118.27
TypeDictionary        371.67  119.12
Switch                491.67  157.59

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 20 types
Method                Time    % of optimal
TypeHandleDictionary  335.33  100.00
TypeDictionary        373.00  111.23
If/Else               462.67  137.97
Switch                490.33  146.22

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 25 types
Method                Time    % of optimal
TypeHandleDictionary  319.33  100.00
TypeDictionary        371.00  116.18
Switch                483.00  151.25
If/Else               562.00  175.99

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 50 types
Method                Time      % of optimal
TypeHandleDictionary  319.67    100.00
TypeDictionary        376.67    117.83
Switch                453.33    141.81
If/Else               1,032.67  323.04

至少在我的机器上,当用作方法输入的类型的分布是随机的时,类型句柄字典方法胜过15种不同类型的所有其他类型.

另一方面,如果输入完全由在if/else链中首先检查的类型组成,那么方法快得多:

Testing 3 iterations with 5,000,000 data elements (mode=UniformFirst) and 50 types
Method                Time    % of optimal
If/Else               39.00   100.00
TypeHandleDictionary  317.33  813.68
TypeDictionary        396.00  1,015.38
Switch                403.00  1,033.33

相反,如果输入始终是if/else链中的最后一个,那么它具有相反的效果:

Testing 3 iterations with 5,000,000 data elements (mode=UniformLast) and 50 types
Method                Time      % of optimal
TypeHandleDictionary  317.67    100.00
Switch                354.33    111.54
TypeDictionary        377.67    118.89
If/Else               1,907.67  600.52

如果您可以对输入做出一些假设,那么您可以从混合方法中获得最佳性能,其中您执行if/else检查最常见的几种类型,然后如果这些方法失败则回退到字典驱动方法.


这可能是我在SO上看到的最好的答案.我的天啊......竖起大拇指!
很棒的答案,+ 1

2> Greg..:

我刚刚实现了一个快速测试应用程序并使用ANTS 4进行了分析.
规范:32位Windows XP中的.Net 3.5 sp1,代码以发布模式构建.

300万次测试:

开关:1.842秒

如果:0.344秒.

此外,switch语句结果显示(不出所料)较长的名称需要更长时间.

100万次测试

鲍勃:0.612秒.

吉尔:0.835秒.

马可:1.093秒.

我看起来像"If Else"更快,至少我创建的场景.

class Program
{
    static void Main( string[] args )
    {
        Bob bob = new Bob();
        Jill jill = new Jill();
        Marko marko = new Marko();

        for( int i = 0; i < 1000000; i++ )
        {
            Test( bob );
            Test( jill );
            Test( marko );
        }
    }

    public static void Test( ChildNode childNode )
    {   
        TestSwitch( childNode );
        TestIfElse( childNode );
    }

    private static void TestIfElse( ChildNode childNode )
    {
        if( childNode is Bob ){}
        else if( childNode is Jill ){}
        else if( childNode is Marko ){}
    }

    private static void TestSwitch( ChildNode childNode )
    {
        switch( childNode.Name )
        {
            case "Bob":
                break;
            case "Jill":
                break;
            case "Marko":
                break;
        }
    }
}

class ChildNode { public string Name { get; set; } }

class Bob : ChildNode { public Bob(){ this.Name = "Bob"; }}

class Jill : ChildNode{public Jill(){this.Name = "Jill";}}

class Marko : ChildNode{public Marko(){this.Name = "Marko";}}


"鲍勃"更快,因为它更短或因为它是第一个?
这很有意义,因为交换机是用字符串完成的,你必须考虑字符串转换开销.如果每个班级都有一个枚举怎么办?

3> ilitirit..:

首先,你要比较苹果和橘子.您首先需要比较开关类型与开启字符串,然后如果在类型vs比较字符串,然后比较获胜者.

其次,这是OO的设计目标.在支持OO的语言中,切换类型(任何类型)都是指代设计不良的代码气味.解决方案是使用抽象或虚拟方法(或类似的构造,取决于您的语言)从公共基础派生

例如.

class Node
{
    public virtual void Action()
    {
        // Perform default action
    }
}

class Bob : Node
{
    public override void Action()
    {
        // Perform action for Bill
    }
}

class Jill : Node
{
    public override void Action()
    {
        // Perform action for Jill
    }
}

然后,您只需调用childNode.Action(),而不是执行switch语句.


(在可读性和可维护性之后)一个有趣的问题是,与其他两种方法相比,它的表现如何.注意:您也可以考虑选择和实例化"Node"实现的部分(例如Factory)的性能.

4> Nescio..:

执行开关语句比if-else-if梯形图更快.这是由于编译器能够优化switch语句.对于if-else-if梯形图,代码必须按程序员确定的顺序处理每个if语句.但是,因为switch语句中的每个case都不依赖于早期的情况,所以编译器能够以提供最快执行的方式重新排序测试.



5> Gary Kephart..:

如果你已经完成了课程,我建议使用策略设计模式而不是switch或elseif.

推荐阅读
落单鸟人
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有