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

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

if(i++ & 1){
}

vs.

if(i % 2){
}

Можно ли использовать операцию Bitwise в нормальном потоке или условных операторах, таких как for, if и т.д., повысить производительность и будет ли better всегда использовать их там, где это возможно?

4b9b3361

Ответ 1

Если вы не используете древний компилятор, он уже может самостоятельно обрабатывать этот уровень конверсии. То есть современный компилятор может и будет реализовывать i % 2 с помощью поразрядной инструкции AND, если имеет смысл делать это на целевом ЦП (что, справедливо, обычно будет).

Другими словами, не ожидайте увидеть какую-либо разницу в производительности между ними, по крайней мере, с достаточно современным компилятором с достаточно компетентным оптимизатором. В этом случае reasonably тоже имеет довольно широкое определение - даже довольно много компиляторов, которые десятилетиями могут справиться с такой микро-оптимизацией, без труда.

Ответ 2

TL; DR Сначала напишите для семантики, сначала оптимизируйте измеренные горячие точки.

На уровне ЦП единичный модуль и деления являются одними из самых медленных операций. Но вы не пишете на уровне процессора, вместо этого вы пишете на С++, который ваш компилятор переводит в промежуточное представление, которое, наконец, переводится на сборку в соответствии с моделью процессора, для которой вы компилируете.

В этом процессе компилятор будет применять Peephole Optimizations, среди которых фигура Оптимизация сокращения прочности, например (любезность Википедии):

Original Calculation  Replacement Calculation
y = x / 8             y = x >> 3
y = x * 64            y = x << 6
y = x * 2             y = x << 1
y = x * 15            y = (x << 4) - x

Последний пример, пожалуй, самый интересный. В то время как умножение или деление по степеням 2 легко преобразуется (вручную) в операции бит-сдвига, компилятор обычно учится выполнять даже более умные преобразования, о которых вы, вероятно, подумаете сами по себе и которые не так легко распознаются (в самом в меньшей степени, я лично не сразу понимаю, что (x << 4) - x означает x * 15).

Ответ 3

Это явно зависит от процессора, но вы можете ожидать, что побитовые операции никогда не потребуются больше и, как правило, потребуют меньше циклов процессора. В общем случае целые числа / и % значительно медленнее, так как идут инструкции CPU. Тем не менее, при использовании современных процессорных конвейеров, имеющих определенную инструкцию, выполненную ранее, не означает, что ваша программа обязательно работает быстрее.

Лучшей практикой является написать код, который понятен и поддерживается - выражает логику, которую он реализует. Чрезвычайно редко, что такая микро-оптимизация дает ощутимую разницу, поэтому ее следует использовать только в том случае, если профилирование указывает на критическое узкое место, и это доказало свою значительную разницу. Более того, если на какой-то определенной платформе это действительно имело существенное значение, ваш оптимизатор компилятора уже может заменять поразрядную операцию, когда он может видеть этот эквивалент.

Ответ 4

Операторы

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

Ответ 5

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

Таким образом, используйте &, если вы извлекаете биты и используете %, если вы проверяете делимость, то есть значение является четным или нечетным.

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

К сожалению, целочисленное деление незначительно нерегулярно по знаковым значениям, поскольку оно округляется до нуля, а результат изменения% меняет знак в зависимости от первого операнда. Бит-операции, с другой стороны, всегда округляются вниз. Таким образом, компилятор не может просто заменить разделение простой битовой операцией. Вместо этого он может либо вызвать подпрограмму для целочисленного деления, либо заменить ее битовыми операциями дополнительной логикой для обработки нерегулярности. Это может зависеть от уровня оптимизации и от какого из операндов являются константами.

Эта нерегулярность в нуле может даже быть плохой, потому что это нелинейность. Например, у меня недавно был случай, когда мы использовали деление на знаковые значения из АЦП, которые должны были быть очень быстрыми на ARM Cortex M0. В этом случае лучше было бы заменить его правым сдвигом, как для производительности, так и для устранения нелинейности.

Ответ 6

Побитовые операции выполняются намного быстрее. Вот почему компилятор сделает этот трюк (и многие другие) для вас. На самом деле, я думаю, что будет быстрее реализовать его как

(! i & 1)

Аналогично, если вы посмотрите на код сборки, который генерирует ваш компилятор, вы можете видеть такие вещи, как x ^= x вместо x=0. но (надеюсь) вы не собираетесь использовать это в своем коде на С++...

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