C# Switch语句中的大小写顺序是否会改变性能?

C# Switch语句中的大小写顺序是否会改变性能?,c#,performance,micro-optimization,switch-statement,C#,Performance,Micro Optimization,Switch Statement,假设我有一个switch语句,如下所示 switch(alphabet) { case "f": //do something break; case "c": //do something break; case "a": //do something break; case "e": //do something break;

假设我有一个switch语句,如下所示

switch(alphabet) {

    case "f":
        //do something
        break;

    case "c":
        //do something
        break;

    case "a":
        //do something
        break;

    case "e":
        //do something
        break;

}
现在假设我知道字母表的频率最高,其次是a,c和f。因此,我刚刚重新构造了
case
语句顺序,并将其设置为:

switch(alphabet) {

    case "e":
        //do something
        break;

    case "a":
        //do something
        break;

    case "c":
        //do something
        break;

    case "f":
        //do something
        break;
}

第二个
开关
语句是否比第一个
开关
语句快?如果是,并且在我的程序中,我需要多次调用这个
开关
语句,这会是一个实质性的改进吗?或者,如果没有,我如何使用我的频率知识来提高性能?

我认为switch case的做法是,它将从上到下循环所有案例,以找到匹配项。如果匹配,就停在那里


因此,如果您更改了频率案例的优先级,答案是肯定的,它可以在某种程度上帮助您提高性能。但我相信这不会有多大帮助。

对于相对较小的值集,它们具有相同的性能。我以前试过检查C程序的汇编代码,编译器用switch语句中的所有值创建一个跳转表

但是如果案例值太多,则可以肯定的是,它们将退化为
if
else if
,因此将案例“E”放在顶部肯定会加快速度


它也在C#中,C#还为switch语句生成一个小集合的跳转表,尽管只针对相邻的值。所以它是O(1),即使第一个值不匹配,也没有多个测试。

你不应该太担心。这当然不是可以预测的

对于字符串大小写标签,编译器实际上使用一个内部哈希表,将字符串映射到跳转表中的索引。所以这个操作实际上是O(1)-与标签的数量无关

对于整数标签,我认为生成的实际代码取决于标签的数量以及这些数字是否连续(或“几乎”连续)。如果它们是连续的(1,2,3,4,…),那么它们将被转换成一个跳转表。如果有很多,那么将使用哈希表+跳转表(与字符串类似)。如果只有几个标签,并且它们不是要立即转换为跳转表的表,那么只有then才会转换为一系列If..then..else语句

不过,一般来说,您应该编写代码以便能够阅读,而不是让编译器生成“更快”的代码


(请注意,我上面的描述是C#编译器如何在内部工作的实现细节:你不应该总是依赖它这样工作——事实上,它现在甚至可能不完全像那样工作,但至少这是一般的想法)。

这取决于编译器如何实现switch语句

首先,你不能随意排列顺序;如果你有一个案例 在C语言(C,C++,C,java,…)中,并且case块不 在中断时终止,您无法重新安排案例,因为缺少 中断意味着编译器必须实现下一种情况。 如果我们忽略了这个特殊情况,你就可以把其余的情况都处理掉

如果案例数量较少,编译器可以实现案例测试 通过一系列的比较。如果病例数量适中,则可能构成 一个平衡的二叉树。如果病例数量大,大多数 编译器在开关值上实现一个索引分支(如果它来自) 密集的一组。如果案例值集的一部分是稠密的,而另一部分是 否则,编译器可能会使用二叉树将案例分成若干组 选择哪个稠密集,以及稠密集中的索引跳转。 (事实上,编译器在技术上可以执行任何将控制传递给 适当的情况,但通常是上述情况之一)

您可以看到,顺序可能重要,也可能不重要,这取决于编译器的工作方式
实现该开关。对于大多数优秀的编译器来说,这并不重要。

+1:我想说的就是这个。这些被实现为GOTOs(检查反射器以验证)。这是O(1)-你真的不应该担心在那一点上优化…+1来自我。如果PERP实际上是一个问题,考虑使用一种更贴近金属的语言。对于.NETFramework 3.5,概念如下。然而,我不知道.NET 4.0版,你不应该以你所知道的最清晰的方式编写,用真实世界的场景描述,并在需要的地方进行优化吗?为了清楚起见,问题被标记为C,C不支持这里第一段中描述的失败案例(每个案例都必须以
break
return
)结束。有趣。因此人们不必要地用C#编码“break”(例如,参见OP的示例)。这并不会改变我的答案。不,C#在这件事上有点冗长,你必须明确地终止case块;
break
(或
return
)是必需的(可能是为了防止失败案例在将来实施时成为破坏性的更改)。我同意,您的答案仍然正确且有用。中断是必需的,以表示语句结束,因为您可以对2+标记使用相同的语句。case“null”:case“0”:case“”:…process“empty”场景…中断;案例“foo1”:…中断;案例“foo2”:…中断;