Подтвердить что ты не робот

С++ Long switch или искать карту?

В моем приложении на С++ у меня есть некоторые значения, которые действуют как коды для представления других значений. Чтобы перевести коды, я обсуждал использование инструкции switch или stl-карты. Переключатель будет выглядеть примерно так:

int code;
int value;
switch(code)
{
case 1:
    value = 10;
    break;
case 2:
    value = 15;
    break;
}

Карта будет stl::map<int, int>, и перевод будет простым поиском с кодом, используемым в качестве значения ключа.

Какой из них лучше/эффективнее/чище/принято? Почему?

4b9b3361

Ответ 1

Лично я бы использовал карту, поскольку ее использование подразумевает поиск данных - использование переключателя обычно указывает на разницу в поведении программы. Кроме того, изменение отображения данных проще с помощью карты, чем с помощью переключателя.

Если производительность является реальной проблемой, профилирование - это единственный способ получить полезный ответ. Переключатель может быть не быстрее, если неверные предсказания происходят достаточно часто.

Другой подход, чтобы подумать об этом, - это не имеет смысла комбинировать код и связанное с ним значение в структуре данных, особенно если диапазон кодов и значений статический:

struct Code { int code; int value; };

Code c = ...

std::cout << "Code " << c.code << ", value " << c.value << std::end;

Ответ 2

Если ваши коды достаточно смежны и их диапазон позволяет вам намного лучше использовать старомодный простой массив, что-то вроде

int lookup[] = {-1, 10, 15, -1 222};

то оператор switch можно переписать так же, как

value = lookup [code];

все другие варианты в некоторой степени вносят дополнительные затраты.

Ответ 3

Это скорее зависит от того, что такое коды и сколько их есть. Хорошие компиляторы используют различные трюки, которые они используют для оптимизации операторов switch, некоторые из которых они не будут использовать с прямыми операциями if/then. Большинство из них достаточно яркие, чтобы выполнять простые математические вычисления или использовать таблицы поиска/перехода для случая 0, 1, 2 или случая 3, 6, 9, например.

Конечно, некоторые не делают этого, и многие легко могут быть сорваны необычными или нерегулярными наборами значений. Также, если код для обработки нескольких случаев выглядит очень похожим, вырезание и вставка могут привести к проблемам обслуживания. Если у вас много кодов, но они могут быть разделены алгоритмически на группы, вы можете рассмотреть несколько/вложенных операторов switch, например, а не:

switch (code) {
    case 0x0001: ...
    case 0x0002: ...
    ...
    case 0x8001: ...
    case 0x8002: ...
    ...
}

Вы можете использовать:

if (code & 0x8000) {
    code &= ~0x8000;
    switch (code) {
        case 0x0001: ... // actually 0x8001
        case 0x0002: ... // actually 0x8002
        ...
    }
}
else {
    switch (code) {
        case 0x0001: ...
        case 0x0002: ...
        ...
    }
}

Многие интерпретаторы языков декодируют коды операций таким образом, поскольку один байтовый код операции может иметь дополнительную информацию, упакованную в различные биты, и переписывание всех возможных комбинаций и их обработчиков будет повторяющимся и хрупким. С другой стороны, чрезмерное разбиение бит может победить любую оптимизацию компилятором и быть контрпродуктивным.

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

Ответ 4

Оператор switch будет быстрее, но если это не будет в узком месте вашего приложения, вам это не должно волновать.

Ищите то, что упрощает работу вашего кода в долгосрочной перспективе.

Ваш образец слишком короткий, чтобы сделать какой-либо значимый вызов в этом отношении.

Ответ 5

Я частично отношусь к поисковым таблицам, потому что необычно длинные операторы switch кажутся мне путать разделение между кодом и данными. Я также считаю, что таблицы лучше подходят для будущих изменений и технического обслуживания.

Все ИМХО, конечно.

Ответ 6

Я предлагаю использовать статическую, постоянную таблицу пар. Это еще одна форма таблицы поиска:

struct Table_Entry
{
    int code;
    int value;
};

static const Table_Entry  lookup_table[] =
{
  {1, 10},
  {2, 15},
  {3, 13},
};

static const unsigned int NUM_TABLE_ENTRIES =
    sizeof(lookup_table) / sizeof(lookup_table[0]);

Преимущество этого заключается в том, что таблица генерируется во время компиляции, в отличие от std::map, которая должна быть инициализирована во время выполнения. Если величины велики, вы можете использовать std::lower_bound для поиска записи при условии, что таблица упорядочена.

Другим преимуществом является то, что этот метод управляется данными. Данные могут изменяться без изменений в поисковой системе. Изменения в коде или процессе могут потребовать серьезного тестирования регрессии, но изменения данных могут не быть; YMMV.

Это похоже на то, что может генерировать компилятор.

Ответ 7

Если вы можете использовать tr1, вы можете использовать unordered_map для хэша значений (хеширование int также может быть очень быстрым), что должно сделать большинство поисковых запросов постоянным.

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

Ответ 8

Я говорю, что если все, что вы делаете, присваивает значение. Моя причина в том, что она расширяема без изменения кода, который не является вашим оператором switch.

btw, как насчет enum?

Ответ 9

Я думаю, что сгенерированный код структуры case-switch может расти достаточно большим, если количество "кодов" становится большим, и в этом случае я думаю, что stl:: map более уместен.

Ответ 10

Обычно я предлагаю массив карт или поиска или, возможно, даже гибридный монстр поиска (если вы, конечно, будете оптимизировать скорость или размер кода, а не читабельность), но стоит отметить, что новые версии GCC достаточно умен, чтобы заменить назначения коммутатора/случая, подобные этим, на таблицы поиска. Хотя это не так хорошо для полностью разреженных ключей, может быть, если вы находитесь в ключах, например: 0, 1, 2, 3, 4, 5, 11, 12, 13, 14, 15, 193, 194, 195, 196, 197, 198...

Конечно, если вы могли бы любить арифметику, чтобы сделать конверсию, еще лучше (обычно). Никогда не упускайте из виду смещение, замену суждения или более традиционную математику, когда делаете что-то подобное.

Ответ 11

  • Прочитайте целые числа в виде массива/вектора
  • сортировать массив/вектор
  • использовать bsearch для базового массива