为什么编译器向开关添加语句?

时间:2018-12-18 19:12:26

标签: c# compiler-optimization

我有以下相当简单的switch语句。     //更早     字符串fullPath = GetFullPath();     字符串类型= GetEntityType();

switch (type.ToLower()) {
    case "tables":
        tables.Add(fullPath);
        break;
    case "views":
        views.Add(fullPath);
        break;
    case "functions":
        functions.Add(fullPath);
        break;
    case "storedprocs":
        storedprocs.Add(fullPath);
        break;
    case "data":
        data.Add(fullPath);
        break;
    case "layouts":
        layouts.Add(fullPath);
        break;
    case "scripts":
        scripts.Add(fullPath);
        break;
    default:
        Console.WriteLine($"What is this: {type}");
        break;
}

当我使用Reflector反编译生成的二进制文件时,switch(string)已更改为ComputeStringHash,然后在每个case语句内部,它都通过if语句检查了值。听起来好像做的是双重工作。

    string s = str2.ToLower();
    switch (<PrivateImplementationDetails>.ComputeStringHash(s))
    {
        case 0x20890fc4:
            if (s == "tables")
            {
                break;
            }
            goto Label_0218;

        case 0x454a414e:
            if (s == "functions")
            {
                goto Label_01DE;
            }
            goto Label_0218;

        case 0x4facf6d1:
            if (s == "views")
            {
                goto Label_01D3;
            }
            goto Label_0218;

        case 0xcdfe2cb3:
            if (s == "storedprocs")
            {
                goto Label_01E9;
            }
            goto Label_0218;

        case 0xd872e2a5:
            if (s == "data")
            {
                goto Label_01F4;
            }
            goto Label_0218;

        case 0x9b4a129b:
            if (s == "scripts")
            {
                goto Label_020C;
            }
            goto Label_0218;

        case 0xba971064:
            if (s == "layouts")
            {
                goto Label_0200;
            }
            goto Label_0218;

        default:
            goto Label_0218;
    }

    first.Add(fullPath);
    continue;
  Label_01D3:
      second.Add(fullPath);
      continue;
  Label_01DE:
      list3.Add(fullPath);
      continue;
  Label_01E9:
      list4.Add(fullPath);
      continue;
  Label_01F4:
      list5.Add(fullPath);
      continue;
  Label_0200:
      list6.Add(fullPath);
      continue;
  Label_020C:
      list7.Add(fullPath);
      continue;
  Label_0218:
    Console.WriteLine("What is this: " + str2);
}

1 个答案:

答案 0 :(得分:7)

这是一个非常聪明的优化,它使switch能够及时完成工作,而该工作几乎与语句的case块中的字符串数无关。

此优化基于以下观察结果:相同字符串的哈希码必须相同。编译器没有一次检查字符串是否相等,而是只计算一次目标字符串的哈希,然后在O(1)中执行基于表的查找。这样可以使编译器达到所需的情况,此时编译器需要检查字符串实际上是否相等。

请注意,在某些情况下,多个查询字符串具有相同的哈希码。在这种情况下,生成的case语句将包含多个if,以便在具有相等哈希码的字符串之间进行决定。

总体而言,此行为模仿了基于哈希的字典的行为:哈希代码确定case(相当于哈希桶),内部的一系列if确定是否存在匹配项。这样可以提高性能,因为它可以使编译器跳过不必要的检查。