优化超过50个OR的if语句(||)

时间:2016-08-29 23:29:35

标签: c++ if-statement optimization

好的,所以我正在做一些涉及键盘输入的东西。我现在有一个像这样的巨大功能:

return key == BB_KEY_SPACE ||
        key == BB_KEY_ZERO ||
        key == BB_KEY_ONE ||
        key == BB_KEY_TWO ||
        key == BB_KEY_THREE ||
        key == BB_KEY_FOUR ||
        key == BB_KEY_FIVE ||
        key == BB_KEY_SIX ||
        key == BB_KEY_SEVEN ||
        key == BB_KEY_EIGHT ||
        key == BB_KEY_NINE ||
        key == BB_KEY_A ||
        key == BB_KEY_B ||
        key == BB_KEY_C ||
        key == BB_KEY_D ||
        key == BB_KEY_E ||
        key == BB_KEY_F ||
        key == BB_KEY_G ||
        key == BB_KEY_H ||
        key == BB_KEY_I ||
        key == BB_KEY_J ||
        key == BB_KEY_K ||
        key == BB_KEY_L ||
        key == BB_KEY_M ||
        key == BB_KEY_N ||
        key == BB_KEY_O ||
        key == BB_KEY_P ||
        key == BB_KEY_Q ||
        key == BB_KEY_R ||
        key == BB_KEY_S ||
        key == BB_KEY_T ||
        key == BB_KEY_U ||
        key == BB_KEY_V ||
        key == BB_KEY_W ||
        key == BB_KEY_X ||
        key == BB_KEY_Y ||
        key == BB_KEY_Z ||
        key == BB_KEY_NUMPAD0 ||
        key == BB_KEY_NUMPAD1 ||
        key == BB_KEY_NUMPAD2 ||
        key == BB_KEY_NUMPAD3 ||
        key == BB_KEY_NUMPAD4 ||
        key == BB_KEY_NUMPAD5 ||
        key == BB_KEY_NUMPAD6 ||
        key == BB_KEY_NUMPAD7 ||
        key == BB_KEY_NUMPAD8 ||
        key == BB_KEY_NUMPAD9 ||
        key == BB_KEY_MULTIPLY ||
        key == BB_KEY_PLUS ||
        key == BB_KEY_SEPERATOR ||
        key == BB_KEY_MINUS ||
        key == BB_KEY_DECIMAL ||
        key == BB_KEY_DIVIDE ||
        key == BB_KEY_OEM1 ||
        key == BB_KEY_OEMPLUS ||
        key == BB_KEY_OEMCOMMA ||
        key == BB_KEY_OEMMINUS ||
        key == BB_KEY_OEMPERIOD ||
        key == BB_KEY_OEM2 ||
        key == BB_KEY_OEM3 ||
        key == BB_KEY_OEM4 ||
        key == BB_KEY_OEM5 ||
        key == BB_KEY_OEM6 ||
        key == BB_KEY_OEM7 ||
        key == BB_KEY_OEM8 ||
        key == BB_KEY_OEM102;`

有没有一种优化方法?我假设它需要一些处理能力来验证所有这些if语句的东西。在查看诊断时,似乎也需要花费一些时间。我想知道是否有更聪明的方法来做这件事。

6 个答案:

答案 0 :(得分:11)

将其转换为开关语句:

 switch (key) {
 case BB_KEY_SPACE:
 case BB_KEY_ZERO:
 case BB_KEY_ONE:
      // ... and so on

您的编译器非常智能。如果这些文字常量仅形成少数几个连续整数值的范围,编译器将处理将其优化为少量比较,以实现对每个值范围的边界检查。

如果没有,编译器可能会使用其他一些启发式工具来提出一组优化的比较。

现代编译器也可能足够聪明,可以对冗长的if()版本使用相同的优化,但使用switch语句可以帮助编译器“看到光明的一天” ,并找出解决所有这些问题的最佳方法。

答案 1 :(得分:11)

利用keychar类型的评论中收集的其他信息,key有256个可能的值。 key的每个可能值的返回值可以存储到由key索引的全局数组中,并通过简单地返回数组的key'元素来检索。

char arr[256] = { 0 };
arr[BB_KEY_SPACE] = 1;
arr[BB_KEY_ZERO] = 1;
/* ... */
arr[BB_KEY_OEM102] = 1;

bool f(char key)
{
    return arr[(unsigned)key] != 0; /* == 1 for the BB_KEY_* values */
}

<小时/> [ EDIT ]正如@Galik在评论中最初建议的那样,通过使用std::vector<bool> arr(256);而不是char数组,可以保存一些内存(速度可以忽略不计)。

< / p>

<小时/> [ EDIT#2 ]正如下面的@Hurkyl和@Danh评论的那样,假设arr是已知固定大小的数组,std::bitset可以(应该?)使用而不是std::vector<bool>

虽然这是完全正确的,但它们之间的选择长期以来一直是讨论和争论的主题。搜索SO和/或谷歌搜索std::bitset vs std::vector<bool>将在双方找到一些意见。

答案 2 :(得分:2)

如果有范围:

if ((key >= SOME_MIN && key <= SOME_MAX) ||
    (key >= SOM_OTHER_MIN && key <= SOME_OTHER_MAX) ...

工作,但你必须深入研究那些不好的常数。

我将匹配的键常量放入一个集合中然后我可以说if (keySet.contains(key))之类的东西。当然,您在启动时设置一次,而不是每次都要检查一个键。

答案 3 :(得分:1)

分解。 你想要像

这样的功能

key_isnumpad(),key_isalpha()等等。

所以函数转变为

I f(key_isnumpad() || key_isalpha() || ...)

并且变得更具可读性。除非你有一个好的优化者,否则这实际上会减慢速度。 但是你可以用一个范围更好地编写各个函数(假设数字是连续的)。

最后将最可能的案例放在第一位。

答案 4 :(得分:0)

这是另一种方式,使用模板。主要是为了娱乐和教育。但是,如果您正在维护一些与每个键码值相关的属性,那么可能还有一个用例:

#include <array>
#include <type_traits>
#include <limits>
#include <utility>

//
// define our enum of codes
//
enum key_code : char
{
    BB_KEY_SPACE = ' ',
    BB_KEY_ZERO = '0',
    BB_KEY_ONE,
    BB_KEY_TWO,
    BB_KEY_THREE,
//  ...etc...
};

//
// by default we don't want the code to match
//    
template <key_code K> const bool is_wanted = false;

//
// but for these codes we do want a match (extend as needed)
//
template <> const bool is_wanted<BB_KEY_SPACE> = true;
template <> const bool is_wanted<BB_KEY_ZERO> = true;
template <> const bool is_wanted<BB_KEY_ONE> = true;
template <> const bool is_wanted<BB_KEY_TWO> = true;

//
// constexpr function to work out the minimum value of the enum
// (in case it's a signed type)
//    
static constexpr std::size_t min_value() { 
  return std::numeric_limits<std::underlying_type_t<key_code>>::min();
}

//
// constexpr function to work out the maximum value of the enum
// (in case it's a signed type)
//    
static constexpr std::size_t max_value() { 
  return std::numeric_limits<std::underlying_type_t<key_code>>::max();
}

//
// constexpr function to work out the extent of the enum
// so we can build an array to represent the test for every value
//    
static constexpr std::size_t to_index(key_code k)
{
  return std::size_t (int(k) - min_value());
}
//
// convenience function to find the correct position in our array
// for a given key code
//    
static constexpr std::size_t to_index(key_code k)
{
  return std::size_t (int(k) + min_value());
}


namespace detail {
  //
  // this function builds an array of bools. Index positions for
  // enum values which have been specialised above will be true
  // all others will be false
  //
  template<std::size_t...Is>
  static constexpr auto make_wanted_array(std::index_sequence<Is...>)
  -> std::array<bool, range_size()>
  {
    return 
    {
      is_wanted<static_cast<key_code>(Is - min_value())>...
    };
  }
}

//
// interface version simply calls the impl having constructed the
// correct index sequence
//    
static constexpr auto make_wanted_array()
-> std::array<bool, range_size()>
{
  return detail::make_wanted_array(std::make_index_sequence<range_size()>());
}

//
// this is the function to test whether a key is valid or not
bool valid_key(key_code k)
{
  // this will be done at compile time. There is zero runtime overhead.
  static constexpr auto wanted_array = make_wanted_array();
  // this is simply a load from memory address with register offset
  return wanted_array[to_index(k)];
}

以下是使用-O2:

编译的gcc5.3为valid_key发出的汇编程序
valid_key(key_code):
        movsx   rdi, dil
        movzx   eax, BYTE PTR valid_key(key_code)::wanted_array[rdi+128]
        ret

请注意,它会调整+128。这是因为,在这种情况下,枚举是有符号类型,因此索引必须转换为基数为0的无符号值。

答案 5 :(得分:-1)

首先,您真的现在需要对此进行优化吗?即使你这样做,我怀疑这是你的主要瓶颈。无论如何,它至少看起来不漂亮。要以另一种方式构建您的问题,您基本上是在询问,如何对可能随机的值集执行if检查?

如您事先知道这些值,那么您的任务就是将随机数据建模为更具语义特色的内容。正如评论和答案中所提到的,您可以公开数据的属性,例如范围(但是,编译器可能能够管理它)。

我认为最好的优化是实现Hash function