79

假设我可以选择根据字符串比较或判断类型来识别要采用的代码路径:

哪个更快,为什么?

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 语句中使用变量?

警告:我正在优化后。此方法在应用程序的慢速部分被多次调用。

4

23 回答 23

143

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<string, int>并给定一个错位名称

//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<string,int>();
    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 种不同类型(其中类型随机分布)的 switch 的 3 倍。在 25 种类型中,切换速度稍快(16%),在 50 种类型中,切换速度是原来的两倍多。

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

private delegate void NodeHandler(ChildNode node);

static Dictionary<RuntimeTypeHandle, NodeHandler> TypeHandleSwitcher = CreateSwitcher();

private static Dictionary<RuntimeTypeHandle, NodeHandler> CreateSwitcher()
{
    var ret = new Dictionary<RuntimeTypeHandle, NodeHandler>();

    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 建议的类似,但使用运行时类型句柄而不是完整类型对象避免了通过反射加载类型对象的开销。

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

使用 5,000,000 个数据元素(模式=随机)和 5 种类型测试 3 次迭代
最佳方法时间百分比
如果/否则 179.67 100.00
类型句柄字典 321.33 178.85
类型字典 377.67 210.20
开关 492.67 274.21

使用 5,000,000 个数据元素(模式=随机)和 10 种类型测试 3 次迭代
最佳方法时间百分比
如果/否则 271.33 100.00
类型句柄字典 312.00 114.99
类型字典 374.33 137.96
开关 490.33 180.71

使用 5,000,000 个数据元素(模式=随机)和 15 种类型测试 3 次迭代
最佳方法时间百分比
类型句柄字典 312.00 100.00
如果/否则 369.00 118.27
类型字典 371.67 119.12
开关 491.67 157.59

使用 5,000,000 个数据元素(模式=随机)和 20 种类型测试 3 次迭代
最佳方法时间百分比
类型句柄字典 335.33 100.00
类型字典 373.00 111.23
如果/否则 462.67 137.97
开关 490.33 146.22

使用 5,000,000 个数据元素(模式=随机)和 25 种类型测试 3 次迭代
最佳方法时间百分比
类型句柄字典 319.33 100.00
类型字典 371.00 116.18
开关 483.00 151.25
如果/否则 562.00 175.99

使用 5,000,000 个数据元素(模式=随机)和 50 种类型测试 3 次迭代
最佳方法时间百分比
类型句柄字典 319.67 100.00
类型字典 376.67 117.83
开关 453.33 141.81
如果/否则 1,032.67 323.04

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

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

使用 5,000,000 个数据元素(模式=UniformFirst)和 50 种类型测试 3 次迭代
最佳方法时间百分比
如果/否则 39.00 100.00
类型句柄字典 317.33 813.68
类型字典 396.00 1,015.38
开关 403.00 1,033.33

相反,如果输入始终是 if/else 链中的最后一件事,则会产生相反的效果:

使用 5,000,000 个数据元素(mode=UniformLast)和 50 种类型测试 3 次迭代
最佳方法时间百分比
类型句柄字典 317.67 100.00
开关 354.33 111.54
类型字典 377.67 118.89
如果/否则 1,907.67 600.52

如果您可以对您的输入做出一些假设,您可能会从混合方法中获得最佳性能,在该方法中您对最常见的少数类型执行 if/else 检查,如果这些方法失败,则回退到字典驱动的方法。

于 2008-09-24T11:03:59.770 回答
19

首先,您在比较苹果和橙子。您首先需要比较 switch on type 与 switch on string,然后比较 if on type 与 if on string,然后比较获胜者。

其次,这就是面向对象的设计目的。在支持 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 语句

于 2008-09-18T18:10:36.997 回答
18

我刚刚实现了一个快速测试应用程序并使用 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";}}
于 2008-09-18T17:59:55.993 回答
12

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

于 2008-09-18T16:55:55.147 回答
6

如果你已经制作了类,我建议使用策略设计模式而不是 switch 或 elseif。

于 2008-09-18T16:58:49.307 回答
4

尝试对每个对象使用枚举,您可以快速轻松地打开枚举。

于 2008-09-18T18:01:46.190 回答
3

除非你已经写了这个并且发现你有性能问题,否则我不会担心哪个更快。选择更具可读性的那个。请记住,“过早的优化是万恶之源。” ——唐纳德·克努斯

于 2008-09-18T16:45:16.483 回答
3

SWITCH 构造最初是为整数数据设计的;它的目的是将参数直接用作“调度表”(指针表)的索引。因此,将有一个测试,然后直接启动到相关代码,而不是一系列测试。

这里的困难在于它的使用已被推广到“字符串”类型,这显然不能用作索引,并且 SWITCH 构造的所有优点都丢失了。

如果速度是您的预期目标,那么问题不在于您的代码,而在于您的数据结构。如果“名称”空间像您展示的那样简单,最好将其编码为整数值(例如,在创建数据时),并在“应用程序的慢速部分多次”中使用此整数。

于 2008-09-18T17:25:24.017 回答
3

如果您打开的类型是原始 .NET 类型,您可以使用 Type.GetTypeCode(Type),但如果它们是自定义类型,它们都将以 TypeCode.Object 的形式返回。

带有委托或处理程序类的字典也可以工作。

Dictionary<Type, HandlerDelegate> handlers = new Dictionary<Type, HandlerDelegate>();
handlers[typeof(Bob)] = this.HandleBob;
handlers[typeof(Jill)] = this.HandleJill;
handlers[typeof(Marko)] = this.HandleMarko;

handlers[childNode.GetType()](childNode);
/// ...

private void HandleBob(Node childNode) {
    // code to handle Bob
}
于 2008-09-18T17:31:16.903 回答
2

switch() 将编译为等效于一组 else if 的代码。字符串比较将比类型比较慢得多。

于 2008-09-18T16:43:58.717 回答
2

我记得在几本参考书中读到 if/else 分支比 switch 语句更快。但是,对 Blackwasp 的一些研究表明 switch 语句实际上更快: http: //www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

实际上,如果您将典型的 3 到 10 个(左右)语句进行比较,我严重怀疑使用其中一个或另一个会带来任何真正的性能提升。

正如 Chris 已经说过的,追求可读性: 什么更快,打开字符串或 elseif 类型?

于 2008-09-18T17:04:07.843 回答
2

我认为这里的主要性能问题是,在 switch 块中,你比较字符串,而在 if-else 块中,你检查类型......这两个不一样,因此,我会说你'正在“将土豆与香蕉进行比较”。

我先比较一下:

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

if(childNode.Name == "Bob")
{}
else if(childNode.Name == "Jill")
{}
else if(childNode.Name == "Marko")
{}
于 2008-09-18T17:20:50.190 回答
2

我不确定正确的设计在多态性方面的速度有多快。

interface INode
{
    void Action;
}

class Bob : INode
{
    public void Action
    {

    }
}

class Jill : INode
{
    public void Action
    {

    }
}

class Marko : INode
{
    public void Action
    {

    }
}

//Your function:
void Do(INode childNode)
{
    childNode.Action();
}

看看你的 switch 语句做了什么会更好。如果您的函数实际上与类型上的操作无关,那么您可能可以在每种类型上定义一个枚举。

enum NodeType { Bob, Jill, Marko, Default }

interface INode
{
    NodeType Node { get; };
}

class Bob : INode
{
    public NodeType Node { get { return NodeType.Bob; } }
}

class Jill : INode
{
    public NodeType Node { get { return NodeType.Jill; } }
}

class Marko : INode
{
    public NodeType Node { get { return NodeType.Marko; } }
}

//Your function:
void Do(INode childNode)
{
    switch(childNode.Node)
    {
        case Bob:
          break;
        case Jill:
          break;
        case Marko:
          break;
        Default:
          throw new ArgumentException();
    }
}

我认为这必须比这两种方法都快。如果纳秒对您很重要,您可能想尝试抽象类路由。

于 2013-01-03T02:06:43.927 回答
2

我创建了一个小控制台来展示我的解决方案,只是为了突出速度差异。我使用了不同的字符串哈希算法,因为证书版本在运行时对我来说会变慢,并且不太可能出现重复,如果是这样,我的 switch 语句将失败(直到现在从未发生过)。我独特的哈希扩展方法包含在下面的代码中。

带有输出的 Core 2 控制台应用程序

任何时候我都会在 695 滴答声中取 29 个滴答声,特别是在使用关键代码时。

使用来自给定数据库的一组字符串,您可以创建一个小型应用程序来在给定文件中创建常量以供您在代码中使用,如果添加了值,您只需重新运行批处理并生成常量并由解决方案。

  public static class StringExtention
    {
        public static long ToUniqueHash(this string text)
        {
            long value = 0;
            var array = text.ToCharArray();
            unchecked
            {
                for (int i = 0; i < array.Length; i++)
                {
                    value = (value * 397) ^ array[i].GetHashCode();
                    value = (value * 397) ^ i;
                }
                return value;
            }
        }
    }

    public class AccountTypes
    {

        static void Main()
        {
            var sb = new StringBuilder();

            sb.AppendLine($"const long ACCOUNT_TYPE = {"AccountType".ToUniqueHash()};");
            sb.AppendLine($"const long NET_LIQUIDATION = {"NetLiquidation".ToUniqueHash()};");
            sb.AppendLine($"const long TOTAL_CASH_VALUE = {"TotalCashValue".ToUniqueHash()};");
            sb.AppendLine($"const long SETTLED_CASH = {"SettledCash".ToUniqueHash()};");
            sb.AppendLine($"const long ACCRUED_CASH = {"AccruedCash".ToUniqueHash()};");
            sb.AppendLine($"const long BUYING_POWER = {"BuyingPower".ToUniqueHash()};");
            sb.AppendLine($"const long EQUITY_WITH_LOAN_VALUE = {"EquityWithLoanValue".ToUniqueHash()};");
            sb.AppendLine($"const long PREVIOUS_EQUITY_WITH_LOAN_VALUE = {"PreviousEquityWithLoanValue".ToUniqueHash()};");
            sb.AppendLine($"const long GROSS_POSITION_VALUE ={ "GrossPositionValue".ToUniqueHash()};");
            sb.AppendLine($"const long REQT_EQUITY = {"ReqTEquity".ToUniqueHash()};");
            sb.AppendLine($"const long REQT_MARGIN = {"ReqTMargin".ToUniqueHash()};");
            sb.AppendLine($"const long SPECIAL_MEMORANDUM_ACCOUNT = {"SMA".ToUniqueHash()};");
            sb.AppendLine($"const long INIT_MARGIN_REQ = { "InitMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long MAINT_MARGIN_REQ = {"MaintMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long AVAILABLE_FUNDS = {"AvailableFunds".ToUniqueHash()};");
            sb.AppendLine($"const long EXCESS_LIQUIDITY = {"ExcessLiquidity".ToUniqueHash()};");
            sb.AppendLine($"const long CUSHION = {"Cushion".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_INIT_MARGIN_REQ = {"FullInitMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_MAINTMARGIN_REQ ={ "FullMaintMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_AVAILABLE_FUNDS = {"FullAvailableFunds".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_EXCESS_LIQUIDITY ={ "FullExcessLiquidity".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_INIT_MARGIN_REQ = {"LookAheadInitMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_MAINT_MARGIN_REQ = {"LookAheadMaintMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_AVAILABLE_FUNDS = {"LookAheadAvailableFunds".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_EXCESS_LIQUIDITY = {"LookAheadExcessLiquidity".ToUniqueHash()};");
            sb.AppendLine($"const long HIGHEST_SEVERITY = {"HighestSeverity".ToUniqueHash()};");
            sb.AppendLine($"const long DAY_TRADES_REMAINING = {"DayTradesRemaining".ToUniqueHash()};");
            sb.AppendLine($"const long LEVERAGE = {"Leverage".ToUniqueHash()};");
            Console.WriteLine(sb.ToString());

            Test();    
        }    

        public static void Test()
        {
            //generated constant values
            const long ACCOUNT_TYPE = -3012481629590703298;
            const long NET_LIQUIDATION = 5886477638280951639;
            const long TOTAL_CASH_VALUE = 2715174589598334721;
            const long SETTLED_CASH = 9013818865418133625;
            const long ACCRUED_CASH = -1095823472425902515;
            const long BUYING_POWER = -4447052054809609098;
            const long EQUITY_WITH_LOAN_VALUE = -4088154623329785565;
            const long PREVIOUS_EQUITY_WITH_LOAN_VALUE = 6224054330592996694;
            const long GROSS_POSITION_VALUE = -7316842993788269735;
            const long REQT_EQUITY = -7457439202928979430;
            const long REQT_MARGIN = -7525806483981945115;
            const long SPECIAL_MEMORANDUM_ACCOUNT = -1696406879233404584;
            const long INIT_MARGIN_REQ = 4495254338330797326;
            const long MAINT_MARGIN_REQ = 3923858659879350034;
            const long AVAILABLE_FUNDS = 2736927433442081110;
            const long EXCESS_LIQUIDITY = 5975045739561521360;
            const long CUSHION = 5079153439662500166;
            const long FULL_INIT_MARGIN_REQ = -6446443340724968443;
            const long FULL_MAINTMARGIN_REQ = -8084126626285123011;
            const long FULL_AVAILABLE_FUNDS = 1594040062751632873;
            const long FULL_EXCESS_LIQUIDITY = -2360941491690082189;
            const long LOOK_AHEAD_INIT_MARGIN_REQ = 5230305572167766821;
            const long LOOK_AHEAD_MAINT_MARGIN_REQ = 4895875570930256738;
            const long LOOK_AHEAD_AVAILABLE_FUNDS = -7687608210548571554;
            const long LOOK_AHEAD_EXCESS_LIQUIDITY = -4299898188451362207;
            const long HIGHEST_SEVERITY = 5831097798646393988;
            const long DAY_TRADES_REMAINING = 3899479916235857560;
            const long LEVERAGE = 1018053116254258495;

            bool found = false;
            var sValues = new string[] {
              "AccountType"
              ,"NetLiquidation"
              ,"TotalCashValue"
              ,"SettledCash"
              ,"AccruedCash"
              ,"BuyingPower"
              ,"EquityWithLoanValue"
              ,"PreviousEquityWithLoanValue"
              ,"GrossPositionValue"
              ,"ReqTEquity"
              ,"ReqTMargin"
              ,"SMA"
              ,"InitMarginReq"
              ,"MaintMarginReq"
              ,"AvailableFunds"
              ,"ExcessLiquidity"
              ,"Cushion"
              ,"FullInitMarginReq"
              ,"FullMaintMarginReq"
              ,"FullAvailableFunds"
              ,"FullExcessLiquidity"
              ,"LookAheadInitMarginReq"
              ,"LookAheadMaintMarginReq"
              ,"LookAheadAvailableFunds"
              ,"LookAheadExcessLiquidity"
              ,"HighestSeverity"
              ,"DayTradesRemaining"
              ,"Leverage"
            };

            long t1, t2;
            var sw = System.Diagnostics.Stopwatch.StartNew();
            foreach (var name in sValues)
            {
                switch (name)
                {
                    case "AccountType": found = true; break;
                    case "NetLiquidation": found = true; break;
                    case "TotalCashValue": found = true; break;
                    case "SettledCash": found = true; break;
                    case "AccruedCash": found = true; break;
                    case "BuyingPower": found = true; break;
                    case "EquityWithLoanValue": found = true; break;
                    case "PreviousEquityWithLoanValue": found = true; break;
                    case "GrossPositionValue": found = true; break;
                    case "ReqTEquity": found = true; break;
                    case "ReqTMargin": found = true; break;
                    case "SMA": found = true; break;
                    case "InitMarginReq": found = true; break;
                    case "MaintMarginReq": found = true; break;
                    case "AvailableFunds": found = true; break;
                    case "ExcessLiquidity": found = true; break;
                    case "Cushion": found = true; break;
                    case "FullInitMarginReq": found = true; break;
                    case "FullMaintMarginReq": found = true; break;
                    case "FullAvailableFunds": found = true; break;
                    case "FullExcessLiquidity": found = true; break;
                    case "LookAheadInitMarginReq": found = true; break;
                    case "LookAheadMaintMarginReq": found = true; break;
                    case "LookAheadAvailableFunds": found = true; break;
                    case "LookAheadExcessLiquidity": found = true; break;
                    case "HighestSeverity": found = true; break;
                    case "DayTradesRemaining": found = true; break;
                    case "Leverage": found = true; break;
                    default: found = false; break;
                }

                if (!found)
                    throw new NotImplementedException();
            }
            t1 = sw.ElapsedTicks;
            sw.Restart();
            foreach (var name in sValues)
            {
                switch (name.ToUniqueHash())
                {
                    case ACCOUNT_TYPE:
                        found = true;
                        break;
                    case NET_LIQUIDATION:
                        found = true;
                        break;
                    case TOTAL_CASH_VALUE:
                        found = true;
                        break;
                    case SETTLED_CASH:
                        found = true;
                        break;
                    case ACCRUED_CASH:
                        found = true;
                        break;
                    case BUYING_POWER:
                        found = true;
                        break;
                    case EQUITY_WITH_LOAN_VALUE:
                        found = true;
                        break;
                    case PREVIOUS_EQUITY_WITH_LOAN_VALUE:
                        found = true;
                        break;
                    case GROSS_POSITION_VALUE:
                        found = true;
                        break;
                    case REQT_EQUITY:
                        found = true;
                        break;
                    case REQT_MARGIN:
                        found = true;
                        break;
                    case SPECIAL_MEMORANDUM_ACCOUNT:
                        found = true;
                        break;
                    case INIT_MARGIN_REQ:
                        found = true;
                        break;
                    case MAINT_MARGIN_REQ:
                        found = true;
                        break;
                    case AVAILABLE_FUNDS:
                        found = true;
                        break;
                    case EXCESS_LIQUIDITY:
                        found = true;
                        break;
                    case CUSHION:
                        found = true;
                        break;
                    case FULL_INIT_MARGIN_REQ:
                        found = true;
                        break;
                    case FULL_MAINTMARGIN_REQ:
                        found = true;
                        break;
                    case FULL_AVAILABLE_FUNDS:
                        found = true;
                        break;
                    case FULL_EXCESS_LIQUIDITY:
                        found = true;
                        break;
                    case LOOK_AHEAD_INIT_MARGIN_REQ:
                        found = true;
                        break;
                    case LOOK_AHEAD_MAINT_MARGIN_REQ:
                        found = true;
                        break;
                    case LOOK_AHEAD_AVAILABLE_FUNDS:
                        found = true;
                        break;
                    case LOOK_AHEAD_EXCESS_LIQUIDITY:
                        found = true;
                        break;
                    case HIGHEST_SEVERITY:
                        found = true;
                        break;
                    case DAY_TRADES_REMAINING:
                        found = true;
                        break;
                    case LEVERAGE:
                        found = true;
                        break;
                    default:
                        found = false;
                        break;
                }

                if (!found)
                    throw new NotImplementedException();
            }
            t2 = sw.ElapsedTicks;
            sw.Stop();
            Console.WriteLine($"String switch:{t1:N0} long switch:{t2:N0}");
            var faster = (t1 > t2) ? "Slower" : "faster";
            Console.WriteLine($"String switch: is {faster} than long switch: by {Math.Abs(t1-t2)} Ticks");
            Console.ReadLine();

        }
于 2018-01-27T11:11:55.343 回答
0

字符串比较将始终完全依赖于运行时环境(除非字符串是静态分配的,尽管需要将它们相互比较是有争议的)。然而,类型比较可以通过动态或静态绑定来完成,无论哪种方式,对于运行时环境来说,它都比比较字符串中的单个字符更有效。

于 2008-09-18T16:46:20.257 回答
0

当然,String 上的开关会编译为字符串比较(每种情况一个),它比类型比较慢(并且比用于 switch/case 的典型整数比较慢得多)?

于 2008-09-18T16:47:53.640 回答
0

开关的问题之一是使用字符串,例如“Bob”,这将导致编译代码中出现更多的循环和行。生成的 IL 必须声明一个字符串,将其设置为“Bob”,然后在比较中使用它。因此,考虑到这一点,您的 IF 语句将运行得更快。

PS。Aeon 的示例不起作用,因为您无法打开类型。(不,我不知道究竟是为什么,但我们已经尝试过了,但它不起作用。它与变量类型有关)

如果您想对此进行测试,只需构建一个单独的应用程序并构建两个简单的方法来执行上面所写的操作,并使用 Ildasm.exe 之类的东西来查看 IL。您会注意到 IF 语句 Method 的 IL 中的行数要少得多。

Ildasm 带有 VisualStudio...

ILDASM 页面 - http://msdn.microsoft.com/en-us/library/f7dy01k1(VS.80).aspx

ILDASM 教程 - http://msdn.microsoft.com/en-us/library/aa309387(VS.71).aspx

于 2008-09-18T17:21:32.217 回答
0

三个想法:

1)如果您要根据对象的类型做一些不同的事情,那么将这种行为移到这些类中可能是有意义的。然后,您只需调用 childNode.DoSomething(),而不是 switch 或 if-else。

2)比较类型将比字符串比较快得多。

3) 在 if-else 设计中,您可以利用重新排序测试的优势。如果“吉尔”对象占通过那里的对象的 90%,请先对其进行测试。

于 2008-09-18T17:50:21.833 回答
0

请记住,分析器是您的朋友。大多数时候,任何猜测都是浪费时间。顺便说一句,我对 JetBrains 的dotTrace分析器有很好的体验。

于 2008-09-18T18:07:37.177 回答
0

Switch on string 基本上被编译成一个 if-else-if 梯形图。尝试反编译一个简单的。在任何情况下,测试 string equalty 应该更便宜,因为它们是被实习的,所需要的只是参考检查。在可维护性方面做有意义的事情;如果您正在压缩字符串,请进行字符串切换。如果您根据类型进行选择,则类型阶梯更为合适。

于 2008-09-18T18:08:05.600 回答
0

我有点不同,你打开的字符串将是常量,所以你可以在编译时预测值。

在您的情况下,我将使用哈希值,这是一个 int 开关,您有 2 个选项,使用编译时常量或在运行时计算。

//somewhere in your code
static long _bob = "Bob".GetUniqueHashCode();
static long _jill = "Jill".GetUniqueHashCode();
static long _marko = "Marko".GeUniquetHashCode();

void MyMethod()
{
   ...
   if(childNode.Tag==0)
      childNode.Tag= childNode.Name.GetUniquetHashCode()

   switch(childNode.Tag)
   {
       case _bob :
        break;
       case _jill :
         break;
       case _marko :
        break;
   }
}

GetUniquetHashCode 的扩展方法可以是这样的:

public static class StringExtentions
    {
        /// <summary>
        /// Return unique Int64 value for input string
        /// </summary>
        /// <param name="strText"></param>
        /// <returns></returns>
        public static Int64 GetUniquetHashCode(this string strText)
        {
            Int64 hashCode = 0;
            if (!string.IsNullOrEmpty(strText))
            {
                //Unicode Encode Covering all character-set
                byte[] byteContents = Encoding.Unicode.GetBytes(strText);
                System.Security.Cryptography.SHA256 hash =  new System.Security.Cryptography.SHA256CryptoServiceProvider();
                byte[] hashText = hash.ComputeHash(byteContents);
                //32Byte hashText separate
                //hashCodeStart = 0~7  8Byte
                //hashCodeMedium = 8~23  8Byte
                //hashCodeEnd = 24~31  8Byte
                //and Fold
                Int64 hashCodeStart = BitConverter.ToInt64(hashText, 0);
                Int64 hashCodeMedium = BitConverter.ToInt64(hashText, 8);
                Int64 hashCodeEnd = BitConverter.ToInt64(hashText, 24);
                hashCode = hashCodeStart ^ hashCodeMedium ^ hashCodeEnd;
            }
            return (hashCode);
        }


    }

此代码的源代码发布在这里 请注意,使用 Cryptography 很慢,您通常会在应用程序启动时预热支持的字符串,我这样做是为了将它们保存在静态字段中,因为它们不会改变并且与实例无关。请注意,我设置了节点对象的标记值,我可以使用任何属性或添加一个,只要确保这些与实际文本同步即可。

我在低延迟系统上工作,我所有的代码都是一串命令:值,命令:值......

现在该命令都被称为 64 位整数值,因此像这样切换可以节省一些 CPU 时间。

于 2018-01-25T09:51:58.640 回答
0

我只是在这里阅读答案列表,并想分享这个基准测试,它将switch构造与if-else三元?运算符进行比较。

我喜欢那篇文章的地方在于它不仅比较了单左结构(例如,if-else),而且比较了双层和三层结构(例如,if-else-if-else)。

根据结果​​,该if-else构造在 8/9 测试用例中是最快的;该switch构造在 5/9 测试用例中并列最快。

因此,如果您正在寻找速度if-else似乎是最快的方法。

于 2018-02-09T02:26:58.753 回答
-1

我可能遗漏了一些东西,但你不能对类型而不是字符串做一个 switch 语句吗?那是,

switch(childNode.Type)
{
case Bob:
  break;
case Jill:
  break;
case Marko:
  break;
}
于 2008-09-18T16:52:55.467 回答