C или С++: для переменной цикла

Мой вопрос очень простой. В С или C++:

Допустим, цикл for выглядит следующим образом:

for(int i=0; i<someArray[a+b]; i++) {
 ....
 do operations;
}

Мой вопрос заключается в том, выполняется ли вычисление a+b для каждого цикла for или оно вычисляется только один раз в начале цикла?

Для моих требований значение a+b является постоянным. Если вычисляется a+b и к значению someArray[a+b] обращаются каждый раз в цикле, я бы использовал временную переменную для someArray[a+b] чтобы повысить производительность.

Ответ 1

Вы можете узнать, когда вы смотрите на сгенерированный код

g++ -S file.cpp

и

g++ -O2 -S file.cpp

Посмотрите на выход file.s и сравните две версии. Если someArray[a+b] можно свести к постоянному значению для всех циклов цикла, оптимизатор обычно сделает это и вытащит его во временную переменную или зарегистрирует.

Ответ 2

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

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

Ответ 3

выполняется для каждого цикла for или вычисляется только один раз в начале цикла?

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

Ответ 4

Во-первых, в стандартах на C и С++ не указывается, как реализация должна оценивать i<someArray[a+b], так что результат должен быть таким, как если бы он выполнялся каждой итерацией (при условии, что программа соответствует требованиям другого языка).

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

В-третьих, некоторые вещи могут помешать этой цели, в том числе:

  • Если a, b или someArray объявлены с областью видимости вне функции (например, объявлены в области файла), а код в цикле вызывает другие функции, реализация C или С++ может быть не удалось определить, изменяются ли в цикле a, b или someArray.
  • Если адрес a, b или someArray или его элементы взяты, реализация C или С++ может быть неспособна определить, используется ли этот адрес для изменения этих объектов. Это включает в себя возможность того, что someArray является массивом, переданным в функцию, поэтому его адрес известен другим объектам вне функции.
  • Если a, b или элементы someArray являются volatile, реализация C или С++ должна предполагать, что они могут быть изменены в любое время.

Рассмотрим этот код:

void foo(int *someArray, int *otherArray)
{
    int a = 3, b = 4;
    for(int i = 0; i < someArray[a+b]; i++)
    {
        … various operations …
        otherArray[i] = something;
    }
}

В этом коде реализация C или С++, как правило, не может знать, указывает ли otherArray на тот же массив (или перекрывающуюся часть) как someArray. Поэтому он должен предположить, что otherArray[i] = something; может изменить someArray[a+b].

Обратите внимание, что я ответил на большее выражение someArray[a+b], а не только на ту часть, о которой вы просили, a+b. Если вас беспокоит только a+b, то, очевидно, важны только факторы, которые влияют на a и b.

Ответ 5

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

// C++ lets you create a const reference; you cannot do it in C, though
const some_array_type &last(someArray[a+b]);
for(int i=0; i<last; i++) {
    ...
}

Ответ 6

В зависимости от того, насколько хорош компилятор, какие уровни оптимизации вы используете и как объявляются a и b.

Например, если a и/или b имеет квалификатор volatile, тогда компилятор должен читать его/их каждый раз. В этом случае компилятор не может оптимизировать его со значением a+b. В противном случае посмотрите на код, сгенерированный компилятором, чтобы понять, что делает ваш компилятор.

Нет стандартного поведения в том, как это вычисляется ни в C не С++.

Ответ 7

Готов поспорить, что если a и b не меняются по циклу, он оптимизируется. Более того, если someArray[a+b] не затрагивается, он также оптимизирован. Это на самом деле более важно, так как операции ввода довольно дороги.

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

Ответ 8

Он вычисляет каждый раз. Используйте переменную:)

Ответ 9

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

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

Ответ 10

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

Создание как оптимизированного, так и неоптимизированного кода сборки - самый простой способ проверить. Однако самое лучшее, что нужно сделать, это не заботиться, потому что стоимость этой суммы настолько невероятно дешевая. Современные процессоры очень быстрые, а медленная работа заключается в извлечении данных из ОЗУ в кеш. Если вы хотите оптимизировать свой код, профайл его; не думаю.

Ответ 11

Расчет a+b будет выполняться на каждой итерации цикла, а затем поиск в someArray выполняется на каждой итерации цикла, поэтому вы, вероятно, могли бы сэкономить много времени процессора за счет временной переменной например, вне цикла, если массив является массивом ints say):

int myLoopVar = someArray[a+b]
for(int i=0; i<myLoopVar; i++) 
{
 ....
 do operations;
}

Очень упрощенное объяснение:

Если значение в позиции массива a+b было простым, например, 5, это было бы 5 вычислений и 5 поисковых запросов, поэтому 10 операций, которые будут заменены на 8, используя переменную вне цикла (5 обращений (1) за итерацию цикла), 1 вычисление a+b, 1 поиск и 1 назначение новой переменной) не так сильно экономит. Если, однако, вы имеете дело с более крупными значениями, например, значением, хранящимся в массиве, в a+b id 100, вы потенциально выполняете 100 вычислений и 100 запросов, против 103 операций, если у вас есть переменная вне цикла (100 обращений ( 1 на итерацию цикла), 1 вычисление a+b, 1 поиск и 1 назначение новой переменной).

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

Сообщите мне, если вам нужна дополнительная информация:)

Ответ 12

для следующего кода:

int a = 10, b = 10;
for(int i=0; i< (a+b); i++) {} // a and b do not change in the body of loop

вы получаете следующую сборку:

L3:
    addl    $1, 12(%esp)     ;increment i
L2:
    movl    4(%esp), %eax    ;move a to reg AX
    movl    8(%esp), %edx    ;move b to reg BX
    addl    %edx, %eax       ;AX = AX + BX, i.e. AX = a + b
    cmpl    12(%esp), %eax   ;compare AX with i
    jg  L3                   ;if AX > i, jump to L3 label

если вы применяете оптимизацию компилятора, вы получаете следующую сборку:

    movl    $20, %eax   ;move 20 (a+b) to AX
L3:
    subl    $1, %eax    ;decrement AX
    jne L3              ;jump if condition met
    movl    $0, %eax    ;move 0 to AX

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

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

если вам не нравится оптимизировать цикл через действие компилятора (-On), тогда объявление одной переменной и ее назначение a+b уменьшит вашу сборку инструкцией или двумя.

int a = 10, b = 10;
const int c = a + b;
for(int i=0; i< c; i++) {}

сборка:

L3:
    addl    $1, 12(%esp)
L2:
    movl    12(%esp), %eax
    cmpl    (%esp), %eax
    jl  L3
    movl    $0, %eax

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