Почему n++ выполняется быстрее, чем n=n+1?
на языке C, почему n++
выполнить быстрее, чем n=n+1
?
(int n=...; n++;)
(int n=...; n=n+1;)
наш инструктор задал этот вопрос в сегодняшнем классе. (это не домашнее задание)
10 ответов
это было бы верно, если вы работаете на "каменного века" компилятора...
в случае "каменного века":++n
быстрее n++
быстрее n=n+1
Машина обычно имеет increment x
а также add const to x
- в случае
n++
, у вас будет только 2 доступа к памяти (чтение n, inc n, запись n ) - в случае
n=n+1
, у вас будет 3 доступа к памяти (читать n, читать const, добавить n и const, напишите n)
но сегодняшний компилятор автоматически преобразует n=n+1
to ++n
, и это сделает больше, чем вы можете себе представить!!
также на сегодняшних неработающих процессорах-несмотря на случай "каменного века" компилятор - среда выполнения не может быть затронуты на всех во многих случаях!!
на GCC 4.4.3 для x86, с и без оптимизации, они компилируются в тот же самый код сборки и, таким образом, занимают столько же времени для выполнения. Как вы можете видеть в сборке, GCC просто преобразует n++
на n=n+1
, затем оптимизирует его в одну инструкцию add (в-O2).
предложение вашего инструктора, что n++
быстрее применяется только к очень старым, не оптимизирующим компиляторам, которые не были достаточно умными, чтобы выбрать обновление на месте инструкции для n = n + 1
. Эти компиляторы устарели в мире ПК в течение многих лет, но все еще могут быть найдены для странных проприетарных встроенных платформ.
C код:
int n;
void nplusplus() {
n++;
}
void nplusone() {
n = n + 1;
}
сборка вывода (без оптимизации):
.file "test.c"
.comm n,4,4
.text
.globl nplusplus
.type nplusplus, @function
nplusplus:
pushl %ebp
movl %esp, %ebp
movl n, %eax
addl , %eax
movl %eax, n
popl %ebp
ret
.size nplusplus, .-nplusplus
.globl nplusone
.type nplusone, @function
nplusone:
pushl %ebp
movl %esp, %ebp
movl n, %eax
addl , %eax
movl %eax, n
popl %ebp
ret
.size nplusone, .-nplusone
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
выходная сборка (с оптимизацией-O2):
.file "test.c"
.text
.p2align 4,,15
.globl nplusplus
.type nplusplus, @function
nplusplus:
pushl %ebp
movl %esp, %ebp
addl , n
popl %ebp
ret
.size nplusplus, .-nplusplus
.p2align 4,,15
.globl nplusone
.type nplusone, @function
nplusone:
pushl %ebp
movl %esp, %ebp
addl , n
popl %ebp
ret
.size nplusone, .-nplusone
.comm n,4,4
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
компилятор оптимизирует n + 1
в небытие.
Вы имеете в виду n = n + 1
?
Если это так, они будут компилироваться в идентичную сборку. (Предполагая, что оптимизация включена и что это операторы, а не выражения)
кто говорит, что это так? Ваш компилятор оптимизирует все это, действительно, делая это спорным вопросом.
современные компиляторы должны уметь распознавать обе формы как эквивалентные и преобразовывать их в формат, который лучше всего работает на вашей целевой платформе. Существует одно исключение из этого правила: переменные обращения, которые имеют побочные эффекты. Например, если n
- это некоторый аппаратный регистр, сопоставленный с памятью, чтение из него и запись в него могут сделать больше, чем просто передача значения данных (чтение может очистить прерывание, например). Вы бы использовали volatile
ключевое слово, чтобы компилятор знал, что он должен быть осторожен в оптимизации доступа к n
и в этом случае компилятор может генерировать другой код n++
(операция инкремента) и n = n + 1
(операции чтения, добавления и хранения). Однако для нормальных переменных компилятор должен оптимизировать обе формы до одного и того же.
на самом деле нет. Компилятор внесет изменения, характерные для целевой архитектуры. Микро-оптимизации, как это часто имеют сомнительные преимущества, но, что важно, конечно, не стоит времени программиста.
на самом деле, причина в том, что оператор определяется по-разному для post-fix, чем для pre-fix. ++n
увеличит "n "и вернет ссылку на" n " в то время как n++
приращение "n" будет возвращать const
копия "n". Отсюда и фраза n = n + 1
будет более эффективным. Но я должен согласиться с вышеприведенными плакатами. Хорошие компиляторы должны оптимизировать неиспользуемый объект return.
на языке C побочный эффект n++
выражения по определению эквивалент побочного эффекта n = n + 1
выражение. Поскольку ваш код полагается только на побочные эффекты, сразу очевидно, что правильный ответ заключается в том, что эти выражения всегда имеют точно эквивалентную производительность. (Независимо от каких-либо настроек оптимизации в компиляторе, кстати, поскольку проблема не имеет абсолютно никакого отношения к какой-либо оптимизации.)
никакого практического различия в производительность этих выражений возможна только в том случае, если компилятор намеренно (и злонамеренно!) пытается ввести это расхождение. Но в этом случае он может пойти в любом случае, конечно, т. е. в любом случае автор компилятора хотел его исказить.
Я думаю, что это больше похоже на аппаратное, а не программное обеспечение... Если я правильно помню, в старых процессорах n=n+1 требует двух местоположений памяти, где ++n-это просто команда микроконтроллера... Но я сомневаюсь, что это применимо к современной архитектуре...
все это зависит от директив компилятора/процессора/компиляции. Поэтому делать какие-либо предположения "что быстрее вообще" - не очень хорошая идея.