在 C 语言中,为什么n++
执行速度比n=n+1
?
(int n=...; n++;)
(int n=...; n=n+1;)
我们的老师在今天的课上问了这个问题。(这不是作业)
在 C 语言中,为什么n++
执行速度比n=n+1
?
(int n=...; n++;)
(int n=...; n=n+1;)
我们的老师在今天的课上问了这个问题。(这不是作业)
如果您正在使用“石器时代”编译器,那将是正确的......
在“石器时代”的情况下:比
机器通常拥有的速度
++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
为++n
,它会做的比你想象的还要多!!
同样在今天的乱序处理器上——尽管是“石器时代”编译器的情况——在许多情况下运行时可能根本不会受到影响!!
在 x86 的 GCC 4.4.3 上,无论有没有优化,它们都编译为完全相同的汇编代码,因此执行时间相同。正如您在程序集中看到的那样,GCC 只是简单地转换n++
为n=n+1
,然后将其优化为单指令添加(在 -O2 中)。
您的讲师建议n++
更快,仅适用于非常旧的非优化编译器,这些编译器不够聪明,无法为n = n + 1
. 这些编译器在 PC 世界中已经过时多年,但仍然可以在奇怪的专有嵌入式平台上找到。
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 $1, %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 $1, %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 $1, n
popl %ebp
ret
.size nplusplus, .-nplusplus
.p2align 4,,15
.globl nplusone
.type nplusone, @function
nplusone:
pushl %ebp
movl %esp, %ebp
addl $1, 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
(读取、添加和存储操作)生成不同的代码。但是对于普通变量,编译器应该将两种形式优化为相同的东西。
它不是真的。编译器将针对目标架构进行特定的更改。像这样的微优化通常有可疑的好处,但重要的是,肯定不值得程序员花时间。
实际上,原因是运算符对后缀的定义与对前缀的定义不同。 ++n
将递增“n”并返回对“n”的引用,而n++
将递增“n”将返回“n”的const
副本。因此,该短语n = n + 1
将更有效率。但我不得不同意上面的海报。好的编译器应该优化掉一个未使用的返回对象。
在 C 语言中,n++
表达式的副作用在定义上等同于表达式的副作用n = n + 1
。由于您的代码仅依赖于副作用,因此很明显正确的答案是这些表达式始终具有完全相同的性能。(不管编译器中的任何优化设置,顺便说一句,因为这个问题与任何优化完全无关。)
只有当编译器有意(并且恶意地!)试图引入这种差异时,这些表达式的性能上的任何实际差异才有可能。但在这种情况下,它当然可以采用任何一种方式,即编译器的作者想要扭曲它的任何一种方式。
I think it's more like a hardware question rather than software... If I remember corectly, in older CPUs the n=n+1 requires two locations of memory, where the ++n is simply a microcontroller command... But I doubt this applies for modern architectures...
All those things depends on compiler/processor/compilation directives. So make any assumptions "what is faster in general" is not a good idea.