Оптимизация кода; переключиться против если - PullRequest
5 голосов
/ 30 января 2011

У меня есть вопрос о том, использовать ли 'case' или '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', я могу разместить наиболее вероятные варианты сверху.

Ответы [ 7 ]

11 голосов
/ 30 января 2011

Вы не можете использовать операторы 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;
     }
}
5 голосов
/ 30 января 2011

Вы можете использовать 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) { /* ... */ }

Если честно, если список параметров не слишком велик или эта функция вызывается непристойно несколько раз, это того не стоит.

Помните: сначала измерьте; оптимизировать позже (только при необходимости).

4 голосов
/ 30 января 2011

Вы можете поместить все значения в 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);
}
2 голосов
/ 30 января 2011

Предполагая, что это действительно имеет значение:

Поскольку strcmp медленный, но целочисленное сравнение быстрое: если все ваши команды имеют длину 4 символа - что вписывается в 32-битное целое число - вы можете привести каждую строку к32-разрядное число, а затем переключаться на основе этого.

В противном случае есть два основных способа быстрого сравнения строки с большим количеством строк-кандидатов:

  • Сохранение кандидатов вхеш-таблица.

или

  • Сортировка кандидатов, отсортированных по алфавиту в массиве.Затем вы можете выполнить двоичный поиск по массиву, используя результат strcmp, чтобы либо найти совпадение, либо исключить половину оставшихся кандидатов.

В качестве примечания - компиляторы, такие каккак MSVC и GCC, реализовали оптимизацию на коммутаторах, которая проверяет условия коммутатора с помощью двоичного поиска.Таким образом, оператор switch, скажем, с 256 элементами, будет оптимизирован до максимум 8 операций сравнения.

2 голосов
/ 30 января 2011

Насколько я понял, было два вопроса.Оптимизация и if / switch.

Прежде всего, оптимизация кода является дорогостоящим процессом.Оптимизируйте только те части кода, которые очевидны.Я сомневаюсь в этом в этом случае.Похоже, вы отправляете текстовый идентификатор для принятия решения, что делать с сообщением.Откуда пришло сообщение?IPC, XML, файл?Что вы собираетесь делать с этим сообщением?Насколько эффективен код обработки содержимого сообщения?В коде должны быть места, которые требуют больше ресурсов, чем сравнение строк.

Вы пробовали некоторые анализаторы производительности, такие как Purify, gperf, cachegrind?

Что касается if / switch: switch работает толькоцелочисленных типов.(char, short, int, long, enum)

2 голосов
/ 30 января 2011

Еще одна альтернатива, с которой я недавно столкнулся и которая может вам не понравиться:

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;
}
2 голосов
/ 30 января 2011

Вы можете очень хорошо использовать «enum» для этих строк. затем используйте операторы регистра переключателей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...