Эффективность булевых сравнений?В С - PullRequest
4 голосов
/ 12 октября 2010

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

В цикле, например, следующий фрагмент:

int i = 0;
while (i < 10) {
    printf("%d\n", i);
    i++;
}

Проверяет ли процессор как (i < 10), так и (i == 10) для каждой итерации? Или он просто проверяет (i < 10) и, если это правда, продолжает?

Если он проверяет оба, не будет:

int i = 0;
while (i != 10) {
    printf("%d\n", i);
    i++;
}

быть более эффективным?

Спасибо!

Ответы [ 9 ]

10 голосов
/ 12 октября 2010

Оба будут переведены в одну инструкцию по сборке.Большинство процессоров имеют инструкции сравнения для МЕНЬШЕ, ЧЕМ МЕНЬШЕ ИЛИ РАВНО, для РАВНО и НЕ РАВНО.

5 голосов
/ 12 октября 2010

Одна из интересных сторон этих вопросов по оптимизации заключается в том, что они часто показывают, почему вы должны писать код для ясности / правильности, прежде чем беспокоиться о влиянии этих операций на производительность (что, к сожалению, не имеет никакой разницы). *

Ваши 2 примера цикла не имеют одинакового поведения:

int i = 0;
/* this will print 11 lines (0..10) */
while (i <= 10) {
    printf("%d\n", i);
    i++;
}

И

int i = 0;
/* This will print 10 lines (0..9) */
while (i != 10) {
    printf("%d\n", i);
    i++;
}

Однако, чтобы ответить на ваш вопрос, почти наверняка производительность двух конструкций будет одинаковой (при условии, что вы исправили проблему, чтобы число циклов было одинаковым). Например, если ваш процессор мог проверять только равенство и было ли одно значение меньше другого за два отдельных шага (что было бы очень необычным процессором), то компилятор, скорее всего, преобразует (i <= 10) в (i < 11) тест - или, может быть, (i != 11) тест.

3 голосов
/ 12 октября 2010

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

Говоря на ваши конкретные вопросы.Во-первых, <= не реализован как тестирование двух операций для < и == отдельно в любом компиляторе C, с которым я встречался в своей карьере.И это включает в себя несколько монументально глупых компиляторов.Обратите внимание, что для целых чисел a <= 5 - это то же условие, что и a < 6, и если целевая архитектура требует, чтобы использовался только <, это то, что будет делать генератор кода.

Ваша вторая проблема, чтоwhile (i != 10) может быть более эффективным, поднимает интересную проблему защитного программирования.Во-первых, нет, это не более эффективно в любой разумной целевой архитектуре.Тем не менее, это повышает вероятность того, что небольшая ошибка вызовет большую ошибку.Подумайте об этом: если какая-то строка кода в теле цикла изменила i, скажем, сделав его больше 10, что может произойти?Сколько времени потребуется для завершения цикла, и будут ли какие-либо другие последствия ошибки?

Наконец, когда вы задаетесь вопросом о подобных вещах, часто стоит выяснить, какой код компилятора вы используете.используют на самом деле генерирует.Большинство компиляторов предоставляют механизм для этого.Для GCC узнайте о параметре -S, который заставит его генерировать код сборки непосредственно вместо создания объектного файла.

0 голосов
/ 13 октября 2010

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

Меньше, чем в вашем коде C, часто превращается в ветвь, если она больше или равна дальней стороне цикла.Если у вашего процессора нет больше или равно, у него может быть ветвь, если больше, и ветвь, если она равна, две инструкции.

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

0 голосов
/ 12 октября 2010

Я пишу цикл на C, и мне просто интересно, как его немного оптимизировать.

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

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

0 голосов
/ 12 октября 2010
// Case I

int i = 0; 
while (i < 10) {
    printf("%d\n", i);
    i++;
    printf("%d\n", i);
    i++;
}

// Case II

int i = 0;
while (i < 10) {
    printf("%d\n", i);
    i++;
}

Код Case I занимает больше места, но быстро, а код Case II занимает меньше места, но медленнее по сравнению с кодом Case I. Потому что в пространстве программирования сложность и сложность времени всегда пропорциональны друг другу. Это означает, что вы должны поставить под угрозу пространство или время.
Таким образом, вы можете оптимизировать сложность времени или пространства, но не то и другое одновременно.

И оба ваших кода одинаковы.

0 голосов
/ 12 октября 2010

Проверяет ли процессор оба (i <10) и (i == 10) для каждой итерации? Или это просто проверить (я <10) и, если это правда, продолжить? </p>

Ни один из них, скорее всего, не проверит (я <11). <code><= 10 как раз для того, чтобы вы могли лучше понять ваш код, поскольку 11 - это магическое число , что на самом деле означает (10+1).

0 голосов
/ 12 октября 2010

Зависит от архитектуры и компилятора.На большинстве архитектур есть одна инструкция для <= или наоборот, которую можно отрицать, поэтому если она переводится в цикл, сравнение, скорее всего, будет только одной инструкцией.(На x86 или x86_64 это одна инструкция)

Компилятор может развернуть цикл в последовательность из десяти раз i++, когда задействованы только постоянные выражения, он даже оптимизирует ++ и оставит толькоКонстанты.

И Ира прав, сравнение действительно пропадает, если задействовано printf, время выполнения которого может составлять миллионы тактов.

0 голосов
/ 12 октября 2010

Операторы <= и <являются одной инструкцией в сборке, не должно быть разницы в производительности. Обратите внимание, что тестирование на 0 может быть немного быстрее на некоторых процессорах, чем тестирование на любую другую константу, поэтому может быть разумным сделать цикл запущенным задом наперед: </p>

int i = 10;
while (i != 0) 
{
    printf("%d\n", i);
    i--;
}

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

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