[c++] Является <быстрее, чем <=?



Answers

Исторически (мы говорим о 1980-х и начале 1990-х годов), были некоторые архитектуры, в которых это было правдой. Корневая проблема заключается в том, что целочисленное сравнение реализуется посредством целочисленных вычитаний. Это приводит к следующим случаям.

Comparison     Subtraction
----------     -----------
A < B      --> A - B < 0
A = B      --> A - B = 0
A > B      --> A - B > 0

Теперь, когда A < B вычитание должно занять высокий бит для правильного вычитания, так же, как вы переносите и занимаете при добавлении и вычитании вручную. Этот «заимствованный» бит обычно упоминается как бит переноса и может быть проверен инструкцией по ветвлению. Второй бит, называемый нулевым битом , будет установлен, если вычитание было тождественно нулевым, что подразумевало равенство.

Как правило, по меньшей мере две условные инструкции ветвления, одна для ветвления на переносном бите и одна на нулевом бите.

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

Comparison     Subtraction  Carry Bit  Zero Bit
----------     -----------  ---------  --------
A < B      --> A - B < 0    0          0
A = B      --> A - B = 0    1          1
A > B      --> A - B > 0    1          0

Таким образом, реализация ветви для A < B может быть выполнена в одной инструкции, поскольку бит переноса является ясным только в этом случае, то есть,

;; Implementation of "if (A < B) goto address;"
cmp  A, B          ;; compare A to B
bcz  address       ;; Branch if Carry is Zero to the new address

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

;; Implementation of "if (A <= B) goto address;"
cmp A, B           ;; compare A to B
bcz address        ;; branch if A < B
bzs address        ;; also, Branch if the Zero bit is Set

Таким образом, на некоторых машинах использование сравнения «меньше» может сохранить одну машинную инструкцию . Это было актуально в эпоху процессорной скорости суб-мегагерца и соотношение скоростей между процессорами и памятью 1: 1, но сегодня это почти не имеет значения.

Question

Я читаю книгу, в которой автор говорит, что if( a < 901 ) быстрее, чем if( a <= 900 ) .

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




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




Они имеют одинаковую скорость. Возможно, в какой-то особой архитектуре, что он / она сказал правильно, но в семье x86, по крайней мере, я знаю, что они одинаковы. Потому что для этого CPU выполнит субстрат (a - b), а затем проверит флаги регистра флага. Два бита этого регистра называются ZF (нулевой флаг) и SF (флаг знака), и это делается за один цикл, потому что он будет делать это с одной маской.




Другие ответы были сосредоточены на архитектуре x86 , и я не знаю, как архитектура ARM (которая, по-видимому, является вашим примером ассемблером) достаточно хорошо, чтобы точно комментировать код, созданный, но это пример micro-optimisation которая представляет собой очень архитектуру и, скорее всего, будет анти-оптимизацией, поскольку она должна быть оптимизацией .

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

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

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

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




Возможно, автор этой неназванной книги прочитал, что a > 0 работает быстрее, чем a >= 1 и считает, что это истинно универсально.

Но это связано с тем, что задействован 0 (поскольку CMP может, в зависимости от архитектуры, заменяться, например, на OR ), а не из-за < .




Я вижу, что это не так быстро. Компилятор генерирует один и тот же машинный код в каждом условии с другим значением.

if(a < 901)
cmpl  $900, -4(%rbp)
jg .L2

if(a <=901)
cmpl  $901, -4(%rbp)
jg .L3

Мой пример if от GCC на платформе x86_64 на Linux.

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

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

int b;
if(a < b)
cmpl  -4(%rbp), %eax
jge   .L2

if(a <=b)
cmpl  -4(%rbp), %eax
jg .L3





Related