Замедляют ли временные переменные мою программу?
предположим, что у меня есть следующий код C:
int i = 5;
int j = 10;
int result = i + j;
Если я зацикливаюсь на этом много раз, было бы быстрее использовать int result = 5 + 10
? Я часто создаю временные переменные, чтобы сделать мой код более читаемым, например, если две переменные были получены из некоторого массива с использованием некоторого длинного выражения для вычисления индексов. Это плохая производительность в C? Как насчет других языков?
5 ответов:
современный оптимизирующий компилятор должен оптимизировать эти переменные, например, если мы используем в следующем примере в godbolt С
gcc
С помощью-std=c99 -O3
флаги (посмотреть его в прямом эфире):#include <stdio.h> void func() { int i = 5; int j = 10; int result = i + j; printf( "%d\n", result ) ; }
это приведет к следующей сборке:
movl , %esi
для расчета
i + j
, это форма постоянной распространения.обратите внимание, я добавил
printf
так что у нас есть сторона эффект, иначеfunc
был бы оптимизирован прочь:func: rep ret
эти оптимизации разрешены под как-если правило, что требует от компилятора только эмуляции наблюдаемого поведения программы. Об этом говорится в проекте стандартного раздела C99
5.1.2.3
выполнение программы он говорит:в абстрактной машине все выражения вычисляются так, как указано семантика. Фактическая потребность в реализации не оценивать часть выражение, если оно может вывести, что его значение не используется и что нет необходимые побочные эффекты произведены (включая любое причиненное путем вызывать а функция или доступ к изменчивому объекту).
Смотрите также: Оптимизация Кода C++: Constant-Folding
Это простая задача для оптимизации для оптимизации компилятора. Он удалит все переменные и заменит
result
с15
.постоянное сворачивание форма SSA это в значительной степени самая основная оптимизация есть.
приведенный вами пример легко оптимизировать для компилятора. Использование локальных переменных для кэширования значений, извлеченных из глобальных структур и массивов, может фактически ускорить выполнение вашего кода. Если, например, вы извлекаете что-то из сложной структуры внутри цикла for, где компилятор не может оптимизировать, и вы знаете, что значение не меняется, локальные переменные могут сэкономить довольно много времени.
Вы можете использовать GCC (другие компиляторы тоже) для создания промежуточной сборки код и посмотреть, что компилятор на самом деле делает.
Существует обсуждение того, как включить списки сборки здесь:использование GCC для создания читаемой сборки?
Это может быть поучительно, чтобы изучить сгенерированный код и посмотреть, что компилятор на самом деле делает.
в то время как все виды тривиальных различий в коде могут возмущать поведение компилятора способами, которые мягко улучшают или ухудшают производительность, в принципе это не должно иметь никакого значения для производительности, используете ли вы временные переменные, как это, пока значение программы не изменяется. Хороший компилятор должен генерировать тот же или сопоставимый код в любом случае, если вы намеренно не строите с оптимизацией, чтобы получить машинный код, который как можно ближе к источник (например, для целей отладки).
вы страдаете той же проблемой, что и я, когда я пытаюсь узнать, что делает компилятор-вы делаете тривиальную программу, чтобы продемонстрировать проблему, и изучаете вывод сборки компилятора, только чтобы понять, что компилятор оптимизировал все, что вы пытались заставить его сделать. Вы можете найти даже довольно сложную операцию в Main() сводится к сути:
push "%i" push 42 call printf ret
ваш исходный вопрос не "что происходит с
int i = 5; int j = 10...
?"но" делают временные переменные вообще понести штраф во время выполнения?"ответ, вероятно, нет. Но вам нужно будет посмотреть на вывод сборки для вашего конкретного, нетривиального кода. Если ваш процессор имеет много регистров, таких как ARM, то i и j, скорее всего, будут в регистрах, точно так же, как если бы эти регистры хранили возвращаемое значение функции напрямую. Например:
int i = func1(); int j = func2(); int result = i + j;
почти наверняка будет точно такой же машинный код, как:
int result = func1() + func2();
Я предлагаю вам использовать временные переменные, если они облегчают понимание и поддержку кода, и если вы действительно пытаетесь затянуть цикл, вы все равно будете смотреть на вывод сборки, чтобы выяснить, как добиться максимальной производительности. Но не жертвуйте удобочитаемостью и ремонтопригодностью в течение нескольких наносекунд, если в этом нет необходимости.