Является ли x> = 0 более эффективным, чем x> -1? - PullRequest
6 голосов
/ 29 ноября 2011

Сравнение в C ++ с целым числом x >= 0 более эффективно, чем x > -1?

Ответы [ 6 ]

19 голосов
/ 29 ноября 2011

короткий ответ: нет.

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

пример кода:

int func_ge0(int a) {
    return a >= 0;
}   

int func_gtm1(int a) {
    return a > -1; 
}

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

   % gcc -S -O2 -fomit-frame-pointer foo.cc

дает следующее:

_Z8func_ge0i:
.LFB0:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    movl    4(%esp), %eax
    notl    %eax
    shrl    $31, %eax
    ret
    .cfi_endproc

против

_Z9func_gtm1i:
.LFB1:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    movl    4(%esp), %eax
    notl    %eax
    shrl    $31, %eax
    ret
    .cfi_endproc

(компилятор: g ++ - 4.4)

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

7 голосов
/ 29 ноября 2011

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

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

Кстати: правильно указано, что x>-1 может вызвать проблемы, если x равно unsigned. Это может быть неявно приведено к signed (хотя вы должны получить предупреждение об этом), что приведет к неверному результату.

6 голосов
/ 29 ноября 2011

В последний раз, когда я отвечал на такой вопрос, я просто написал «мера» и заполнял периодами, пока SO не принял его.

Этот ответ был понижен в голосовании 3 раза в течение нескольких минут и удален (наряду с хотя бы еще одним ответом на вопрос) модератором SO.

Тем не менее, альтернативы измерениям нет.

Так что это единственный возможный ответ.

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

Или, может быть, мне следует упомянуть, что у большинства процессоров есть специальная инструкция для сравнения с нулем, и, тем не менее, она не позволяет сделать вывод о производительности фрагментов кода?

Ну, я думаю, что я на этом остановлюсь. Помните: мера. И не оптимизируйте преждевременно!



РЕДАКТИРОВАТЬ : поправка с пунктами, упомянутыми @MooingDuck в комментарии .

Вопрос:

Сравнение в C ++ с int на x >= 0 более эффективно, чем x > -1?

Что не так с вопросом

Дональд Кнут, автор классической трехтомной работы «Искусство компьютерного программирования», однажды написал [1],

& ldquo; Мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация - корень всех зол & rdquo;

Как эффективный x >= 0 по сравнению с x > -1 чаще всего не имеет значения. То есть скорее всего, это неправильная вещь.

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

Почему фокус вопроса неправильный

Ясность влияет на вероятность правильности. Любой код может быть выполнен произвольно быстро, если он не должен быть правильным. Поэтому правильность является наиболее важной и означает, что ясность очень важна & ndash; гораздо важнее, чем брить наносекунду времени выполнения & hellip;

И два выражения не эквивалентны по отношению к. ясность и WRT. к их шансу быть правильными.

Если x является целым числом со знаком, то x >= 0 означает точно так же, как x > -1. Но если x является целым числом без знака, например, типа unsigned, тогда x > -1 означает x > static_cast<unsigned>(-1) (через неявное продвижение ), что, в свою очередь, означает x > std::numeric_limits<unsigned>::max(). По-видимому, это не то, что программист хотел выразить!

Еще одна причина, по которой акцент делается неправильно (речь идет о микроэффективности, в то время как она должна быть ясной), заключается в том, что основное влияние на эффективность, в целом, оказывает не время отдельных операций (за исключением некоторых случаев динамического распределения) и от еще более медленных дисковых и сетевых операций), но от алгоритмическая эффективность . Например, писать & hellip;

string s = "";
for( int i = 0;  i < n;  ++i ) { s = s + "-"; }

довольно неэффективно, поскольку использует время, пропорциональное квадрату n , O ( n 2 ), квадратичное время .

Но вместо этого пишу &

string s = "";
for( int i = 0;  i < n;  ++i ) { s += "-"; }

уменьшает время до пропорционального n , O ( n ), линейное время .

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

string s( n, '-' );

Вау!

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

Как выяснить ответ на вопрос

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

Например, вы можете посмотреть машинный код, запустив программу в отладчике, или выиспользуйте опцию Соответственно, чтобы попросить компилятор сгенерировать листинг на ассемблере.Примечание для g ++: опция -masm=intel удобна для указания компилятору не генерировать ненадежную сборку синтаксиса AT & T, а вместо этого сборку синтаксиса Intel.Например, ассемблер Microsoft использует расширенный синтаксис Intel.

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

Поэтому для обычного программиста единственным выходом является мера .

Измерять, измерять, измерять!

И в целом это включает в себя выполнение измеряемой вещи миллион раз и деление на миллион.

В противном случае время запуска ивремя взлета будет доминировать, а результатом будет мусор.

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

Почему измерение является правильным подходом

Допустим, теоретические соображения в ответе SO указывают на то, что x >= -1 будет медленнее, чем x > 0.

Компилятор может превзойти любое такое теоретическое соображение, генерируя ужасный код для этого x > 0, возможно, из-за возможности контекстной «оптимизации», которую он затем (к сожалению!) Распознает.

Процессор компьютера также может испортить прогноз.

Так что в любом случае вам придется измерить.

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

Почему этот подробный ответ, хотя и кажется полезным, ИМХО на самом деле не

Лично я предпочел бы одно слово «мера» в качестве ответа.

Потому что это то, к чему это сводится.

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

Ссылки:

[1] Кнут, Дональд. Структурированное программирование с переходом к операторам , ACM Journal Computing Surveys, том 6, № 4, декабрь 1974. с.268.

3 голосов
/ 29 ноября 2011

Ваш компилятор может решить, как его реализовать (какие инструкции по сборке использовать). Из-за этого нет никакой разницы. Один компилятор может реализовать x > -1 как x >= 0, а другой может реализовать x >= 0 как x > -1. Если есть какая-либо разница (маловероятная), ваш компилятор выберет лучший.

2 голосов
/ 29 ноября 2011

Они должны быть эквивалентны.Оба будут преобразованы компилятором в одну инструкцию по сборке (без учета того, что оба должны будут загрузить x в регистр).На любом современном процессоре дня есть инструкция «больше чем» и инструкция «больше или равно».А поскольку вы сравниваете его с постоянным значением, это займет столько же времени.

Не беспокойтесь о мелочах, найдите большие проблемы с производительностью (например, разработка алгоритма) и атакуйте их, посмотритепо закону Амдала.

1 голос
/ 29 ноября 2011

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

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

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