什么是更快,打开string或elseif的types?

比方说,我可以select一个代码path,以string比较为基础,否则如果是这样的话:

哪个更快,为什么?

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语句对于什么来说是个奇怪的事情。 例如它不会允许你使用variables,只有常数被移动到主程序集。 我以为它有这个限制,由于一些时髦的东西,它正在做。 如果只是翻译成elseifs(就像一个海报发表的评论)那么为什么我们不允许在case语句中使用variables?

警告:我在后优化。 这个方法在应用程序的缓慢部分被调用了很多次。

格雷格的个人资料结果对于他所描述的确切情况非常有用,但有趣的是,考虑到一系列不同的因素,不同方法的相对成本发生了显着的变化,这些因素包括被比较的types数量,相对频率以及基础数据中的任何模式。

简单的答案是,没有人可以告诉你在你的具体情况下性能差异将会是什么,你将需要在你自己的系统中以不同的方式测量性能,以获得准确的答案。

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

如果我执行以下操作:

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

在input正确的程序段之前,必须对每个之前的条件进行评估。 另一方面

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

将执行一个简单的跳转到正确的代码位。

在你的例子中,更复杂的地方在于你的另一个方法使用了一个string开关而不是整数,而这个整数变得复杂一点。 在低级别,string不能以与整数值相同的方式打开,所以C#编译器会做一些神奇的事情来使这个工作。

如果switch语句“足够小”(编译器按照自认为最好的方式进行),则切换string将生成与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(); } 

一旦字典中的项目列表变得足够大,编译器将自动创build一个内部字典,该字典将交换机中的string映射到一个整数索引,然后根据该索引进行切换。

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

静态字段在与包含types为Dictionary<string, int>的switch语句的类相关联的“隐藏”位置中定义,并给出一个错位的名称

 //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(); } 

在刚刚运行的一些快速testing中,If / Else方法的速度是3种不同types(其中types是随机分布的)的3倍。 在25种types的交换机中,50种types的交换机速度较小(16%),交换机的速度提高了一倍以上。

如果你要打开大量的types,我会build议第三种方法:

 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所build议的类似,但是使用运行时types句柄而不是全types对象避免了通过reflection来加载types对象的开销。

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

用5,000,000个数据元素(mode = Random)和5个typestesting3次迭代
方法时间%最佳
如果/否则179.67 100.00
 TypeHandleDictionary 321.33 178.85
 TypeDictionary 377.67 210.20
开关492.67 274.21

用5,000,000个数据元素(mode = Random)和10个typestesting3次迭代
方法时间%最佳
如果/另外271.33 100.00
 TypeHandleDictionary 312.00 114.99
 TypeDictionary 374.33 137.96
开关490.33 180.71

用5,000,000个数据元素(mode = Random)和15个typestesting3次迭代
方法时间%最佳
 TypeHandleDictionary 312.00 100.00
如果/另外369.00 118.27
 TypeDictionary 371.67 119.12
开关491.67 157.59

使用5,000,000个数据元素(mode = Random)和20个typestesting3次迭代
方法时间%最佳
 TypeHandleDictionary 335.33 100.00
 TypeDictionary 373.00 111.23
 If / Else 462.67 137.97
开关490.33 146.22

用5,000,000个数据元素(mode = Random)和25个typestesting3次迭代
方法时间%最佳
 TypeHandleDictionary 319.33 100.00
 TypeDictionary 371.00 116.18
开关483.00 151.25
 If / Else 562.00 175.99

用5,000,000个数据元素(mode = Random)和50个typestesting3次迭代
方法时间%最佳
 TypeHandleDictionary 319.67 100.00
 TypeDictionary 376.67 117.83
开关453.33 141.81
 If / Else 1,032.67 323.04

至less在我的机器上,当用作方法input的types的分布是随机的时,types句柄字典方法击败所有超过15种不同types的所有其他types。

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

使用5,000,000个数据元素(mode = UniformFirst)和50个typestesting3次迭代
方法时间%最佳
如果/否则39.00 100.00
 TypeHandleDictionary 317.33 813.68
 TypeDictionary 396.00 1,015.38
开关403.00 1,033.33

相反,如果input始终是if / else链中的最后一项,则会产生相反的效果:

用5,000,000个数据元素(mode = UniformLast)和50个typestesting3次迭代
方法时间%最佳
 TypeHandleDictionary 317.67 100.00
开关354.33 111.54
 TypeDictionary 377.67 118.89
 If / Else 1,907.67 600.52

如果您可以对input做出一些假设,那么您可以从混合方法中获得最佳性能,在这种方法中,您执行if / else检查最常见的几种types,如果这些方法失败,则会回到字典驱动的方法。

