哪个更快:增量或带加法运算的方程式
Which is faster: Increment or equation with addition arithmetic
示例:
a : ++i;
b : i++;
c : i += 1;
d : i = i + 1;
假设abcd每一个都被完全同时调用,先执行哪一个?
现代编译器足够聪明,可以优化所有四种情况以提高性能。
您应该注意,在最后一个表达式 i = i+1
中,i
将被计算两次。
使用 gcc 5.2 编译此程序:
#include<stdio.h>
int main()
{
int i = 0;
++i;
i++;
i += 1;
i = i + 1;
return 0;
}
它给出了这个 ASM:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
add DWORD PTR [rbp-4], 1 #++i
add DWORD PTR [rbp-4], 1 #i++
add DWORD PTR [rbp-4], 1 #i += 1
add DWORD PTR [rbp-4], 1 #i = i + 1
mov eax, 0
pop rbp
ret
这意味着 gcc 5.2 的执行速度完全相同。
从4.4.7到5.2的版本好像都一样
在此特定示例中,所有四个表达式都具有完全相同的外部可观察结果,因此有能力的编译器应该为它们生成完全相同的代码。
编译器不会盲目地读取代码并为每个语句生成一些指令,编译器根据标准推理代码的结果应该是什么,并生成整个程序运行所需的代码按要求。因此,询问有关单个语句的性能问题几乎总是毫无意义的。让我举个例子:
void foo(unsigned int a, unsigned int b) { unsigned int i = a * b; }
void bar(unsigned int a, unsigned int b) { unsigned int i = a + b; }
哪个更快?函数 foo
还是 bar
?许多人会说 "of course multiplication is slower",但最有可能的答案是:两者都同样快,因为一个非常简单的死存储优化会发现没有使用 i
,所以不需要计算它,所以编译器可以将功能优化为零。让我们试试吧:
$ cat > foo.c
void foo(unsigned int a, unsigned int b) { unsigned int i = a * b; }
void bar(unsigned int a, unsigned int b) { unsigned int i = a + b; }
$ cc -S -fomit-frame-pointer -O2 foo.c
$ cat foo.s
[... I edited out irrelevant spam to make this more readable ...]
_foo: ## @foo
retq
_bar: ## @bar
retq
两个函数中唯一的指令是 retq
,它只是来自函数的 returns。
在编程中,一元运算符比其他运算符具有更高的优先级。一元运算符在其他运算符执行之前执行。 Pre 和 Post 增量运算符是一元运算符的示例,而 c 和 d 是二元运算符,因此执行 later.Also c 只是 d 的简写符号,因此两者都需要相同的时间并且来自a 和 b,a 比 b 执行得早,因为 post increment 比 pre increment 快。
希望这个回答对您有所帮助。
示例:
a : ++i;
b : i++;
c : i += 1;
d : i = i + 1;
假设abcd每一个都被完全同时调用,先执行哪一个?
现代编译器足够聪明,可以优化所有四种情况以提高性能。
您应该注意,在最后一个表达式 i = i+1
中,i
将被计算两次。
使用 gcc 5.2 编译此程序:
#include<stdio.h>
int main()
{
int i = 0;
++i;
i++;
i += 1;
i = i + 1;
return 0;
}
它给出了这个 ASM:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
add DWORD PTR [rbp-4], 1 #++i
add DWORD PTR [rbp-4], 1 #i++
add DWORD PTR [rbp-4], 1 #i += 1
add DWORD PTR [rbp-4], 1 #i = i + 1
mov eax, 0
pop rbp
ret
这意味着 gcc 5.2 的执行速度完全相同。
从4.4.7到5.2的版本好像都一样
在此特定示例中,所有四个表达式都具有完全相同的外部可观察结果,因此有能力的编译器应该为它们生成完全相同的代码。
编译器不会盲目地读取代码并为每个语句生成一些指令,编译器根据标准推理代码的结果应该是什么,并生成整个程序运行所需的代码按要求。因此,询问有关单个语句的性能问题几乎总是毫无意义的。让我举个例子:
void foo(unsigned int a, unsigned int b) { unsigned int i = a * b; }
void bar(unsigned int a, unsigned int b) { unsigned int i = a + b; }
哪个更快?函数 foo
还是 bar
?许多人会说 "of course multiplication is slower",但最有可能的答案是:两者都同样快,因为一个非常简单的死存储优化会发现没有使用 i
,所以不需要计算它,所以编译器可以将功能优化为零。让我们试试吧:
$ cat > foo.c
void foo(unsigned int a, unsigned int b) { unsigned int i = a * b; }
void bar(unsigned int a, unsigned int b) { unsigned int i = a + b; }
$ cc -S -fomit-frame-pointer -O2 foo.c
$ cat foo.s
[... I edited out irrelevant spam to make this more readable ...]
_foo: ## @foo
retq
_bar: ## @bar
retq
两个函数中唯一的指令是 retq
,它只是来自函数的 returns。
在编程中,一元运算符比其他运算符具有更高的优先级。一元运算符在其他运算符执行之前执行。 Pre 和 Post 增量运算符是一元运算符的示例,而 c 和 d 是二元运算符,因此执行 later.Also c 只是 d 的简写符号,因此两者都需要相同的时间并且来自a 和 b,a 比 b 执行得早,因为 post increment 比 pre increment 快。 希望这个回答对您有所帮助。