c# – 在开关vs字典的Func值,这是更快,为什么?

假设有以下代码:

private static int DoSwitch(string arg)
{
    switch (arg)
    {
        case "a": return 0;
        case "b": return 1;
        case "c": return 2;
        case "d": return 3;
    }
    return -1;
}

private static Dictionary<string, Func<int>> dict = new Dictionary<string, Func<int>>
    {
        {"a", () => 0 },
        {"b", () => 1 },
        {"c", () => 2 },
        {"d", () => 3 },
    };

private static int DoDictionary(string arg)
{
    return dict[arg]();
}

通过迭代这两种方法和比较,我得到的是,字典稍快,即使“a”,“b”,“c”,“d”扩展包括更多的键。为什么会这样?

这与圈复杂性有关吗?是因为抖动将字典中的返回语句编译为本机代码只有一次?是因为字典的查找是O(1),其中may not be the case for a switch statement? (这些只是猜测)

简单的答案是switch语句执行线性,而字典执行对数。

在IL级,小的switch语句通常被实现为一系列if-elseif语句,比较切换变量和每种情况的相等性。因此,此语句将在与myVar的有效选项数成线性比例的时间执行;情况将按照它们出现的顺序进行比较,最坏的情况是所有的比较被尝试,并且最后一个匹配或者没有匹配。因此,有32个选项,最糟糕的情况是,它们都不是,并且代码将进行32个比较来确定这一点。

另一方面,词典使用索引优化的集合来存储值。在.NET中,字典基于Hashtable,它具有有效的持续访问时间(缺点是空间效率极低)。通常用于“映射”集合(如字典)的其他选项包括平衡树结构,如提供对数访问(和线性空间效率)的红黑树。任何这些将允许代码找到与集合中的正确“case”相对应的键(或确定它不存在),比switch语句可以做的更快。

编辑:其他答案和评论者触及这一点,所以为了完整性我也会。 Microsoft编译器并不总是编译切换到if / elseif最初我推断。它通常用少量的情况和/或用“稀疏”情况(非增量值,例如1,200,4000)来这样做。对于较大的相邻情况集,编译器将使用CIL语句将交换机转换为“跳转表”。对于大集合的稀疏情况,编译器可以实现二分搜索以缩小字段,然后“下降”少量稀疏情况或针对相邻情况实现跳转表。

然而,编译器通常会选择性能和空间效率最佳折衷的实现,因此它将只对大量密集打包的情况使用跳转表。这是因为跳转表需要在内存中的空间在其必须覆盖的情况的范围的量级上,对于稀疏情况,在内存方面是非常低效的。通过在源代码中使用Dictionary,你基本上强制编译器的手;它会做你的方式,而不是妥协的性能,以获得内存的效率。

所以,我期望大多数情况下,switch语句或字典可以在源中使用时更好地使用字典。在switch语句中大量的情况都要避免,因为它们不易维护。

http://stackoverflow.com/questions/11617091/in-a-switch-vs-dictionary-for-a-value-of-func-which-is-faster-and-why

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c# – 在开关vs字典的Func值,这是更快,为什么?