代码之家  ›  专栏  ›  技术社区  ›  Johannes Schaub - litb

“默认”开关盒是否会干扰跳转表优化?

  •  11
  • Johannes Schaub - litb  · 技术社区  · 14 年前

    在我的代码中,我习惯于编写包含如下断言的回退默认情况,以防止在语义更改时忘记更新开关

    switch(mode) {
    case ModeA: ... ;
    case ModeB: ... ;
    case .. /* many of them ... */
    default: {
      assert(0 && "Unknown mode!");
      return ADummyValue();
    }
    };
    

    现在我想知道人工回退检查默认情况是否会干扰跳转表的生成?假设“ModeA”和“ModeB”等是连续的,这样编译器就可以优化到一个表中。由于“default”用例包含一个实际的“return”语句(因为断言将在release模式下消失,编译器将抱怨丢失的return语句),编译器似乎不太可能优化默认分支。

    最好的办法是什么?一些朋友建议我用空指针解引用替换“ADummyValue”,这样编译器在出现未定义的行为时,就可以忽略对丢失的返回语句的警告。有没有更好的方法来解决这个问题?

    7 回复  |  直到 14 年前
        1
  •  2
  •   ruslik    14 年前

    如果编译器是MSVC,则可以使用 __assume 内在的: http://msdn.microsoft.com/en-us/library/1b3fsfxw(v=VS.80).aspx

        2
  •  3
  •   Jerry Coffin    14 年前

    至少在我看过的编译器中,答案通常是否定的。大多数编译器都会编译这样的switch语句来编写大致相当于:

    if (mode < modeA || mode > modeLast) {
        assert(0 && "Unknown mode!");
        return ADummyValue();
    }
    switch(mode) { 
        case modeA: ...;
        case modeB: ...;
        case modeC: ...;
        // ...
        case modeLast: ...;
    }
    
        3
  •  2
  •   echofromtheinterwebz    14 年前

    如果您使用的是“默认”(ha!) <assert.h> ,无论如何,定义都与NDEBUG宏绑定,所以可能只是

        case nevermind:
    #if !defined(NDEBUG)
        default:
            assert("can" && !"happen");
    #endif
        }
    
        4
  •  1
  •   stefaanv    14 年前

    我只看到一个解决方案,以防优化实际受到干扰:臭名昭著的“ifndef-NDEBUG”绕过默认情况。不是最好的把戏,但在这种情况下是清楚的。

    顺便问一句:你已经看过你的编译器在默认情况下做了什么吗?

        5
  •  1
  •   Å imon Tóth    14 年前

    如果你有一个永远不应该到达的状态,那么你应该终止这个程序,因为它刚刚到达了一个意外的状态,即使是在发布模式下(你可能只是更老练一些,实际上是保存用户数据,在崩溃之前做所有其他好的事情)。

    并且请不要沉迷于微观优化,除非您已经实际测量(使用剖析器)您需要它们。

        6
  •  1
  •   BЈовић    14 年前

    处理此问题的最佳方法是不禁用断言。这样你也可以留意可能的虫子。有时,应用程序最好在崩溃时显示一条很好的消息,解释到底发生了什么,然后继续工作。

        7
  •  0
  •   Timothy003    13 年前

    使用编译器扩展:

    // assume.hpp
    #pragma once
    
    #if defined _MSC_VER
    #define MY_ASSUME(e) (__assume(e), (e) ? void() : void())
    #elif defined __GNUC__
    #define MY_ASSUME(e) ((e) ? void() : __builtin_unreachable())
    #else   // defined __GNUC__
    #error unknown compiler
    #endif  // defined __GNUC__
    

    -

    // assert.hpp
    #include <cassert>
    #include "assume.hpp"
    
    #undef MY_ASSERT
    #ifdef NDEBUG
    #define MY_ASSERT MY_ASSUME
    #else   // NDEBUG
    #define MY_ASSERT assert
    #endif  // NDEBUG