C# 什么更快,在字符串上切换还是在类型上切换?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/94305/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is quicker, switch on string or elseif on type?
提问by Quibblesome
Lets say I have the option of identifying a code path to take on the basis of a string comparison or else iffing the type:
假设我可以选择根据字符串比较或其他类型确定要采用的代码路径:
Which is quicker and why?
哪个更快,为什么?
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)
{
}
Update:The main reason I ask this is because the switch statement is perculiar about what counts as a case. For example it wont allow you to use variables, only constants which get moved to the main assembly. I assumed it had this restriction due to some funky stuff it was doing. If it is only translating to elseifs (as one poster commented) then why are we not allowed variables in case statements?
更新:我问这个的主要原因是因为 switch 语句对于什么算作一个案例是特别的。例如,它不允许您使用变量,只能使用移动到主程序集的常量。我认为它有这个限制是因为它正在做一些时髦的事情。如果它只是转换为 elseifs(正如一位海报评论的那样),那么为什么我们不允许在 case 语句中使用变量?
Caveat:I am post-optimising. This method is called manytimes in a slow part of the app.
警告:我正在优化。这种方法被称为许多在应用程序的缓慢一部分倍。
采纳答案by ilitirit
Greg's profile results are great for the exact scenario he covered, but interestingly, the relative costs of the different methods change dramatically when considering a number of different factors including the number of types being compared, and the relative frequency and any patterns in the underlying data.
Greg 的配置文件结果非常适合他所涵盖的确切场景,但有趣的是,当考虑到许多不同的因素时,不同方法的相对成本会发生巨大变化,这些因素包括被比较的类型数量、相对频率和基础数据中的任何模式.
The simple answer is that nobody can tell you what the performance difference is going to be in your specific scenario, you will need to measure the performance in different ways yourself in your own system to get an accurate answer.
简单的答案是,没有人可以告诉您特定场景中的性能差异是什么,您需要自己在自己的系统中以不同的方式衡量性能以获得准确的答案。
The If/Else chain is an effective approach for a small number of type comparisons, or if you can reliably predict which few types are going to make up the majority of the ones that you see. The potential problem with the approach is that as the number of types increases, the number of comparisons that must be executed increases as well.
If/Else 链是用于少量类型比较的有效方法,或者如果您可以可靠地预测哪些类型将构成您看到的大部分类型。该方法的潜在问题是,随着类型数量的增加,必须执行的比较数量也会增加。
if I execute the following:
如果我执行以下操作:
int value = 25124;
if(value == 0) ...
else if (value == 1) ...
else if (value == 2) ...
...
else if (value == 25124) ...
each of the previous if conditions must be evaluated before the correct block is entered. On the other hand
在输入正确的块之前,必须评估前面的每个 if 条件。另一方面
switch(value) {
case 0:...break;
case 1:...break;
case 2:...break;
...
case 25124:...break;
}
will perform one simple jump to the correct bit of code.
将执行一个简单的跳转到正确的代码位。
Where it gets more complicated in your example is that your other method uses a switch on strings rather than integers which gets a little more complicated. At a low level, strings can't be switched on in the same way that integer values can so the C# compiler does some magic to make this work for you.
在您的示例中变得更复杂的地方是您的其他方法使用字符串上的开关而不是整数,这会变得更复杂一些。在低级别上,字符串不能像整数值那样被打开,所以 C# 编译器会做一些魔术来为你工作。
If the switch statement is "small enough" (where the compiler does what it thinks is best automatically) switching on strings generates code that is the same as an if/else chain.
如果 switch 语句“足够小”(编译器自动执行它认为最好的操作),则切换字符串会生成与 if/else 链相同的代码。
switch(someString) {
case "Foo": DoFoo(); break;
case "Bar": DoBar(); break;
default: DoOther; break;
}
is the same as:
是相同的:
if(someString == "Foo") {
DoFoo();
} else if(someString == "Bar") {
DoBar();
} else {
DoOther();
}
Once the list of items in the dictionary gets "big enough" the compiler will automatically create an internal dictionary that maps from the strings in the switch to an integer index and then a switch based on that index.
一旦字典中的项目列表变得“足够大”,编译器将自动创建一个内部字典,该字典从开关中的字符串映射到整数索引,然后基于该索引的开关。
It looks something like this (Just imagine more entries than I am going to bother to type)
它看起来像这样(想象一下比我要费心输入的条目更多)
A static field is defined in a "hidden" location that is associated with the class containing the switch statement of type Dictionary<string, int>
and given a mangled name
静态字段定义在“隐藏”位置,该位置与包含类型的 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();
}
In some quick tests that I just ran, the If/Else method is about 3x as fast as the switch for 3 different types (where the types are randomly distributed). At 25 types the switch is faster by a small margin (16%) at 50 types the switch is more than twice as fast.
在我刚刚运行的一些快速测试中,If/Else 方法大约是 3 种不同类型(其中类型随机分布)的 switch 方法的 3 倍。在 25 种类型时,切换速度略快 (16%),而在 50 种类型时,切换速度快两倍多。
If you are going to be switching on a large number of types, I would suggest a 3rd method:
如果您要打开大量类型,我建议使用第 3 种方法:
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...
}
}
This is similar to what Ted Elliot suggested, but the usage of runtime type handles instead of full type objects avoids the overhead of loading the type object through reflection.
这与 Ted Elliot 建议的类似,但使用运行时类型句柄而不是完整类型对象避免了通过反射加载类型对象的开销。
Here are some quick timings on my machine:
以下是我机器上的一些快速计时:
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
On my machine at least, the type handle dictionary approach beats all of the others for anything over 15 different types when the distribution of the types used as input to the method is random.
至少在我的机器上,当用作方法输入的类型的分布是随机的时,类型句柄字典方法在超过 15 种不同类型的任何情况下都胜过所有其他方法。
If on the other hand, the input is composed entirely of the type that is checked first in the if/else chain that method is muchfaster:
另一方面,如果输入完全由 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
Conversely, if the input is always the last thing in the if/else chain, it has the opposite effect:
相反,如果输入始终是 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 you can make some assumptions about your input, you might get the best performance from a hybrid approach where you perform if/else checks for the few types that are most common, and then fall back to a dictionary-driven approach if those fail.
如果您可以对您的输入做出一些假设,那么您可能会从混合方法中获得最佳性能,在这种方法中,您对少数最常见的类型执行 if/else 检查,然后在失败时回退到字典驱动的方法。
回答by moonshadow
The switch() will compile out to code equivalent to a set of else ifs. The string comparisons will be much slower than the type comparisons.
switch() 将编译为等效于一组 else if 的代码。字符串比较将比类型比较慢得多。
回答by Chris Upchurch
Unless you've already written this and find you have a performance problem I wouldn't worry about which is quicker. Go with the one that's more readable. Remember, "Premature optimization is the root of all evil." - Donald Knuth
除非你已经写了这个并且发现你有性能问题,否则我不会担心哪个更快。选择更具可读性的那个。请记住,“过早的优化是万恶之源”。- 唐纳德·克努斯
回答by Magsol
String comparison will always rely completely on the runtime environment (unless the strings are statically allocated, though the need to compare those to each other is debatable). Type comparison, however, can be done through dynamic or static binding, and either way it's more efficient for the runtime environment than comparing individual characters in a string.
字符串比较将始终完全依赖于运行时环境(除非字符串是静态分配的,尽管需要将它们相互比较是有争议的)。然而,类型比较可以通过动态或静态绑定来完成,无论哪种方式,对于运行时环境来说都比比较字符串中的单个字符更有效。
回答by JeeBee
Surely the switch on String would compile down to a String comparison (one per case) which is slower than a type comparison (and far slower than the typical integer compare that is used for switch/case)?
当然,字符串上的开关会编译成字符串比较(每个案例一个),它比类型比较慢(并且比用于开关/案例的典型整数比较慢得多)?
回答by Aeon
I may be missing something, but couldn't you do a switch statement on the type instead of the String? That is,
我可能遗漏了一些东西,但你不能对类型而不是字符串做一个 switch 语句吗?那是,
switch(childNode.Type)
{
case Bob:
break;
case Jill:
break;
case Marko:
break;
}
回答by Nescio
Switch statement is faster to execute than the if-else-if ladder. This is due to the compiler's ability to optimise the switch statement. In the case of the if-else-if ladder, the code must process each if statement in the order determined by the programmer. However, because each case within a switch statement does not rely on earlier cases, the compiler is able to re-order the testing in such a way as to provide the fastest execution.
Switch 语句的执行速度比 if-else-if 梯形图要快。这是由于编译器能够优化 switch 语句。在 if-else-if 梯形图的情况下,代码必须按照程序员确定的顺序处理每个 if 语句。然而,因为 switch 语句中的每个 case 不依赖于较早的 case,编译器能够以提供最快执行的方式重新排序测试。
回答by Gary Kephart
If you've got the classes made, I'd suggest using a Strategy design pattern instead of switch or elseif.
如果您已经创建了类,我建议您使用 Strategy 设计模式而不是 switch 或 elseif。
回答by Metro Smurf
I recall reading in several reference books that the if/else branching is quicker than the switch statement. However, a bit of research on Blackwasp shows that the switch statement is actually faster: http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
我记得在几本参考书中读到 if/else 分支比 switch 语句更快。然而,对 Blackwasp 的一些研究表明 switch 语句实际上更快:http: //www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
In reality, if you're comparing the typical 3 to 10 (or so) statements, I seriously doubt there's any real performance gain using one or the other.
实际上,如果您比较典型的 3 到 10 个(左右)语句,我严重怀疑使用其中一个是否会带来真正的性能提升。
As Chris has already said, go for readability: What is quicker, switch on string or elseif on type?
正如克里斯已经说过的,追求可读性: 什么更快,打开字符串还是打开类型?
回答by SaguiItay
I think the main performance issue here is, that in the switch block, you compare strings, and that in the if-else block, you check for types... Those two are not the same, and therefore, I'd say you're "comparing potatoes to bananas".
我认为这里的主要性能问题是,在 switch 块中,您比较字符串,而在 if-else 块中,您检查类型......这两个不一样,因此,我会说你'正在“将土豆与香蕉进行比较”。
I'd start by comparing this:
我首先比较这个:
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")
{}
回答by SaguiItay
One of the issues you have with the switch is using strings, like "Bob", this will cause a lot more cycles and lines in the compiled code. The IL that is generated will have to declare a string, set it to "Bob" then use it in the comparison. So with that in mind your IF statements will run faster.
开关的问题之一是使用字符串,如“Bob”,这将导致编译代码中出现更多循环和行。生成的 IL 必须声明一个字符串,将其设置为“Bob”,然后在比较中使用它。因此,考虑到这一点,您的 IF 语句将运行得更快。
PS. Aeon's example wont work because you can't switch on Types. (No I don't know why exactly, but we've tried it an it doesn't work. It has to do with the type being variable)
附注。Aeon 的示例不起作用,因为您无法打开类型。(不,我不知道究竟是为什么,但我们已经尝试过了,但它不起作用。这与变量类型有关)
If you want to test this, just build a separate application and build two simple Methods that do what is written up above and use something like Ildasm.exe to see the IL. You'll notice a lot less lines in the IF statement Method's IL.
如果您想对此进行测试,只需构建一个单独的应用程序并构建两个简单的方法来执行上面所写的内容,并使用 Ildasm.exe 之类的东西来查看 IL。您会注意到 IF 语句方法的 IL 中的行少了很多。
Ildasm comes with VisualStudio...
Ildasm 带有 VisualStudio...
ILDASM page - http://msdn.microsoft.com/en-us/library/f7dy01k1(VS.80).aspx
ILDASM 页面 - http://msdn.microsoft.com/en-us/library/f7dy01k1(VS.80).aspx
ILDASM Tutorial - http://msdn.microsoft.com/en-us/library/aa309387(VS.71).aspx
ILDASM 教程 - http://msdn.microsoft.com/en-us/library/aa309387(VS.71).aspx