Else блок и оптимизаторы

184
06 апреля 2018, 18:35

Есть блок однотипных условий вида. Всего условий около 120.

if (event_code == 0xF1)
{
    // action 1
}
else if (event_code == 0xEF)
{
    // action 2
}
...
else if (event_code == 0xDB)
{
    // action 119
}   
else
{
    // print event_code
}

Проблема в условии 119 (event_code == 0xDB). Если в коде разместить это условие в самом конце, то оно не выполняется, а вместо него выполняется условие else, которое выводит на печать event_code = 0xDB. Все касты и приведения типов в порядке, т.к. если разместить данное условие в начале блока, то оно начинает работать...

Пробовал переделать блок на switch-case и получил такой же результат. Собирается проект с помощью icc 14.0.2.176 [IA-32]. В доках на icc никаких ограничений не нашел.

Помогите кто чем может)

Answer 1

Покажу несколько способов оптимизации условий.

  1. Метод двоичного поиска. Мы выстраиваем дерево таким образом, что бы колличество сравнений было минимальное (обратите внимание что б event_code был unsigned)

    if (event_code <= 0xEF) { /*Уровень 1*/
      if (event_code == 0xDB) {/*Уровень 2*/
      } 
      if (event_code == 0xEE) {/*Уровень 2*/
      }
    }else  { /*Уровень 1*/    
      if (event_code == 0xEF) {/*Уровень 2*/
        }
      if (event_code == 0xF1)/*Уровень 2*/
      {
        // action 1
      }
    }

это было дерево {{ EE,EF } , {0xEF, F1 }} На этом примере не так хорошо видно ефект дерева. Вот пример, как вычислить число бит нужных для числа то 0 до 255

     if (a<16){/*1*/
        if (a<4){/*2*/
           if (a<=1)/*3*/ return 1 else return 2;  
        }else{/*2*/ 
           if (a<8)/*3*/ return 3 else return 4;
        }
     }else{/*1*/
        if (a < 64)/*2*/  {
          if (a < 32)/*3*/ return 5; else return 6;
        } else {
           if (a < 128)/*3*/ return 7; else return 8;
         }

Тут за три сравнения ясен результат. Три, не 8, не 256. Так можно успешно оптимизировать число сравнений.

  1. Метод switch. Многие компиляторы хорошо оптимизируют выражение в switch. Думаю вы ничего не потеряете если используете switch. Одни компиляторы стоят "дерево" как показано в [1], другие строят таблицу вызовов как в [3]. Но и бывают случаи когда просто лепят сравнения. Проверить что сгенерировал компилятор можно дизассемблером.

    switch ((unsigned)event_code) {
       case 0xDB:; break;
       case 0xEF:; break; 
       case 0xF1:; break;       
       //...
       }

    Я часто пользовался switch, и... иногда нужно "выйти" из него не вниз, а вообще. Поэтому кроме break, есть ещё два способа "остановить процесс сравнения" - это return и continue (если используется цикл).

  2. Метод массива вызовов.

    void handler_1()  {
      }
    void (*my_handlers[])() = { NULL, NULL /*... */, handler_1 };
    void my_proc() {
       if (my_handlers[(unsigned)event_code] != NULL)
           my_handlers[(unsigned)event_code]();     
       }

    этот метод ефективен если случаев очень много, и они сфокусированы на большом промежутке чисел, например от 0 до 255.

READ ALSO
C++ cgi запуск exe файла

C++ cgi запуск exe файла

Добрый день, подскажите пожалуйста что я делаю не так?

170
Как лучше сравнивать даты?

Как лучше сравнивать даты?

Необходимо в цикле сравнивать даты, в чем у меня может быть ошибка?

176
Qt \ Sql Получение родительских атрибутов

Qt \ Sql Получение родительских атрибутов

Есть таблицы: "Единицы измерения" код наименование

203