【问题标题】:Code optimization; switch versus if's代码优化; switch 与 if 的对比
【发布时间】:2011-01-30 12:47:25
【问题描述】:

我有一个问题是在一个经常被调用的函数中使用“case”还是“ifs”。 这是现在的以下内容,在“ifs”中;代码不言自明:

int identifyMsg(char* textbuff) {
if (!strcmp(textbuff,"text")) {
    return 1;
}
if (!strcmp(textbuff,"name")) {
    return 2;
}
if (!strcmp(textbuff,"list")) {
    return 3;
}
if (!strcmp(textbuff,"remv")) {
    return 4;
}
if (!strcmp(textbuff,"ipad")) {
    return 5;
}
if (!strcmp(textbuff,"iprm")) {
    return 6;
}
return 0;
}

我的问题是:开关性能会更好吗?我知道如果使用“ifs”,我可以将最有可能的选项放在顶部。

【问题讨论】:

  • 在担心微优化之前,您应该专注于使代码正确。一旦它是正确的,你应该在优化之前进行测量。然后,当您认为需要优化时,您应该仔细考虑优化后代码的未来可维护性。简而言之,优化不正确的代码是没有意义的。

标签: c++ if-statement switch-statement optimization


【解决方案1】:

您不能对字符串使用switch 语句,因为它们是指针并且在编译时不会被计算。 你被一堆if 语句卡住了。

但是为了性能,我相信当有更多的条件需要检查时,开关的性能会更好,但差异会很小,没关系。

虽然我以前从未测试过这个,但我读过这种开关优化:

switch (value) {
  case frequent_value1:
  case frequent_value2:
  case frequent_value3:
    break;

default:
  switch (value) {
     case infrequent_value1:
     case infrequent_value2:
     case infrequent_value3:
        break;
     }
}

【讨论】:

  • 确实如此。但除此之外:即使有可能,“哪个表现更好”的问题也没有实际意义。
  • 另一个关于 SO 的好话题:stackoverflow.com/questions/97987/switch-vs-if-else
  • 那些嵌套的开关不应该对性能产生任何积极的影响。如果出现其中一种常见情况,则无论如何都不会检查任何其他情况的条件。常规 if-elseif 也是如此。
  • 不过,有各种技巧涉及散列和代码生成器,它们可以为字符串启用开关。在我看来,不值得麻烦,除非分析表明该特定部分的性能是一个真正的问题。
【解决方案2】:

您可以使用gperf 生成您想要查看的“动词”的完美哈希值。然后你可以使用switch 声明。

或者,你可以这样做:

switch (textbuff[0])
{
    case 'i':
    {
        switch (textbuff[1])
        {
            case 'p':
            {
                 switch (textbuff[2])
                 {
                     case 'a': /* something. */ break;
                     case 'r': /* something else. */ break;
                 }
            }
        }
    }

(你明白了)。

作为另一种选择(如果您的所有命令都是 4 个字符),将它们转换为单个 32 位数字,然后打开它:

int32_t mashed =
    textbuff[0] << 24 | 
    textbuff[1] << 16 |
    textbuff[2] << 8 |
    textbuff[3];

switch (mashed) { /* ... */ }

但老实说,除非选项列表特别大,或者这个函数被多次调用,否则不值得。

记住:先测量;稍后优化(仅在必要时)。

【讨论】:

  • 生成hash,然后用switch是不是太多了?尤其是当 OP 专注于如此微不足道的性能时?
  • 这可能是值得的;它可能不会。首先衡量,确定是否值得优化。
  • 或者我们只是全力以赴并使用 flex 生成适当的词法分析器。
【解决方案3】:

您可以将所有值放入 std::map。

class MessageMap
{ 
    std::map<std::string,int>    data;
    public:
        MessageMap()
        {
             data["text"]   = 1;
             data["name"]   = 2;
             data["list"]   = 3;
             data["remv"]   = 4;
             data["ipad"]   = 5;
             data["iprm"]   = 6;
        }
        int getMessageId(std::string const& index) cosnt
        {
            std::map<std::string,int>::const_iterator f;
            if ((f = data.find(index)) != data.end())
            {
                return f->second;
            }
            return 0;
        }
};
int identifyMsg(char* textbuff)
{
    static MessageMap   mssageMap;
    return messageMap.getMessageId(textbuff);
}

【讨论】:

    【解决方案4】:

    你可以很好地使用“枚举”来表示这些字符串。然后使用 switch case 语句。

    【讨论】:

    • +1 错误::-) 实际上,这就是他正在做的 - 将 id 字符串转换为数字 id。如果 id 来自数据(例如 XML),它几乎不能是数字,但不能是枚举。
    【解决方案5】:

    据我所知,有两个问题。优化和 if/switch。

    首先,代码优化是一个代价高昂的过程。只优化那些明显是瓶颈的代码部分。在这种情况下,我对此表示怀疑。看起来,就像您正在发送一个 textual-id 来决定如何处理消息。消息从何而来? IPC、XML、文件?你打算怎么处理这个消息?消息内容处理代码的性能如何?代码中应该有比字符串比较占用更多资源的地方。

    您是否尝试过一些性能分析器,例如 Purify、gperf、cachegrind?

    关于 if/switch:switch 仅适用于整数类型。 (char, short, int, long, enum)

    【讨论】:

      【解决方案6】:

      我最近遇到的另一种可能适合您也可能不适合您的选择是:

      int identifyMsg(const char* textbuff) {
          static const struct { const char* str; int id; } pairs[] = {
              { "text", 1 },
              { "name", 2 },
              { "list", 3 },
              { "remv", 4 },
              { "ipad", 5 },
              { "iprm", 6 },
          };
          for (int i = 0; i < sizeof(pairs)/sizeof(pairs[0]); ++i) {
              if (!strcmp(textbuff, pairs[i].str))
                  return pairs[i].id;
          }
          return 0;
      }
      

      【讨论】:

      • 那么为什么不简单地使用 STL 关联数组呢?
      • 如果您将函数的static 成员配对,则每次输入函数时它都不会重新生成。
      • 对于这么少的元素,STL 关联数组的性能可能会降低。如果我担心性能,我会在大约 10-20 个元素之后开始倾向于 STL map。不过,这主要是直觉。如有必要,应使用分析来确定两者在性能上的交汇点。
      【解决方案7】:

      假设它确实很重要:

      因为 strcmp 很慢,但整数比较很快:如果您的所有命令都是 4 个字符长(恰好适合 32 位整数),您可以将每个字符串转换为 32 位数字,然后根据它进行切换。

      否则,有两种基本方法可以快速将一个字符串与大量候选字符串进行比较:

      • 将候选对象存储在哈希表中。

      或

      • 按字母顺序对数组中的候选进行排序。然后,您可以使用 strcmp 的结果对数组执行二分搜索,以找到匹配项,或排除剩余的一半候选者。

      附带说明 - MSVC 和 GCC 等编译器已对使用二分搜索测试开关条件的开关进行了优化。因此,具有 256 个元素的 switch 语句将被优化为最多 8 个比较操作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-07
        • 1970-01-01
        • 1970-01-01
        • 2015-12-28
        • 1970-01-01
        • 2022-11-16
        • 1970-01-01
        相关资源
        最近更新 更多