我刚刚实施了一个快速testing应用程序,并用ANTS 4进行了分析。
规格:.net 3.5 sp1在32位Windows XP中,代码内置在发行模式。

300万testing:

  • 开关:1.842秒
  • 如果:0.344秒。

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

100万个testing

  • 鲍勃:0.612秒。
  • 吉尔:0.835秒。
  • Marko:1.093秒。

我看起来像“如果其他”更快,至less是我创造的场景。

 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";}} 

首先,你要比较苹果和橘子。 您首先需要比较开关types与开关string,然后如果在typesvs如果在string,然后比较优胜者。

其次,这是面向对象的devise。 在支持面向对象的语言中,开启types(任何types)都是一种指向糟糕devise的代码味道。 解决scheme是从一个抽象的或虚拟的方法(或类似的结构,取决于你的语言)

例如。

 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 } } 

然后,而不是做switch语句,你只需调用childNode.Action()

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

如果你已经有了类,我build议使用策略devise模式,而不是switch或elseif。

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

除非你已经写了这个,并发现你有一个性能问题,我不会担心哪个更快。 去一个更可读的。 请记住,“过早优化是万恶之源”。 Donald Knuth

SWITCH结构最初是用于整数数据的; 它的意图是直接使用参数作为索引到一个“调度表”,一个指针表。 因此,会有一个单一的testing,然后直接启动相关的代码,而不是一系列的testing。

这里的难点在于它的使用已经被推广到“string”types,这显然不能被用作索引,并且SWITCH结构的所有优点都失去了。

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

如果您打开的types是原始.NETtypes,则可以使用Type.GetTypeCode(Type),但是如果它们是自定义types,则它们都将返回为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 } 

switch()将会编译成相当于一组else if的代码。 string比较将比types比较慢得多。

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

实际上,如果你比较典型的3到10个(或者多个)陈述,我真的怀疑使用这个或那个真正的性能增益。

正如克里斯已经说过,去可读性: 什么是更快,打开string或elseif的types?

我认为这里的主要性能问题是,在switch块中,比较string,在if-else块中检查types…这两个不一样,所以我会说你把土豆和香蕉比较“。

我首先比较一下:

 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") {} 

我不确定多合适的devise可能会多快。

 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语句会做得更好。 如果你的函数实际上并不是关于这个types的一个动作,那么你可以定义一个枚举types。

 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(); } } 

我认为这个问题比这两种方法都要快。 如果纳秒对您来说很重要,您可能要尝试抽象类路由。

string比较将始终完全依赖于运行时环境(除非string是静态分配的,尽pipe需要比较这些string是有争议的)。 然而,types比较可以通过dynamic或静态绑定来完成,无论哪种方式,对于运行时环境来说,比比较string中的单个字符更有效。

当然,String上的开关将会编译成一个string比较(每个案例一个),比​​一个types比较慢(而且比用于switch / case的典型整数比较要慢)。

我可能会错过一些东西,但是不能在types而不是String上做switch语句吗? 那是,

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

三个想法:

1)如果你打算根据对象的types做一些不同的事情,把这种行为转移到这些类中可能是有意义的。 然后,而不是开关或if-else,你只需要调用childNode.DoSomething()。

2)比较types将比string比较快得多。

3)在if-elsedevise​​中,您可能能够利用重新sorting的testing。 如果“Jill”物体构成了90%的物体,那么先testing它们。

你使用开关的一个问题是使用string,比如“Bob”,这会在编译后的代码中产生更多的循环和行数。 生成的IL将必须声明一个string,将其设置为“Bob”,然后在比较中使用它。 所以考虑到这一点你的IF语句将运行得更快。

PS。 永旺的例子不会工作,因为你不能打开types。 (不,我不知道为什么,但我们已经尝试过,它不起作用,它与types是可变的)

如果你想testing这个,只需构build一个单独的应用程序,然后构build两个简单的方法来完成上面所写的内容,并使用诸如Ildasm.exe之类的方法来查看IL。 您会注意到IF语句Method中的ILless得多。

Ildasm附带VisualStudio …

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

ILDASM教程 – http://msdn.microsoft.com/zh-cn/library/aa309387(VS.71).aspx

请记住,分析器是你的朋友。 任何猜测都是浪费大部分时间。 顺便说一句,我有JetBrains的dotTrace分析器的一个很好的经验。

打开string基本上被编译成if-else-if梯形图。 尝试反编译一个简单的。 在任何情况下,testingstringequailty应该是便宜的,因为他们是interned和所有需要的是参考检查。 在可维护性方面做什么是有道理的; 如果您正在压缩string,请执行string切换。 如果您正在根据types进行select,则types梯子更合适。