Проверки битовой маски более эффективны, чем сравнение чисел? - PullRequest
2 голосов
/ 06 января 2020

Мне сказали, что проверки на основе битовой маски более эффективны, чем сравнение чисел. Это правда?

Проверка "на основе битовой маски":

if (val & IMPORTANT_BIT)
...

Сравнение чисел:

if (val == IMPORTANT_VAL)
...

В обоих случаях я чувствую, что придется извлечь val не хватает памяти в любом случае. Так почему бы битовая маска была более эффективной? Компилятор делает что-то необычное для битовых масок?

Ответы [ 2 ]

2 голосов
/ 06 января 2020

Прежде всего, мы ограничиваем вопрос, предполагая, что IMPORTANT_BIT == IMPORTANT_VAL и все остальные биты val равны 0, так что результат обоих тестов одинаков.

Short ответ : не беспокойтесь об этом.

Длинный ответ:

Это зависит от целевой архитектуры, значения IMPORTANT_BIT и контекста в код выполняется.

x86 имеет инструкцию AND, которая удобно устанавливает флаг Z, и поэтому может использоваться для маскирования и проверки значения против 0 в одной инструкции. Тогда есть инструкция CMP, которая сравнивает значения. Инструкции AND и CMP имеют одинаковую задержку 1 и пропускную способность 4. Таким образом, в x86 будет без разницы , если только компилятору не удастся повторно использовать определенное значение c и смешайте с кодом выше / ниже. Однако разница в производительности будет незначительной.

Сказав это, семантика тестов & и == действительно отличается.

  • if (val & ... test используется для маскировки биты.
  • if (val == ... тест используется для сравнения значений.

Я предлагаю подойти к нему с точки зрения читаемости кода в первую очередь. Если val не является битовым полем, сделайте себе одолжение и не используйте против него &.

2 голосов
/ 06 января 2020

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

// using bool
bool a,b,c,d;
if (a && !b && c && !d) ...

// hoping the compiler knows what we are doing
enum { A= 0x01, B= 0x02, C= 0x04, D= 0x08};
if ((flags&A) && !(flags&B) && (flags&C) && !(flags&D)) ...

// Optimise it ourselves:
enum { A= 0x01, B= 0x02, C= 0x04, D= 0x08};
if ((flags & (A|B|C|D)) == (A|!B|C|!D)) ...

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

Но в любом случае 3-я форма выиграет против первой формы для краткости, а также сэкономит немного места.

Обратите внимание, что ваши два примера не проверяют одно и то же.

A & 5 проверяет, что 4-битный и 1-битный оба установлены, но полностью игнорирует 2-битный и любые старшие биты.

A == 5 проверяет, установлены ли 1-битный и 4-битный, но также проверяет, что ВСЕ ДРУГИЕ биты сброшены.

...